v0.33.0: bugfix batch from tool-audit (B1-B14)#28
Merged
Conversation
Surfaced from cross-machine tp-report.txt audit + external code review
on 2026-05-08. 1255 tests pass; full diff in beads token-pilot-n2r.
P0 (release blocker — silent breakage):
- B1+B3: install-agents probes ~/.claude/plugins/cache for fresher tp-*
templates when running CLI is stale (e.g. global v0.20.1 binary
shipping only 6 of 25 agents). New: pickFreshestAgentsSource().
- B2: user settings.json hooks pinned to /home/.../npx/<hash>/...
paths kept calling old binary even after plugin cache shipped a
new minor (e.g. PreToolUse:Task in v0.31.0 silently no-op'd).
- new helper isStaleTokenPilotHookCommand() detects the pattern
- installHook() now filters those entries before re-writing
- new helper isTokenPilotPluginEnabled() short-circuits manual
install when the bundled plugin is already enabled — the
plugin's hooks/hooks.json with ${CLAUDE_PLUGIN_ROOT} is the
source of truth in that case
- new CLI subcommand: `token-pilot migrate-hooks` cleans both
user-level + project-level settings.json idempotently
P1 (functional):
- B6: smart_log gitArgs malformed — `git log -- HEAD -- foo.ts`
treated HEAD as pathspec; removed the leading `'--'` separator.
- B7+B8: project root detection on WSL caught C:\\Windows /
/mnt/c/Windows / UNC paths. New: prefer CLAUDE_PROJECT_DIR over
INIT_CWD/PWD/cwd; reject Windows system paths via new
isWindowsSystemPath().
- B10: ast-index `exec()` failed permanently when init silently
errored at server startup. Added lazy retry with a friendlier
error pointing at `npx token-pilot install-ast-index`.
- B14: subagents (general-purpose, code-analyzer, feature-dev:*)
loop on raw Read after hook-pre-read deny because their prompts
don't know about MCP tools. PreToolUse:Task now ALWAYS injects a
generic SUBAGENT_TOOL_GUIDE additionalContext for non-tp-*
dispatches (empty / no-match / escape paths included).
- B4: TOKEN_PILOT_FORCE_SUBAGENTS=1 was a silent no-op when the
agent catalog was empty. Now hard-denies with a clear
"run install-agents" diagnostic.
P2:
- B5: hook-events.jsonl was fragmented across monorepo subdirs
(e.g. apps/admin, apps/api, packages/prisma). New
loadEventsTree() walks the repo root and merges; `stats` uses
it by default, opt out with `--no-merge`.
- B9: read_range rejected start_line/end_line when the MCP
transport sent them as strings ("10"). New coerceIntFromAny()
accepts numeric strings, rejects everything else.
P3 deferred:
- B11 belonged to a different package (epitaxy) — not ours.
- B12, B13 (docs path drift, retroactive ADR for 6 new tools)
filed for a follow-up — non-blocking for v0.33.0.
Manifests bumped 0.32.0 → 0.33.0; agents rebuilt (25/25).
Closes the visibility gap exposed by tp-report.txt: every silent
hook throw, edge-case branch, and stale-config slipped past us
because we had no place to write them. 14 new tests; 1269/1269 pass.
Pack 1 — error channel
- new src/core/error-log.ts:
appendError, loadErrors, formatErrorList,
classifyError (ENOENT/parse_error/timeout/EACCES/...),
safeBasename / safePathInfo (privacy helpers),
rotation (5 MB) + 30d archive retention,
TOKEN_PILOT_NO_ERROR_LOG=1 opt-out.
- new src/hooks/safe-runner.ts:
runHookEntryPoint(opts, fn) wraps every hook entry point;
on throw → structured record to ~/.token-pilot/hook-errors.jsonl;
always exits 0 so Claude Code never sees a hook failure.
- wraps all 8 entry points in src/index.ts:
hook-read, hook-edit, hook-pre-bash, hook-pre-grep,
hook-pre-task, hook-post-bash, hook-post-task,
hook-session-start.
- new CLI: `token-pilot errors [--tail=N --code=X --hook=Y --level=L]`.
Pack 2 — diagnostic events
- HookEvent extended with optional level/code/detail/duration_ms +
union now includes "diagnostic".
- new helper appendDiagnostic(projectRoot, args).
- emit points wired:
* hook-pre-task → `force_subagents_no_agents`
when TOKEN_PILOT_FORCE_SUBAGENTS=1 and the agent index is empty
* startServer detect path → `wsl_path_rejected`
one event per WSL/UNC candidate that isWindowsSystemPath() filters
- doctor extended with a new "runtime health" section:
* top error codes from last 100 records
* stale npx-cache / pinned-version hook entries in
user-level + project-level settings.json (B2 leftover scan)
* tp-* agent inventory: installed vs catalog
* cwd safety: warns when CLAUDE_PROJECT_DIR or process.cwd()
lands on a Windows system path
Pack 3 — timing groundwork
- HookEvent gains optional duration_ms.
- runHookEntryPoint measures wall-clock and exposes onTiming hook
for callers that want to log a `hook_slow` diagnostic.
(Per-event emit deferred — needs a threshold tuned from real data.)
Privacy: error records carry only sanitized input (basename, ext,
short metadata). No file content, no prompts, no full paths.
Fully local — nothing is sent anywhere.
Tests:
- tests/core/error-log.test.ts (14 tests) — classifier, sanitizers,
loadErrors filters/tail, formatErrorList top-codes.
- 1255 prior tests untouched and pass.
External review surfaced four functional MCP bugs (UNC project root, git cwd, read_range schema, find_usages init) plus a fifth version- mismatch claim. All four functional ones are fixed in v0.33.0 (B6, B7, B8, B9, B10); the fifth belongs to a different package and is filed as not-applicable. Document tracks origin → root cause → commit → verification steps so future readers can cross-reference the report against the fix without rerunning the audit.
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
Surfaced from cross-machine
tp-report.txtaudit + external code review on 2026-05-08. 1255/1255 tests pass. Tracked under beads epictoken-pilot-n2r.Bugs fixed
P0 — release blocker (silent breakage)
install-agentsprobes~/.claude/plugins/cachefor fresher tp-* templates when the running CLI is stale (e.g. globalv0.20.1shipping only 6 of 25 agents). New helperpickFreshestAgentsSource().settings.jsonhooks pinned to/home/.../npx/<hash>/.../dist/index.jswere calling old binaries even after the plugin cache shipped a new minor — that's whyv0.31.0PreToolUse:Tasksilently no-op'd in real sessions. Three pieces:isStaleTokenPilotHookCommand()detects the npx-cache + pinned-version patterninstallHook()filters those entries before re-writingisTokenPilotPluginEnabled()short-circuits manual install when the bundled plugin is enabled —hooks/hooks.jsonwith${CLAUDE_PLUGIN_ROOT}is the source of truthtoken-pilot migrate-hookscleans user + project settings idempotentlyP1 — functional
smart_logmalformed gitArgs (git log -- HEAD -- foo.tstreatedHEADas pathspec)C:\\Windows//mnt/c/Windows/ UNC paths. NewisWindowsSystemPath()+ preferCLAUDE_PROJECT_DIR.exec()failed permanently when init silently errored at startup → lazy retry with friendlier error.general-purpose,code-analyzer,feature-dev:*) loop on rawReadafterhook-pre-readdeny because their prompts don't know about MCP tools.PreToolUse:Tasknow always injects aSUBAGENT_TOOL_GUIDEadditionalContextfor non-tp-* dispatches.TOKEN_PILOT_FORCE_SUBAGENTS=1was a silent no-op when the agent catalog was empty. Now hard-denies with "run install-agents" diagnostic.P2
hook-events.jsonlfragmented across monorepo subdirs (apps/admin, apps/api, packages/prisma). NewloadEventsTree()walks repo root and merges;statsuses it by default, opt out via--no-merge.read_rangerejectedstart_line/end_linewhen transport sent them as strings. NewcoerceIntFromAny()accepts numeric strings, rejects everything else.Deferred
Test plan
npx tsc --noEmitclean (pre-existing unreachable-after-exit warnings unchanged)npx vitest run→ 1255/1255 passnpm run build→ 25 agents composed, dist refreshedmigrate-hooksadditionalContextinjection in subagent promptMigration note for users on stale installs
🤖 Generated with Claude Code