Uh oh!
There was an error while loading. Please reload this page.
Classify plugin commands as CLI surface or internal mechanism - #847
Conversation
philcunliffe
commented
Aug 18, 2026
Neutral review round: |
…th hidden sides for the client hooks Two findings from the review of #847. 1. `validateCommandContributions` made every field of a command entry fatal to `loadManifest`. That turns a cosmetic help-metadata mistake into a whole-plugin outage (its sources, sinks and datasets go with it) and it points the reader somewhere else: `hyp status` reports `plugin_unknown` and never names the field. It also made the `contributes_malformed` rows the doctor already has for `commands` (`src/core/plugin_doctor/diagnose.js`) unreachable, because `diagnosePlugin` returns at `manifest_invalid` before its shape checks run - contradicting the doctor table in `docs/PLUGIN_AUTHORING.md`. Every sibling contribution category stays opaque to `validateManifest` for exactly that reason. Narrowed to the one field LLP 0268 needs to be trustworthy: `hidden`, which is not help metadata but the flag deciding whether a command is CLI surface at all. LLP 0268's Consequences bullet says so now. 2. LLP 0268 says the parity test is what holds the manifest and registration `hidden` flags together, but the walk covered only `claude-account` and `claude-desktop`. For the three commands that motivated the decision (`claude-hook session-context`, `claude-hook classify-cwd`, `codex-hook classify-cwd`) it read the manifest alone, so dropping `hidden: true` from a registration left the suite green while `hyp claude-hook --help` started listing an internal hook - the half-hidden state #field calls worse than either consistent answer. The hook test now activates both client adapters and asserts the registry side too; removing either flag fails it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
philcunliffe
commented
Aug 19, 2026
Review: findings (3), 2 fixed and pushedReviewed Verdict: the mechanism is right. 1. |
philcunliffe
commented
Aug 19, 2026
Triage at head |
0268 is already claimed by fix/issue-838 (PR #847), which is older, so this branch yields the number. 0279 is free across master and every open branch. Mechanical renumber only: no content change (LLP 0156). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every command a plugin declared in `contributes.commands` became public CLI surface the moment its plugin was config-active, because that array is both the registration list and the pre-boot help advertisement. Nothing separated them. `claude-account credential` is the case that shows the cost: its caller is the no-arg wrapper Claude Desktop execs, its entire stdout is a live credential (LLP 0116#helper-contract), and it sat in group help beside `login` and `status` as a third thing a person might try. LLP 0009 said hidden commands stay out of help by being omitted from the manifest. Omission also deletes the only pre-boot record the command exists, which is what the dispatch-miss path reads to say "unavailable, enable @hypaware/x" instead of "unknown" (LLP 0153), and what the manifest/ registration parity tests compare. So this takes the picker's route instead (LLP 0202): hiding is a display filter, never a catalog deletion. - `PluginCommandManifest` gains `hidden?: boolean`, and `contributes.commands` is now validated rather than passed through opaquely, so a manifest spelling it `"true"` is rejected instead of silently advertising an internal mechanism. - `collectPluginHelpCommands` skips hidden entries; the registry-side filters already existed. - `claude-account credential` is hidden on both sides and still dispatches. Nothing else is hidden: LLP 0139#macos-only already settled that `claude-desktop profile`/`install-helper`/`status` serve a fleet admin directly, so they are compatibility surface, not plumbing. - `claude-desktop status` and `claude-account status` gain the long help a visible diagnostic owes its reader, both stating they print no secret. LLP 0268 records the four-way classification (public workflow, public diagnostic, compatibility surface, internal mechanism) for every first-party command and the two-sided `hidden` rule. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
LLP 0268 claimed `claude-account credential` was the only internal mechanism and that "nothing else is" hidden. Three commands already registered `hidden: true`: `claude-hook session-context`, `claude-hook classify-cwd`, and `codex-hook classify-cwd`. None appeared in its plugin's manifest at all, which is exactly the hide-by-omission the decision's #not-deletion section rejects. It costs what that section says it costs. The hook lines live in Claude Code's settings.json and Codex's config and keep firing after the plugin leaves the active config; with `claude-hook` declared nowhere, the miss path had no head token to match, so: hyp: unknown command 'claude-hook session-context' is now: hyp: 'claude-hook' is provided by @hypaware/claude, which is not in the active config - Declare the three in the `claude` / `codex` manifests, marked `hidden: true`. They stay out of help (verified: `hyp --help` renders no hook row) and run exactly as before. - LLP 0268: add them to the classification table, correct "exactly one" and "nothing else is", and say which reader omission actually costs for which command. The miss path matches head tokens, so it is only blinded when the whole token goes undeclared, which is the hook case, not the credential case; there omission costs the parity tests. - The parity test walked manifest to registry only, so it could not see a registration declared nowhere. Walk both directions, and pin the three hooks. - `claude-desktop status` long help promised nonzero only for a missing wrapper; `runStatus` also exits 1 when `resolveInputs` throws, which an ephemeral `:0` gateway listen does on precisely the non-Mac admin box the help invites. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…th hidden sides for the client hooks Two findings from the review of #847. 1. `validateCommandContributions` made every field of a command entry fatal to `loadManifest`. That turns a cosmetic help-metadata mistake into a whole-plugin outage (its sources, sinks and datasets go with it) and it points the reader somewhere else: `hyp status` reports `plugin_unknown` and never names the field. It also made the `contributes_malformed` rows the doctor already has for `commands` (`src/core/plugin_doctor/diagnose.js`) unreachable, because `diagnosePlugin` returns at `manifest_invalid` before its shape checks run - contradicting the doctor table in `docs/PLUGIN_AUTHORING.md`. Every sibling contribution category stays opaque to `validateManifest` for exactly that reason. Narrowed to the one field LLP 0268 needs to be trustworthy: `hidden`, which is not help metadata but the flag deciding whether a command is CLI surface at all. LLP 0268's Consequences bullet says so now. 2. LLP 0268 says the parity test is what holds the manifest and registration `hidden` flags together, but the walk covered only `claude-account` and `claude-desktop`. For the three commands that motivated the decision (`claude-hook session-context`, `claude-hook classify-cwd`, `codex-hook classify-cwd`) it read the manifest alone, so dropping `hidden: true` from a registration left the suite green while `hyp claude-hook --help` started listing an internal hook - the half-hidden state #field calls worse than either consistent answer. The hook test now activates both client adapters and asserts the registry side too; removing either flag fails it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
bd608a4 to
292ee6bCompareUh oh!
There was an error while loading. Please reload this page.
What this is
A plugin's
contributes.commandsarray was doing two jobs at once: it listswhat the plugin registers, and it is the advertisement
hyp --helprendersbefore boot (LLP 0009). Nothing separated them, so every command a plugin
registered became a public CLI promise the moment its plugin was
config-active.
claude-account credentialis the case that shows what that costs. Its calleris the no-arg wrapper Claude Desktop
execs, and its entire contract is thatstdout is a live credential and nothing else (LLP 0116#helper-contract). It
sat in
hyp claude-account --helpbesideloginandstatus, reading as athird thing a person might try, and the thing they get for trying is a token
in their scrollback.
Approach
LLP 0009 said hidden commands stay out of help "by being omitted from the
manifest". Omission also deletes the only pre-boot record that the command
exists, and two things read that record: the dispatch-miss path that turns
"unknown command" into "unavailable, enable
@hypaware/x" (LLP 0153), and themanifest/registration parity tests that catch summary drift. An internal
command is the most likely to be typed by someone who does not know which
plugin owns it, so it is the worst one to make unattributable.
So this takes the route LLP 0202 already set for picker rows: hiding is a
display filter, never a catalog deletion. The declaration stays and is marked.
PluginCommandManifestgainshidden?: boolean.contributes.commandsisnow validated (
validateCommandContributions) instead of passed throughopaquely, so a manifest spelling it
"true"is rejected rather thansilently advertising an internal mechanism.
collectPluginHelpCommandsskips hidden entries. The registry-side filters(
renderHelp,listGroupChildren) already existed and are unchanged, whichis why both sides must be set: the manifest flag governs pre-boot top-level
help, the registration flag governs group help after activation.
claude-account credentialis hidden on both sides, and still dispatches.claude-desktop statusandclaude-account statusgain the long help avisible diagnostic owes its reader, both stating they print no secret and
where sign-in state actually lives.
What is deliberately not hidden
The issue proposed
claude-desktop profile,install-helper, andstatusasinternal on the grounds that
installdrives all three. It does, but that isnot the whole audience: LLP 0139#macos-only (Accepted) already settled that
these three stay runnable off a Mac because rendering the MDM payload or
staging the helper "is legitimately useful on a non-Mac admin box preparing a
fleet push". A command an Accepted LLP keeps working for an audience is not
one to make undiscoverable for them. They are classified as compatibility
surface and stay visible;
statusgets the diagnostic long help instead.LLP
llp/0268-plugin-commands-classified-as-surface-or-mechanism.decision.md(Decision, Systems: CLI, Plugins) records the four-way classification (public
workflow / public diagnostic / compatibility surface / internal mechanism) for
every manifest-declared first-party command, and the two-sided
hiddenrule.LLP 0009 gets an
Extended byforward-ref; nothing it settled is edited.Regression test
test/core/plugin-command-visibility.test.js(7 tests). Againstmaster5 ofthe 7 fail; all 7 pass on this branch:
manifest hidden commands stay out of pre-boot top-level help- before, astaged plugin declaring
{ name: 'demo plumbing', hidden: true }rendereddemo Subcommands: plumbing, run; now it rendersSubcommands: run.manifest command hidden must be a boolean- the validator did not look atcontributes.commandsat all.the credential helper contract is an internal mechanism in manifest and registry alike,group help hides the credential helper but keeps the public claude-account surfaces-credentialwas a visible row inhyp claude-account --help.the hidden credential helper still dispatches- proves hiding is a helpfilter, not a deregistration: signed out, the body reports it (exit 1), not
the dispatcher's unknown/unavailable exit (2).
every claude-desktop and claude-account command declares the same visibility in manifest and registry- holds the twohiddensides together.Local:
npm test4264 pass / 0 fail,npm run typecheckclean.Fixes#838