fix(doctor): detect new Claude Code hook exec-form schema#177
Merged
Conversation
CLI 2.3.1 → 2.3.2 · plugin 2.4.10 → 2.4.11
`checkSettingsHooks` was matching the legacy shell form only — looking
for `"onebrain checkpoint stop"` inside the `command` field. Claude
Code's current hook schema splits this into `{command: "onebrain",
args: ["checkpoint", "stop"]}`, so the substring check missed every
canonical entry and `/doctor` false-flagged both required hooks as
missing on vaults whose `register-hooks` had already migrated them.
Changes:
- New `effectiveCommand()` joins `command + args[]` so a single
substring check handles both schemas
- New `detectHookForm()` returns `exec` / `legacy` / `absent` —
legacy entries now warn "--fix will migrate to exec form" instead
of being invisible
- Stale-hook sweep also uses the joined effective command so a stale
reference hidden inside `args[]` is no longer missed
- `--fix` path unchanged: `runRegisterHooks` already migrates legacy
shell-form to exec form via its existing `rewriteIfShellForm` pass
- 6 new unit tests cover canonical exec, legacy shell, bash-wrapper,
absent, mixed-group, and qmd-skip cases
- doctor SKILL.md updated to describe the effective-command rule
- detectHookForm now scans all matching entries per event and reports exec if any matching entry is canonical, only falling back to legacy when no canonical exists. Handles partial-migration state where a stale legacy entry was left behind alongside a new canonical one - effectiveCommand filters non-string args entries — hand-edited settings.json with stray null/numbers can't produce ghost matches - 4 new tests: partial-migration duplicates, mixed Stop+PostToolUse migration, stale exec-form event detection, defensive args filter
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.
Summary
/doctorwas false-flagging both required hooks as missing on vaults whoseregister-hookshad already migrated entries to the canonical exec-form schema ({command: "onebrain", args: ["checkpoint", "stop"]}). The substring check only inspected thecommandfield, which under the new schema is just"onebrain"— the verb lives inargs[].This PR teaches the validator to compute an effective command string by joining
command+args[]before substring matching, so both legacy and exec form resolve to"onebrain checkpoint stop". It also adds form classification so legacy entries are surfaced (not hidden) and the existing--fixmigration path can take over.Changes
validator.ts: neweffectiveCommand()+detectHookForm()helpers; required-hook check now distinguishesexec/legacy/absentonebrainreferences buried inargs[]--fixpath unchanged:runRegisterHooksalready migrates legacy → exec via itsrewriteIfShellFormpasssrc/lib/index.test.tscovering exec, legacy, bash-wrapper, absent, mixed-group, and qmd-skipdoctor/SKILL.mddescription updated to match new behaviorTest plan
bun run typecheckcleanbun test src/lib/index.test.ts— 27/27 pass (6 new + 21 existing)bun test src/commands/internal/register-hooks.test.ts— 40/40 pass