Repository navigation
feat(cli): non-interactive subcommands for agent-driven configuration (#602) - #11
Conversation
…sirmalloc#602) Add `src/utils/cli.ts` and wire it into mode detection in src/ccstatusline.ts: args present + TTY stdin dispatches the CLI before the interactive TUI; piped stdin keeps the statusline render path. Subcommands operating on the same settings file (load/save/migration via src/utils/config.ts): - get [--json] - print the effective post-migration config - widget add <line> <widget> [--index N] [--option value ...] - widget remove <line> <index-or-type> - widget move <line> <index> --to <index> - set <option-path> <value> - global options, JSON or plain string values - validate [--file <path>] - exit 0/1 with a machine-readable report - help Guarantees preserved: mutations refuse to run on an unreadable/invalid config (never overwrite it with in-memory defaults), every write is validate-before-write through SettingsSchema and persists via saveSettings() (atomic temp+rename, symlink write-through). All errors are single-line and machine-parseable with a --json variant. No new dependencies; runtime stays Bun+Node compatible. Vitest coverage (34 cases) for every subcommand including error paths, idempotency, invalid-config refusal, and symlink write-through. Co-Authored-By: Claude Code <[email protected]>
axisrow
left a comment
There was a problem hiding this comment.
Reviewed the full PR diff (new src/utils/cli.ts, wiring in src/ccstatusline.ts, 443 lines of tests) against main. Verdict: approving, no blocking findings.
The design is right: every mutation routes through loadSettings/saveSettings (atomic temp+rename, symlink write-through, --config honored), refuses to touch an invalid on-disk config, validates the result with the settings schema before writing, and the piped render path is untouched since isCliMode requires a TTY stdin. Error output is single-line and machine-parseable, exit codes are consistent, and tests cover the error paths, idempotency, invalid-config refusal, and symlink behavior.
Three minor inline findings below; only the first (unknown-option guard bypass in set) is worth fixing before this goes upstream. Deferred README updates are fine as a follow-up.
…README section - set <path>: guard option paths with Object.prototype.hasOwnProperty instead of `in`, so inherited keys like toString/constructor cannot pass the check, get stripped by the schema, and report a false success (Node 14-safe; no Object.hasOwn). Regression test: set toString / powerline.constructor fail and leave the file untouched. - extractJsonFlag(): walk argv and treat only a standalone --json as the output flag; tokens filling a value or boolean option are kept intact, so 'widget add 0 custom-text --customText --json' no longer loses its value. Unit tests for the flag/value boundary plus an add-persistence case. - README: new 'Non-Interactive CLI' section documenting the subcommands and noting that get/validate write defaults on a missing settings.json (first run) while never overwriting an existing unreadable/invalid file. Co-Authored-By: Claude Code <[email protected]>
Adds a line to the CLI usage block so agent loops see that get and validate write the default config when settings.json is missing (the behavior itself is unchanged, matching the TUI first-run contract). Co-Authored-By: Claude Code <[email protected]>
Summary
Implements the non-interactive CLI proposed in sirmalloc#602, so coding agents (Claude Code) can self-configure the status line without driving the interactive TUI.
New
src/utils/cli.ts, wired intosrc/ccstatusline.tsmode detection: command-line args + TTY stdin → CLI mode before the interactive TUI; the piped render path is unchanged.Commands
Guarantees preserved
config.tsload/save: atomic temp+rename writes, symlink write-through,--config <path>honored.--jsonvariant — designed for agent read-modify-write loops.WidgetItemfields; indices are 0-based.src/already uses.End-to-end demo (real entrypoint, temp config via
--config)get→widget add custom-text --index 0 --customText ... --color red→validate(exit 0) →get --jsonreturns one-line JSON containing the new widget; unknown widget type → exit 1, single-line error; piped render unaffected.Tests
bun test: 2408 pass / 0 fail (151 files, single final run)bun run lint: clean, no suppressionsScope cuts (deliberate, for review)
install/uninstallsubcommands not included (TUI flow stays authoritative for now).Refs: sirmalloc#602.
🤖 Generated with Claude Code