原始内容
CLY - Command Line Utilities
Modular Go CLI for day-to-day utilities with beautiful TUI interfaces powered by Charm libraries.
Features
- Claude Code Statusline - Context window, model, cost display for Claude Code sessions
- UUID Generator - Interactive UUID generation (v4, v7, multiple)
- AliExpress Scraper - Browser automation for product data extraction
- 48 TUI Demos - Complete Bubbletea component showcase
- Modular Architecture - Zero-coupling design, easy to extend
- Single Binary - No runtime dependencies
Installation
Quick Install (Recommended)
curl -sSL https://raw.githubusercontent.com/yurifrl/cly/main/install.sh | bash
The script installs to ~/.local/bin/cly by default. Set INSTALL_DIR to customize:
INSTALL_DIR=/usr/local/bin ./install.sh
Via go install
go install github.com/yurifrl/cly@latest
Build from source
git clone https://github.com/yurifrl/cly
cd cly
go run main.go --help
Usage
UUID Generator
cly uuid
# Interactive selection: v4 (random), v7 (time-ordered), multiple (5x)
AliExpress Scraper
# Launch persistent browser (solve CAPTCHA once)
cly scraper browser
# In another terminal, scrape products
cly scraper aliexpress --url 1005003618976317
cly scraper aliexpress --url "1005003618976317,1005010081760632"
cly scraper aliexpress -f products.txt
TUI Component Demos
cly demo --help # List all 48 demos
cly demo chat # Chat room (textarea + viewport)
cly demo spinner # Animated spinner
cly demo table # Data table with 100 cities
cly demo list-simple # Selection list
cly demo progress-static # Progress bar
cly demo file-picker # File picker
Commands
Utilities
| Command | Description |
|---|---|
uuid |
Generate UUIDs interactively (v4, v7, multiple) |
scraper |
Web scraping with browser automation |
scraper browser |
Launch persistent browser for scraping |
scraper aliexpress |
Scrape AliExpress product data |
skills install [name...] |
Install cly-bundled AI agent skills (default ~/.agents/skills/). Cherry-pick by name; default is all. |
pi extensions install |
Install cly-bundled pi extensions (default ~/.pi/agent/extensions/). Currently ships pi-cly with a /save slash command. |
Dotfiles integration:
@once cly-skills -- cly skills install
@once cly-pi-ext -- cly pi extensions install
Demos (48 Total)
The demo namespace contains all 48 official Bubbletea examples:
Core Components:
chat,spinner,table,list-simple,list-default,list-fancytextinput,textarea,textinputsprogress-static,progress-animated,progress-download
Forms & Pickers:
credit-card-form,file-picker,autocomplete
Layout & Views:
pager,paginator,viewport,tabs,split-editorsviews,composable-views
Time & Animation:
timer,stopwatch,spinners
Advanced:
mouse,focus-blur,prevent-quit,window-sizeexec,pipe,suspend,tui-daemon-combosend-msg,sequence,result,realtimedebounce,eyes,cellbuffer,glamouraltscreen-toggle,fullscreen,set-window-titlesimple,help,http,package-manager,table-resize
Run cly demo --help to see all available demos.
Architecture
Modular design with clean command registration:
cly/
├── main.go # Entry point
├── cmd/
│ └── root.go # Root command, module registration
├── modules/
│ ├── uuid/ # UUID utility
│ │ ├── cmd.go # Command registration
│ │ └── uuid.go # Implementation
│ └── demo/ # Demo namespace (48 subcommands)
│ ├── cmd.go # Demo parent + registrations
│ ├── chat/
│ ├── spinner/
│ └── ... (46 more)
└── pkg/
└── style/ # Shared Lipgloss styles
└── theme.go
Key principles:
- Each module is self-contained
- Zero coupling between modules
- Single registration point in
cmd/root.go - Adding command = create directory + add one line to root
Adding a Module
See docs/adding-modules.md for detailed template and instructions.
Quick steps:
- Create
modules/<name>/directory - Copy pattern from existing module
- Adapt for your use case
- Register in
cmd/root.goinit()
Or use the Claude Code skill: Just ask Claude to "create a demo module for X"
Dependencies
- Cobra - CLI framework
- Viper - Configuration management
- Bubbletea - TUI framework (Elm Architecture)
- Bubbles - Pre-built TUI components
- Huh - Forms and prompts
- Lipgloss - Styling and layout
Development
# Run in development
go run main.go demo chat
# Build binary
go build
# Run tests (when implemented)
go test ./...
Native macOS Notifications
pkg/notify ships an embedded, signed Swift daemon (cly-notifier.app) that
delivers notifications via UNUserNotificationCenter with action buttons
(Snooze, Retry) and persistence in Notification Center. On non-darwin hosts
or when the bundle is unavailable, it falls back to beeep.
First-time setup: see docs/notifier-cert-setup.md
for the codesign certificate dance (Apple Developer cert + WWDR CA + 1Password
field). Without that, notifications fall back to beeep (no action buttons).
One-time bundle build (darwin only)
The Apple Developer ID identity lives as a field on your cly 1Password
Secure Note (Personal account). The repo ships a .env.op template that
resolves it at build time — same pattern as other 1Password-backed env files
(1password skill).
# Resolve secrets into .env (gitignored)
task envs:op
# Build the notifier bundle (codesigned with your Developer ID)
task build:notifier
# Build cly normally
task build
cly update (alias cly u) is self-sufficient — if .env is missing it
runs op inject to a temp file itself, so you don't have to remember to
task envs:op first.
If op isn't on PATH or CLY_NOTIFIER_SIGN_ID is unset, build.sh uses an
ad-hoc signature (-). That works on your own Mac for local development
but is not distributable.
The template .env.op is committed; the resolved .env is gitignored.
Fresh checkouts ship with a 1-byte placeholder tarball at
pkg/notify/assets/cly-notifier.app.tar.gz so go build always succeeds.
The native backend detects the placeholder and falls back silently.
Permission prompt
First time the daemon runs, macOS shows a notification permission prompt for
dev.yurifrl.cly. Approve it once and macOS remembers per bundle ID
forever. If denied, notifications still send but won't appear; cly logs an
actionable hint to stderr.
cly every snooze + retry
When cly every --notify is on, transitions emit notifications with action
buttons:
failing→[Snooze 5m] [Dismiss]recovered→ no buttons (auto-dismiss)gaveup→[Retry] [Dismiss]
Clicking Snooze 5m sets SnoozeUntil on the task state; the loop skips
runs until that time passes. Clicking Retry resets the failure counter
and triggers the next run immediately.
Standalone-flavor packages
pkg/notify and modules/every are designed for future extraction as their
own Go modules. They import only stdlib + their declared external deps —
nothing else under github.com/yurifrl/cly. Run task lint:isolation to
verify the contract.