Repository navigation
feat(claude-mod): agent-run plannotator commands open through the mod, so Ask AI reaches the session - #1688
Merged
Conversation
…, so Ask AI reaches the session Claude sometimes runs `plannotator annotate x.html --gate --json` in Bash instead of calling the plannotator tool; that blocked the session and started a server with no bridge token, so Ask AI offered separate SDK agents. One host-neutral parser, plannotatorCommandToToolInput (shared tool contract, copied into the mod), decides what is taken over; the mod's tool.call hook answers a matching main-loop Bash call through the same launch as the tool. Strict gates, other flags, several targets, compound commands, a dev build run by path, and every subagent's command run unchanged.
backnotprop
force-pushed
the
feat/take-over-agent-cli-runs
branch
from
October 4, 2026 21:25
2478fc4 to
16c70c7
Compare
1 task
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.
The bug
Owner report: annotating HTML showed the provider options in Ask AI instead of only "Ask this session". This is not specific to HTML. Evidence from one Claude Code session with the mod on:
plannotator annotate .../INDEX.html --gate --json(Claude ran it in Bash)plannotator annotate .../REVIEW-3.md --gate --json(Claude ran it in Bash)plannotator annotate-last --stdin(started by the mod)Cause: Claude ran the CLI itself through Bash instead of using the
plannotatortool. That run blocks the session and starts a server without a bridge token. The tool is deferred behind tool search, and the CLI is documented, so models still reach for Bash. #1687 improved the skill wording; this PR makes the Bash path behave the same as the tool.Design
plannotatorCommandToToolInput(command)lives in the CONTRACT section ofpackages/shared/plannotator-tool.ts, and the mod keeps its byte-for-byte copy inhooks/mod/tool.ts(enforced bytool.test.ts). It turns a shell command into theplannotatortool's input, or returnsnullfor a command that should run unchanged.simpleShellCommandWordsfollows the same quoting rules assplitShellWordsand expands nothing. It returns null for anything a shell would interpret.hooks/mod/take-over.ts): the existingtool.callhook, on the main loop'sBashonly, sends a matching call throughPlannotatorMod.runTool, the same launch theplannotatortool uses. That launch is detached, carries the result file and the bridge token, and delivers the decision later as a plugin turn.{ stdout: <the tool's opened text>, stderr: "", interrupted: false }; a startup error comes back as adeny.register.tsis still the only file with$calls, andcontroller.tsis unchanged.revieworannotate notes.mdwould therefore open the wrong diff or file, so a subagent's command always runs for real.Taken over vs. passed through
Taken over only when all of these hold:
plannotator;annotate <one target> [--gate] [--markdown]review [one target] [--base <ref>]annotate-lastorlast, with no arguments--jsonis accepted and dropped;parsePlannotatorToolInput.Passed through unchanged (the real CLI runs):
./plannotator,/tmp/dev/plannotator): that is a dev build;--require-approval,--result-file,--hook,--tailscale,--static,--app,--no-jina,--render-html,--diff-type,--local,--patch-file,--stdin,--help, ...npx,env;; & | < > ( ), line breaks,$, backticks, unquoted globs or braces,#comments,~user, an unterminated quote. This coverscd x && plannotator ....So scripted strict gates keep the real CLI and its exit codes.
Not in this PR
Pi and OpenCode 2 are held. Their wiring is preserved on branch
feat/take-over-agent-cli-runs-pi-opencode(no PR):execute, which turns any shellTool.Errorinto an uncaught defect.plannotatortool, and the only way to answer a call is a block whose result is flagged as an error, which invites a retry through the blocking CLI.On both hosts an agent-run
plannotatorcommand still runs the CLI.Tests
packages/shared/plannotator-tool-command.test.ts: the parser's take-over and pass-through rules, including a dev build run by path, shell syntax, and quoting.apps/hook/hooks/mod/take-over.test.ts(in-memory Host):plannotator annotate INDEX.html --gate --jsonlaunches exactly what the tool launches, with the bridge token and the result file, and answers with the tool's text;--require-approval,--result-file, a piped command andcd x && ...launch nothing;apps/hook/tests/register.test.ts(engine harness): the take-over through the real engine; pass-through reaching the core Bash, covering a subagent and./plannotator; and the knob off.Runs:
bun test apps/hook packages/sharedwith a tempPLANNOTATOR_DATA_DIR: 1784 pass, 0 fail.scripts/test-claude-code-mod.sh: 13 pass.bun run typecheck(which includes the mod's tsconfig) andclaude plugin validate apps/hook: clean.