fix: standard help, honest flags, and an rm that asks first #10

Merged
dan-claude-bot merged 2 commits from fix/cli-help-and-contract into main 2026-07-13 20:43:14 +00:00
dan-claude-bot commented 2026-07-13 20:31:23 +00:00 (Migrated from github.com)

Closes #8.

Stacked on #9 (feat/list-command) — both rewrite the dispatch block, and
the new help has to document list. Merge #9 first; this PR's diff narrows
to its own commit once it does. Review it after, or read just the second commit.

The help was the script scraping its own header comment by line number
(sed -n '2,9p' "$0"). No sections, no per-command help, no -h, no
--version. And parts of the interface it described weren't real — which is the
half that actually cost my co-founder time.

The interface now tells the truth

you type before now
claudebox lst prints help, exit 0 unknown command: lst — did you mean 'list'?, exit 2
claudebox new --help usage: error, exit 1 the new help page
claudebox snapshot work --labl x snapshot silently named --labl unknown option: --labl
claudebox exec work ls -la -la swallowed "a command's own flags go after --"
claudebox rm work destroys it, no prompt asks first; --force skips
claudebox --version nothing claudebox 0.2.0 (/path)

--force was the sharp one: parsed on line 23, never read anywhere, while rm
ran incus delete -f unconditionally. Shellcheck had been calling it (SC2034: force appears unused) and it was read as a lint nit rather than the missing
guard on the one irreversible command.

What's here

  • usage() / help_cmd(): USAGE, COMMANDS, OPTIONS (every flag, with the
    command it belongs to), EXAMPLES, EXIT STATUS, and a short THE MODEL on
    creds-free + no-inbound-path. Plus a page per command — claudebox help rm,
    claudebox new --help.
  • -h/--help anywhere; --version/-V reading a new VERSION file (0.2.0 —
    bump it on release, or swap it for a tag if you'd rather).
  • Usage errors exit 2, runtime failures 1, and unknown commands get a
    did-you-mean by edit distance.
  • Per-command usage strings are accurate (exec no longer reports
    usage: claudebox exec <box>, omitting the -- <cmd...> that makes it work).
  • shellcheck bin/claudebox is now clean — SC2034 and SC2015 both gone.

⚠️ One behavior change

rm confirms before deleting ([y/N], default no). --force/-f skips it.
With no TTY and no --force, it refuses and exits 2 rather than destroying a
box — so any script calling claudebox rm must now pass --force. That's the
point of the change, but it is a break: shout if you'd rather it only guard
running boxes.

Testing

Same caveat as #9: no real Incus — I work inside a claudebox, which has
neither incus nor nested virt. Every path above was driven against a stubbed
incus and is reproduced verbatim in the table; the rm prompt was exercised on
a real pty (script) for y, n, and bare Enter (aborts, exit 1), plus the
no-TTY refusal. shellcheck and bash -n clean. The help paths touch no Incus
at all, so those are exercised for real.

🤖 Generated with Claude Code

Closes #8. > **Stacked on #9** (`feat/list-command`) — both rewrite the dispatch block, and > the new help has to document `list`. **Merge #9 first**; this PR's diff narrows > to its own commit once it does. Review it after, or read just the second commit. The help was the script scraping its own header comment by line number (`sed -n '2,9p' "$0"`). No sections, no per-command help, no `-h`, no `--version`. And parts of the interface it described weren't real — which is the half that actually cost my co-founder time. ## The interface now tells the truth | you type | before | now | |---|---|---| | `claudebox lst` | prints help, **exit 0** | `unknown command: lst — did you mean 'list'?`, exit 2 | | `claudebox new --help` | `usage:` error, exit 1 | the `new` help page | | `claudebox snapshot work --labl x` | snapshot silently named `--labl` | `unknown option: --labl` | | `claudebox exec work ls -la` | `-la` swallowed | "a command's own flags go after `--`" | | `claudebox rm work` | **destroys it, no prompt** | asks first; `--force` skips | | `claudebox --version` | nothing | `claudebox 0.2.0 (/path)` | `--force` was the sharp one: parsed on line 23, never read anywhere, while `rm` ran `incus delete -f` unconditionally. Shellcheck had been calling it (`SC2034: force appears unused`) and it was read as a lint nit rather than the missing guard on the one irreversible command. ## What's here - `usage()` / `help_cmd()`: `USAGE`, `COMMANDS`, `OPTIONS` (every flag, with the command it belongs to), `EXAMPLES`, `EXIT STATUS`, and a short `THE MODEL` on creds-free + no-inbound-path. Plus a page per command — `claudebox help rm`, `claudebox new --help`. - `-h`/`--help` anywhere; `--version`/`-V` reading a new `VERSION` file (0.2.0 — bump it on release, or swap it for a tag if you'd rather). - Usage errors exit **2**, runtime failures **1**, and unknown commands get a did-you-mean by edit distance. - Per-command usage strings are accurate (`exec` no longer reports `usage: claudebox exec <box>`, omitting the `-- <cmd...>` that makes it work). - `shellcheck bin/claudebox` is now **clean** — SC2034 and SC2015 both gone. ## ⚠️ One behavior change `rm` confirms before deleting (`[y/N]`, default no). `--force`/`-f` skips it. **With no TTY and no `--force`, it refuses and exits 2** rather than destroying a box — so any script calling `claudebox rm` must now pass `--force`. That's the point of the change, but it is a break: shout if you'd rather it only guard *running* boxes. ## Testing Same caveat as #9: **no real Incus** — I work inside a claudebox, which has neither `incus` nor nested virt. Every path above was driven against a stubbed `incus` and is reproduced verbatim in the table; the `rm` prompt was exercised on a real pty (`script`) for `y`, `n`, and bare Enter (aborts, exit 1), plus the no-TTY refusal. `shellcheck` and `bash -n` clean. The help paths touch no Incus at all, so those are exercised for real. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
dan-claude-bot commented 2026-07-13 20:41:34 +00:00 (Migrated from github.com)

Rebased onto the updated #9 (list / info split). The help, the per-command pages, COMMANDS, and the synopses all follow the split — claudebox help info is its own page, and inf now suggests info.

Still stacked: merge #9 first. And note #11 proposes replacing this PR's hand-written help with a table that generates dispatch and help from one source — which is the durable fix for the drift this PR is patching by hand.

Rebased onto the updated #9 (`list` / `info` split). The help, the per-command pages, `COMMANDS`, and the synopses all follow the split — `claudebox help info` is its own page, and `inf` now suggests `info`. Still stacked: **merge #9 first.** And note #11 proposes replacing this PR's hand-written help with a table that generates dispatch *and* help from one source — which is the durable fix for the drift this PR is patching by hand.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: heavy-duty/box#10
No description provided.