Skip to content

--help prints the usage and never runs the command - #15

Merged
JOsacky merged 2 commits into
mainfrom
claude/help-never-runs
Sep 29, 2026
Merged

JOsacky merged 2 commits into
mainfrom
claude/help-never-runs

Conversation

@JOsacky

@JOsacky JOsacky commented Sep 29, 2026

Copy link
Copy Markdown
Member

A safety fix: most subcommands ignored --help / -h and did their work anyway.

skillhook jobs prune --help         # pruned jobs
skillhook service install --help    # installed (and started) the launchd/systemd service
skillhook config set port 1 --help  # wrote skillhook.json
skillhook link --help               # linked the current directory
skillhook serve --help              # started the server

0.6.0 (#14) fixed this for skillhook cloud <subcommand> --help only, inside cloud.ts.

The fix: one check, before any command runs

  • COMMANDS in src/commands/main.ts now pairs every command with its usage ({ run, usage }), and main() checks --help / -h right after resolving the command, before any command code runs. It prints the usage and exits 0. A new command cannot be registered without a usage, and cannot forget the check.
  • skillhook help <command> [subcommand] does the same thing (skillhook help alone still prints the overview).
  • --json prints { "ok": true, "command": "jobs", "usage": "…" }.
  • The per-command checks in cloud.ts and update.ts are gone, because the central one covers them.
  • serve, doctor, health, runners, mcp and url had no usage text and now have one. init's usage now lists --runner shell, which the command already accepted.
  • skillhook job <subcommand> --help follows jobCommand's dispatch. The agent subcommands (progress|ask|outcome|note|context, or no subcommand) print the job API usage. Everything else (job list, job prune, …) is really jobs …, so it prints the jobs usage.
  • Each command's private USAGE became an exported <NAME>_USAGE, as INIT_USAGE and UPDATE_USAGE already were. Most of the diff in src/commands/*.ts is this mechanical rename.

The test (src/cli.test.ts)

It runs every entry of COMMANDS and every subcommand in three forms: … --help, … -h --json and help …. That is about 400 invocations, against a tempHome() set up so that each line has something to act on: a job to prune or answer, a delivery to replay, secrets and config to change, a linked project, a scheduled skill and a stored API key. Every invocation must:

  • exit 0 and print exactly the command's usage (or the JSON above), with nothing on stderr;
  • leave the home unchanged (a snapshot of every entry with its mtime and content, compared after each invocation);
  • never read stdin.

The table must also cover every subcommand the usage texts document (skillhook jobs prune …, skillhook projects add|remove …), and every COMMANDS key (aliases reuse their command's lines). As a control, the same jobs prune line without --help does prune, and the snapshot shows it.

It is hermetic even if the fix regresses. The env sets SKILLHOOK_NO_UPDATE_CHECK=1, unreachable registry and cloud URLs, SKILLHOOK_NO_CLOUD=1 and fake claude/codex. installService, uninstallService, restartService, enableExposure and disableExposure are stubbed with vi.mock for this file, so a broken check can never touch the real launchd/systemd service or Tailscale Funnel. The test asserts none of them was called.

I checked that the test catches the bug. With the check skipped for jobs, it fails on expected 'Removed 1 old job(s)' to contain 'skillhook jobs prune [--keep N]'. With a stray file write added to the help path, the snapshot names the line and shows the new file. With a subcommand missing from the table, it fails on skillhook jobs prune --help: expected [...] to include 'prune'. The whole test takes about 0.3 s.

npm run check passes: typecheck, 280 tests, build, schema and release metadata. CHANGELOG has an ## Unreleased entry. README (global options) and AGENTS.md (registering a command) mention the new rule.

🤖 Generated with Claude Code

JOsacky and others added 2 commits September 29, 2026 12:29
Most subcommands ignored --help / -h and ran anyway: `skillhook jobs prune --help`
pruned jobs, `service install --help` installed the service, `config set ... --help`
wrote the config. 0.6.0 fixed this only for `skillhook cloud`.

main.ts now handles --help / -h (and `skillhook help <command>`) before any command
code runs, printing the usage that COMMANDS pairs with every command, so a new
command cannot forget it; --json prints { ok, command, usage }. The per-command
checks in cloud.ts and update.ts are gone. serve, doctor, health, runners, mcp and
url gained a usage text, and `job <subcommand> --help` follows jobCommand's dispatch
(the agent's job API, or `jobs` for the rest). Each command's USAGE is now an
exported <NAME>_USAGE, like INIT_USAGE and UPDATE_USAGE already were.

src/cli.test.ts runs every COMMANDS entry and every subcommand with --help,
-h --json and `help <command>` against a tempHome() and asserts exit 0, the usage
on stdout, nothing on stderr, no stdin read and an unchanged home (every file with
its mtime and content). The service and Tailscale mutators are stubbed for the
file, so a regression can never reach the real launchd/systemd or Funnel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JOsacky
JOsacky enabled auto-merge (squash) September 29, 2026 16:31
@JOsacky
JOsacky merged commit 89aa7f7 into main Sep 29, 2026
6 checks passed
@JOsacky
JOsacky deleted the claude/help-never-runs branch September 29, 2026 16:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant