feat: add the extra_commands registration seam for variant CLI verbs - #12
Merged
Conversation
SunsetDrifter
force-pushed
the
feat/extra-commands-seam
branch
from
August 24, 2026 12:35
6a4c1a4 to
7bc3b70
Compare
3 tasks
A wiki that needs its own `lint.py <verb>` had no way to register it, so it
had to intercept sys.argv in its lint.py before calling wikilint.main().
That produces two partial usage strings -- the engine's, missing the wiki's
verb, and the wiki's, missing the engine verbs -- and neither is
authoritative.
The engine now owns the whole verb list:
* settings.BUILTIN_COMMANDS: the engine's own (verb, help) table, declared
once. It lives in settings.py because that module is the validation
boundary and needs the reserved names.
* settings.DEFAULTS["extra_commands"]: {verb: (callable(root) -> exit code,
help)}, default {}, validated by _validate_commands() alongside the other
extension points -- dict shape, non-empty verb, no shadowing a built-in,
callable handler, non-empty help.
* cli.USAGE (a fixed literal) became cli.usage(), rendering
BUILTIN_COMMANDS plus the registered extra_commands. One string, every
verb.
* cli.main() dispatches a registered verb, and the new help/-h/--help, ahead
of the CLAUDE.md wiki-root guard, so a pure-reporting verb works from any
cwd. A registered command that does need the root checks for it itself.
Engine verbs keep the guard; an unknown verb still prints usage, exits 2.
Behavior-neutral: the shipped template registers nothing, so it gets
`extra_commands: {}`, and usage() renders the four pre-existing verbs
byte-identically to the old literal -- only the new `help` row is added.
SunsetDrifter
force-pushed
the
feat/extra-commands-seam
branch
from
August 24, 2026 12:47
7bc3b70 to
f957c7f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A wiki can register its own
lint.py <verb>subcommands with the engine, dispatched bycli.main()and listed in a single unified usage string. Addshelp/-h/--help.Why
A wiki that needs its own verb (a schema dump, a custom report) had no way to register it, so it had to intercept
sys.argvin its ownlint.pybefore callingwikilint.main(). That produces two partial usage strings — the engine's, missing the wiki's verb; the wiki's, missing the engine verbs — and neither is authoritative. Any workflow or doc that treats one verb as the single source of truth is then documented by a usage string that doesn't list it.How
settings.BUILTIN_COMMANDS— the engine's own(verb, help)table, declared once. It lives insettings.pyrather thancli.pybecause that module is the validation boundary and needs the reserved names to reject a shadowing registration.settings.DEFAULTS["extra_commands"]—{verb: (callable(root) -> exit code, help text)}, default{}, validated by_validate_commands()alongside the other extension points: dict shape, non-empty string verb, no shadowing a built-in, callable handler, non-empty help. Consistent with howextra_checks/extra_secret_patterns/skills_diralready fail fast at startup instead of mid-run.cli.USAGE(a fixed literal) →cli.usage()— rendersBUILTIN_COMMANDSplus the registeredextra_commands, column-aligned. One string, every verb, one declaration of the verb list, so usage can't drift from whatmain()actually dispatches.cli.main()dispatches a registered verb and the newhelpverb ahead of theCLAUDE.mdwiki-root guard, so a pure-reporting verb works from any cwd. A registered command that does need the wiki root checks for it itself. Engine verbs keep the guard unchanged; an unknown verb still prints usage and exits 2.Why it is behavior-neutral
wiki/template registers nothing, so it getsextra_commands: {}and its dispatch is unchanged. Pinned by a test that asserts the template does not set the key.usage()renders the four pre-existing verbs byte-identically to the old literal (same column width, same help text); the only difference in output is the addedhelprow.helpbypass it.check,rebuild-index,reverse-deps,coverage, and unknown verbs all still hit the wiki-root guard first and still exit 2 outside a wiki — covered by a test.Test plan
python -m unittest discover -s tests— 107 passed (was 91; +16)tests/test_extra_commands.pycovers:root, and its exit code is returnedCLAUDE.mdpresent (the root-guard bypass)help,-h,--helpeach exit 0 without a wiki rootcheck/rebuild-index/ an unknown verb (exit 2)usage()lists engine + registered verbs, and covers every entry ofBUILTIN_COMMANDS(verb and help text), so the table can't driftNonehelp, an empty/blank verb, a non-dict tableconfigure()(exit 2) before any dispatch happensNote on the sibling PR
Opened alongside #11. Both are cut from
mainand are independently mergeable, but they touch adjacent regions ofsettings.pyand the same README bullet, so whichever merges second needs a trivial rebase (no logical conflict).🤖 Generated with Claude Code