feat(core): agent states from Claude Code hooks, a Failed mark (#131) - #132
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FHHaWKR4M5QtW7Wecyuk4t
A bundled Claude Code plugin prints a fixed OSC 777 through terminalSequence for each hook event; the core reads it as the agent's own word: working, question, done, failed with its kind. Failed is a new attention line, the Exact status setting hands new shells the plugin. Codex's Action Required title is a question. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FHHaWKR4M5QtW7Wecyuk4t
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FHHaWKR4M5QtW7Wecyuk4t
…rms (#131) Review of #131, each MEASURED on Claude Code 2.1.289: - Without Git Bash, Claude Code runs a hook's command string through PowerShell, which reads `"<root>/hook.cmd" working` as a ParserError: no states, and a visible hook error on every event. The hooks now use exec form (`cmd.exe` + args, no shell, available since 2.1.139); `call` keeps cmd from stripping the quotes off a plugin folder with brackets in it. The unit test runs every hook as written from such a folder. - A warm shell started with the other plugin answer was never adopted and never replaced, so every new tab in that folder started cold after the setting was switched. It now makes way. - core/.gitattributes carries the CRLF rule for hook.cmd into core-dist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FHHaWKR4M5QtW7Wecyuk4t
github-actions Bot
pushed a commit
that referenced
this pull request
Oct 4, 2026
…#132) * docs(spec): agent states from Claude Code hooks, design and plan (#131) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FHHaWKR4M5QtW7Wecyuk4t * feat(core): agent states from Claude Code hooks, a Failed mark (#131) A bundled Claude Code plugin prints a fixed OSC 777 through terminalSequence for each hook event; the core reads it as the agent's own word: working, question, done, failed with its kind. Failed is a new attention line, the Exact status setting hands new shells the plugin. Codex's Action Required title is a question. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FHHaWKR4M5QtW7Wecyuk4t * docs(core): hooks rules, privacy line, core 0.24.0 and app 0.30.0 (#131) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FHHaWKR4M5QtW7Wecyuk4t * fix(core): plugin hooks run without a shell, a switched setting re-warms (#131) Review of #131, each MEASURED on Claude Code 2.1.289: - Without Git Bash, Claude Code runs a hook's command string through PowerShell, which reads `"<root>/hook.cmd" working` as a ParserError: no states, and a visible hook error on every event. The hooks now use exec form (`cmd.exe` + args, no shell, available since 2.1.139); `call` keeps cmd from stripping the quotes off a plugin folder with brackets in it. The unit test runs every hook as written from such a folder. - A warm shell started with the other plugin answer was never adopted and never replaced, so every new tab in that folder started cold after the setting was switched. It now makes way. - core/.gitattributes carries the CRLF rule for hook.cmd into core-dist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FHHaWKR4M5QtW7Wecyuk4t --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.
Closes #131.
What and why
Today the tab indicator guesses an agent's state from the outside: the title, a process poll, output activity, and Claude's English footer text for questions. That guessing has two weak spots:
Claude Code hooks report these states exactly. This PR adds a small Claude Code plugin to the core,
core/claude-plugin. Each hook runshook.cmd, which just echoes a fixed line (no node, 20-100 ms measured). Claude writes that line'sterminalSequenceinto its own terminal:ESC]777;prism-agent;state=...[;kind=...]BEL. So:claude -pand SDK runs never write the sequence (measured), so nested runs can't light up a tab.Main gives a new shell the plugin through
CLAUDE_CODE_PLUGIN_DIRS:claudePluginDirand Settings > Appearance > Exact status from Claude Code is on (the default).The core reads the OSC 777, parses it purely, and passes it on through termBus to
useAgentIndicator.state=workingpermission_prompt/elicitation_dialog/elicitation_url_dialogquestiondoneerrorvalue plus a catch-allfailed;kind=<error>Failed is a new 3 px line:
agent-failed-on, on by default.marksApart).Codex's
[ ! ] Action Requiredtitle now counts as its Question.What stays of the old method
workingwith no Stop means idle and no Finished line. A Stop that lands after the idle title (the two race; seen in the real run) still marks Finished.Prism impact (core change: 0.24.0, a bump PR follows)
AttentionSettings, so itstermOptionscheck sees them automatically.failedIds, and itsthemedAgentColorsmay namefailed(otherwiseFAILED_RED).core/claude-pluginand passesclaudePluginDir. Until then no shell's environment changes in Prism. A unit test checks thatptyEnvwith no plugin option is byte for byte what it was.TermApigain an optionalhooksargument ontermSpawnandtermPrewarm.Gate
npm run typecheck: 0 errors.npm run lint: 0 errors, 12 warnings (the existing react-refresh ones).npm test: 88 files, 1323 tests passed.npm run e2e(full): 46 scenarios, 684 checks, 0 fails.agentHookse2e scenario (22 checks): a real pwsh prints the OSC 777 sequences. It covers:core/andsrc/restored from origin/main it showed 11 FAILs and a throw.npm run packageshipsresources/claude-plugin(.claude-plugin/plugin.json,hook.cmd,hooks/hooks.json), andPT_E2E_PACKAGED=1agentHooks passes 22/22.Real Claude Code check (2.1.289, the built app, parked like the e2e)
ANTHROPIC_MODEL=claude-bogus-model-0, no tokens): the tab went working, then failed. Tooltip:...agent-hooks\nFailed: model not found. The screen showed Claude's "There's an issue with the selected model".--model haiku --permission-mode default, amkdirin a scratch folder, one tiny-model turn): the tab went working, then question while "Do you want to proceed?" was on screen. After approval it went working, then done, and the folder was created.Not done here
The hands-on install on this machine still waits for the owner's go. The installer is unsigned, as are all releases until SignPath enrolment.
Update after review (94aabe1):
cmd.exe /d /c call ${CLAUDE_PLUGIN_ROOT}/hook.cmd <state> [kind]in exec form. The earlier command string failed under PowerShell, which is what Claude Code uses on a Windows with no Git Bash: every event raised a hook error. Checked in real Claude, including a folder with spaces and brackets.core/.gitattributeskeepshook.cmdCRLF incore-dist.Hands-on: the packaged 0.30.0 is installed on the owner's machine with
resources/claude-pluginpresent.🤖 Generated with Claude Code
https://claude.ai/code/session_01FHHaWKR4M5QtW7Wecyuk4t