You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: modernize CLI UX with interactive prompts, spinners, and live displays
Add questionary for arrow-key artist selection in select/edit commands,
Rich spinners for all long operations, Rich Live table for sync progress,
diff panel with colored add/remove tables, delete/sync confirmations,
and retry prompts for recoverable failures.
Also adds codecov CI integration, pytest coverage thresholds, expanded
CLAUDE.md with critical rules, Makefile dev/format/check-all targets,
pre-push validation script, releasing docs, testing checklist, and
normalizes docs/ to lowercase filenames.
1.**All CLI output goes through `output.py`** — never call `console.print()` directly. A pre-commit pygrep hook enforces this. Use the semantic helpers: `error()`, `warn()`, `success()`, `info()`, `status()`, `dim()`, `confirm()`, `spinner()`.
53
+
54
+
2.**Never move or rename source files** — Plex reads from the same music library. Metadata changes are written in-place.
55
+
56
+
3.**The `scan` command is read-only** — it only reads metadata and writes to SQLite. No file modifications.
57
+
58
+
4.**FLAC files are excluded from iPod sync** — stock iPod firmware doesn't support FLAC. Don't add transcoding.
59
+
60
+
5.**`clickwheel/ipod/` is vendored code** — excluded from ruff linting. Don't refactor it unless fixing a bug in the iPod database writer.
61
+
62
+
6.**Interactive prompts use questionary** — not raw `typer.prompt()` or `input()`. Arrow-key selection with `questionary.select()` and `questionary.checkbox()`. Non-interactive flags (`--add`/`--remove`) are kept for scripting.
63
+
64
+
7.**Long operations use `spinner()` context manager** — beets phases, iPod reads, Last.fm calls, eject. Never leave the user staring at a frozen terminal.
65
+
66
+
8.**Destructive operations require confirmation** — delete, sync. Use `typer.confirm()` with sensible defaults.
67
+
68
+
9.**`select`, `edit`, `diff`, `sync` auto-scan** the library if stale. `--no-scan` skips this. Controlled by `auto_scan` and `auto_scan_staleness_minutes` in config.
69
+
70
+
10.**`fix` requires beets extras** — `pip install 'clickwheel[fix]'`. Auto-generates beets config on first run.
71
+
72
+
## Code Style
73
+
74
+
-**Naming**: snake_case for functions/variables, UPPER_CASE for constants
75
+
-**Error handling**: use `error()` + `raise typer.Exit(1)` for fatal errors. Offer retry for recoverable failures (sync DB write, scrobble submission).
76
+
-**Imports**: lazy imports for heavy modules (questionary, subprocess, pylast) — import inside the function that uses them
Copy file name to clipboardExpand all lines: README.md
+14-14Lines changed: 14 additions & 14 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,7 +7,7 @@
7
7
8
8
A CLI for syncing a music library to a classic iPod from a modern Mac — no iTunes required.
9
9
10
-
Handles the full workflow: scan and clean up your library's metadata, interactively pick what goes on the iPod, and sync with a progress bar.
10
+
Handles the full workflow: scan and clean up your library's metadata, interactively pick what goes on the iPod, and sync — all with a modern terminal UI.
11
11
12
12
## Install
13
13
@@ -38,19 +38,19 @@ Commands like `select`, `edit`, `diff`, and `sync` automatically check for libra
0 commit comments