gstack 中的 Claude Code Hook 变异实战:AskUserQuestion 的自动决策与确定性记录协议

发布时间:2026/10/28 17:12:11

gstack 中的 Claude Code Hook 变异实战:AskUserQuestion 的自动决策与确定性记录协议 gstack 中的 Claude Code Hook 变异实战AskUserQuestion 的自动决策与确定性记录协议【免费下载链接】gstackUse Garry Tans exact Claude Code setup: 23 opinionated tools that serve as CEO, Designer, Eng Manager, Release Manager, Doc Engineer, and QA项目地址: https://gitcode.com/GitHub_Trending/gs/gstack本文基于 gstack 仓库中的 spike 文档 claude-code-hook-mutation.md 展开回答一个具体的工程问题Claude Code 的 PreToolUse hook 能否通过updatedInput直接替换用户在AskUserQuestion工具中的回答文章将完整给出 hook 的 stdin/stdout 协议、permissionDecision四种取值的精确语义包括defer的已知陷阱、matcher 的覆盖策略与多 hook 并发规则并结合同仓库中三个真实 hook 实现自动决策、答案记录、失败兜底说明这套协议在 gstack 的 plan-tune 体系里是如何落地的。读完后你可以照着 spike 中的 settings.json 片段注册自己的 hook并理解 gstack 为何最终选择了保守替代方案。1. Spike 要回答的问题spike 文档开宗明义地提出了它要解决的问题原文档标注服务于 D10 与 D19/Codex 两个决策下游消费者为 T3、T5、T6、T8 四个任务Can a PreToolUse hook onAskUserQuestionactually substitute the users answer viaupdatedInput? If yes, whats the exact protocol?即针对AskUserQuestiongstack 中简称 AUQ这个向用户提问的工具一个 PreToolUse hook 能否真的用updatedInput把用户的回答替换掉如果能精确的协议是什么spike 的结论是肯定的updatedInput是受支持机制依据是 Claude Code 官方 hooks 文档spike 标注为 2026-04 的确认版本。gstack 需要这个机制的动机来自 plan-tune 的never-ask偏好当用户声明某类问题不要再问我时hook 应当代替用户自动选择推荐选项而不是依赖模型自觉遵守提示词。2. hook 的输入协议stdin schemaClaude Code 调用 hook 时会把一个 JSON 对象写入 hook 进程的 stdin。spike 中记录的 PreToolUse / PostToolUse 通用 schema 如下{ session_id: abc123, transcript_path: /path/to/transcript.jsonl, cwd: /current/working/dir, permission_mode: default, effort: { level: medium }, hook_event_name: PreToolUse, tool_name: AskUserQuestion, tool_input: { /* tool-specific */ }, tool_use_id: unique-id-12345 }在 subagent 上下文中还可能出现可选字段agent_id、agent_type。gstack 的实现里对这个 schema 的 TypeScript 类型声明可以对照 question-preference-hook.ts 中的HookStdin接口第 50–63 行它只声明了实际用到的session_id、hook_event_name、tool_name、tool_use_id、tool_input与cwd字段并对tool_input.questions[*]的question/options/multiSelect做了结构约束。3. hook 的输出协议PreToolUse 的 stdout schemahook 通过在 stdout 输出一段 JSON 来影响工具调用。spike 记录的allow updatedInput形态为{ hookSpecificOutput: { hookEventName: PreToolUse, permissionDecision: allow, permissionDecisionReason: auto-decided by plan-tune preference, updatedInput: { /* shallow-merged into original tool_input */ }, additionalContext: optional context for Claude } }3.1 permissionDecision 的四种取值spike 对四个取值给出了精确语义其中defer是重点陷阱allow— 放行可附带updatedInputdeny— 阻断反馈回给 Claude注意按 D 系列决策中的 Codex 修正deny不是合成一个假答案而是把原因作为反馈交给模型ask— 升级给用户决定defer— 暂停该工具调用等待外部恢复Claude Code v2.1.89 的 headless 特性之后用-p --resume重新求值。spike 特别警告永远不要用defer来表达无意见——在交互式会话中没有任何东西会恢复被暂停的调用工具会以Tool result missing due to internal error死掉对应 issue #2035、#2006。要弃权abstain应当退出码 0 且 stdout 为空或者只输出不含permissionDecision的、仅带additionalContext的hookSpecificOutput。3.2 updatedInput 的语义updatedInput的合并规则是浅合并shallow merge只把返回对象中出现的字段覆盖到原始tool_input上且仅在permissionDecision: allow时有效。正是这一点让为never-ask偏好自动代入答案成为可能。3.3 三个典型输出示例spike 原文继承自动决策PreToolUsenever-ask偏好 非单向门{ hookSpecificOutput: { hookEventName: PreToolUse, permissionDecision: allow, permissionDecisionReason: plan-tune: never-ask preference on ship-test-failure-triage, updatedInput: { questions: [{ /* same as input, but with auto-selected answer */ }] } } }透传无偏好或单向门安全覆盖退出码 0、stdout 为空。如果有需要注入的上下文比如 plan-tune 的记忆片段输出不带permissionDecision的additionalContext{ hookSpecificOutput: { hookEventName: PreToolUse, additionalContext: [plan-tune memory] Past answers suggest: ... } }spike 附带的历史注记值得所有 hook 作者警惕这个示例最初输出的是permissionDecision: defer在 Claude Code v2.1.89 给defer赋予暂停-恢复语义后导致每一次AskUserQuestion都失败#2035随后才改为空 stdout。PostToolUse 捕获总是执行{ hookSpecificOutput: { hookEventName: PostToolUse } }PostToolUse hook 也可以设置additionalContext向工具结果追加内容v1 的捕获不需要。4. Matcher同时覆盖原生与 MCP 变体~/.claude/settings.json中 hook 的matcher字段在包含正则元字符时按 JS 正则解析只含字母/下划线的 matcher 则是精确匹配。要同时覆盖原生AskUserQuestion与任意 MCP 服务器暴露的同名工具spike 给出的写法是matcher: (AskUserQuestion|mcp__.*__AskUserQuestion)这里有一个 gstack 特有的背景Conductor 通过--disallowedTools禁用原生AskUserQuestion改走mcp__conductor__AskUserQuestion路由——如果 matcher 不带 MCP 后缀hook 在那条路径上根本不会触发。gstack 三个 hook 的运行时还各自做了一层防御性过滤不依赖 matcher 已经过滤例如 question-log-hook.ts 第 280–286 行if ( toolName ! AskUserQuestion !toolName.match(/^mcp__.__AskUserQuestion$/) ) { // Matcher should have filtered this out; defensive no-op. process.exit(0); }5. 多 hook 并发与决策优先级spike 引用的平台行为是所有匹配的 hook 并行执行完全相同的 handler 会被自动去重。对 gstack 的含义gstack 恰好注册一个 PreToolUse hook 和一个 PostToolUse hookAUQ 形状的工具名如果用户自己另有一个对AskUserQuestion也返回updatedInput的 hook两次updatedInput的合并顺序是未定义的缓解措施在bin/gstack-settings-hook安装器的提示中记录该约束见 bin/gstack-settings-hook用户在接受前可以通过 diff 预览发现冲突。多个 hook 都给出决策时的permissionDecision优先级deny ask allow defer最严格的胜出。6. settings.json 注册片段T8 hook 安装器用spike 给出的完整注册片段如下可直接用于理解安装器 bin/gstack-settings-hook 写入~/.claude/settings.json的形状{ hooks: { PreToolUse: [ { matcher: (AskUserQuestion|mcp__.*__AskUserQuestion), hooks: [ { type: command, command: $CLAUDE_PROJECT_DIR/.claude/skills/gstack/hosts/claude/hooks/question-preference-hook, timeout: 5 } ] } ], PostToolUse: [ { matcher: (AskUserQuestion|mcp__.*__AskUserQuestion), hooks: [ { type: command, command: $CLAUDE_PROJECT_DIR/.claude/skills/gstack/hosts/claude/hooks/question-log-hook, timeout: 5 } ] } ] } }两个执行细节spike 原文 仓库佐证hook 的command字符串由 Claude Code 的 hook runner 经/bin/sh执行必须使用绝对路径或$CLAUDE_PROJECT_DIR变量替换仓库里的 hook 本体是 TypeScript 文件settings.json 引用的其实是一个 bash shim见 question-preference-hook内容仅 5 行#!/usr/bin/env bash # Bash shim — Claude Code hooks run command strings via /bin/sh, so this # wrapper makes the TypeScript hook executable via bun. Settings.json # references this file directly. set -e HERE$(cd $(dirname $0) pwd) exec bun $HERE/question-preference-hook.ts这与 spike 中Hook commands takebuninvocation under the hood的描述一一对应。7. 协议落地gstack 的三个 hook 实现spike 定下协议契约后gstack 在 hosts/claude/hooks/ 下实现了三个 hook各自对应 plan-tune 的不同任务。7.1 question-preference-hook为什么用 denyreason 而不是 allowupdatedInputquestion-preference-hook.ts 是 T6 的自动决策 hook。有意思的是它的文件头注释明确说明了一个协议验证了但实现选了更保守路径的决策Why denyreason instead of allowupdatedInput: AskUserQuestionsupdatedInputshape for pre-resolve this question isnt structurally pinned in Claude Code docs (spike T4 left as open question).denywith a reason that names the auto-decided option is conservative reliable ... When the spike around input mutation lands, we can swap to allowupdatedInput without changing the contract.也就是说spike 证明了机制可行但预解析问题这一具体形态的updatedInput形状未在文档中被结构化固定实现因此选择deny 在permissionDecisionReason里写明自动决策结果让模型读到被拒绝的反馈后直接按推荐选项继续、不再次提问。这个决策树的完整形态在文件头注释第 8–19 行中通过gstack-qid:foo-bar标记提取question_id无标记则跳过强制执行——哈希 ID 只被观察永不作为偏好键对应 D18从 scripts/question-registry.ts 查询door_type默认 two-way按项目级 全局优先级读取偏好D8应用规则never-ask one-way→ 透传单向门永远要问人never-ask two-way 标记→ 自动决策always-ask或无偏好 → 透传。几个关键实现细节弃权即空 stdoutpassThrough()第 100–120 行严格遵守 spike 的 defer 陷阱结论注释里直接引用了 #2035/#2006多问题全有或全无对一次携带多个问题的 AUQ只有当所有问题都满足有标记 never-ask 安全门型时才整体 deny 自动决策混合情况一律透传让用户作答第 416–456 行的fullyAutoDecidable逻辑未注册 ID 的防御#2024 修复后对注册表外的 question_id 会额外跑 scripts/one-way-doors.ts 的classifyQuestion正则分类器避免一个临时性的破坏性问题DESTRUCTIVE带着旧的 never-ask 偏好被自动放行推荐项解析spike 遗留问题 1 的落地extractRecommended()第 278–297 行先找选项 label 上的(recommended)后缀正则/\(recommended\)\s*$/i失败则回退到问题文本里的Recommendation: X散文匹配多候选或零候选都视为歧义而拒绝自动决策D2 的 refuse-on-ambiguousdeny 的记账补偿deny会使 PostToolUse 不再触发所以 PreToolUse 一侧自己调用gstack-question-log记录source: auto-decided事件logAutoDecided第 325–357 行并写.auto-decided-tool_use_id标记文件——这正是 spike遗留问题 2自动决策事件打标中提出的 marker file 方案的实现Conductor 特殊路径在 Conductor 会话中 AUQ 不可靠原生被禁用、MCP 变体不稳定hook 会 deny 并把提问强制改写成 prose 决策简报第 480–498 行。7.2 question-log-hookPostToolUse 确定性捕获question-log-hook.ts 是 T5 的答案记录 hook从tool_input/tool_response中抽取每个问题与用户选项经gstack-question-log落库no agent compliance required。值得注意的鲁棒性设计tool_response形状不固定原生与 MCP 变体返回结构不同extractUserChoices()第 166–241 行依次尝试 Shape D{ answers: {问题文本: 答案}, annotations? }当前原生形态、Shape Aanswers数组、Shape Bquestions[*].user_answer未识别的形状只记诊断日志而不嵌入记录that poisons user_choice for every downstream metricquestion_id 策略标记优先gstack-qid:foo-bar无标记时回退到sha1(skill::question::sorted-options)的hook-前缀哈希 ID——同样只被观察、不作为偏好键不变式永远退出码 0失败只写~/.gstack/hook-errors.log绝不阻塞用户会话。7.3 auq-error-fallback-hookspike 中 UNVERIFIED 一节的落地OV3:Bspike 文档保留了一个当时无法在 harness 中验证的开放问题当 MCP 工具调用返回传输/缺结果错误Conductor bug 的[Tool result missing due to internal error]时Claude Code 是否会触发 PostToolUse hook官方文档只覆盖成功路径。spike 的决策OV3:B A是防御性地照样构建因为它在成功路径上惰性只有isErrorResponse(tool_response)为真才动作即使平台在该错误路径上从不触发它也没有危害提示词层的兜底generate-ask-user-format.ts无论如何都覆盖该场景——hook 只是可靠性层不是机制本身。对应的实现是 auq-error-fallback-hook.tsspike 提到的确定性单测即 test/auq-error-fallback-hook.test.ts。两个被单测覆盖的纯函数isErrorResponse()第 96–123 行保守判定错误形状——空值/空串、匹配tool result missing哨兵短语只匹配这个特定短语避免真实答案如 Investigate the internal error 误触发这是 Codex 评审提出的、is_error true布尔真而非序列化字符串里的子串、非空error字段以及{ content: ... }包装形态directiveFor(kind)第 143–169 行按SESSION_KIND注入不同的additionalContext指令——spawned会话自动选推荐项、不阻塞headless报BLOCKED并停止interactive要求立即渲染 prose 决策ELI10 Recommendation 行 每个选项一段附(recommended)标记与Completeness: X/10。spike 给出的后续补全验证的建议也原样保留在文档中注册一个只打日志的临时 PostToolUse hook先(a)触发普通工具错误确认 PostToolUse 在工具出错时到底会不会触发再(b)复现 Conductor 的 MCP AUQ 失败观察日志若 (b) 确认可触发就把该 hook 从防御/惰性升级为已验证。在此之前运行时层按 best-effort 对待提示词层兜底才是保证路径。7.4 子进程与 Windows 兼容三个 hook 都通过 spawn-bin.ts 的runBin()调用bin/下的 bash 脚本如gstack-question-log、gstack-session-kind该模块修复了两个 Windows 专属坑fileURLToPath替代new URL(...).pathname否则路径被解析成C:\C:\Users\...以及显式经 Git Bash 执行无扩展名脚本Windows 不支持 shebang。调用时还特意以发起工具调用时的cwd为工作目录运行保证gstack-slug解析到用户实际所在的项目而非 hook 脚本所在目录。8. spike 移交实现的三个遗留问题spike 末尾列出了留给实现阶段的开放问题现在可以逐一对照仓库看它们的落点推荐项解析范围D2 规定先解析(recommended)标签标签位于选项的label字段上实现需要遍历tool_input.questions[*].options[*]找 label 后缀。实际工作中选项形如ship/SKILL.md.tmpl产出的A) Fix now (recommended)。→ 已实现为extractRecommended()见 7.1 节并增加了散文回退与歧义拒绝。自动决策事件打标需要 PostToolUse payload 上的额外字段如was_auto_decided: truespike 提出的方案是 PreToolUse 写标记文件~/.gstack/sessions/id/.auto-decided-tool_use_id、PostToolUse 读取后删除。→ 实现见markAutoDecided()question-preference-hook.ts 第 308–318 行且因为deny使 PostToolUse 不触发记账改由 PreToolUse 直接完成。超时行为hook 默认超时 60 秒但文档对超时后的行为描述很薄。spike 决定显式设置timeout: 5让用户在 hook 故障时永远不超过 5 秒等待超时则回退为透传。→ settings.json 片段中的timeout: 5即此决策。9. 可验证要点小结结论依据updatedInput是 PreToolUse 替换tool_input的受支持机制浅合并、仅allow时有效spike 文档 §Answer、§stdout schemadefer自 CC v2.1.89 起是暂停待外部恢复交互式会话中误用会让 AUQ 全部失败spike §permissionDecision#2035、#2006及 question-preference-hook.ts 注释matcher 需写(AskUserQuestion|mcp__.*__AskUserQuestion)才能覆盖 Conductor 的 MCP 路由spike §Matcher schema多 hook 决策优先级deny ask allow deferupdatedInput合并顺序未定义spike §Multiple-hook concurrency caveathook command 必须绝对路径或$CLAUDE_PROJECT_DIRTS 本体经 bash shim 由 bun 执行spike §Settings.json snippet 各 bash shim 文件PostToolUse 在工具出错时是否触发仍属未验证防御性 hook 按 best-effort 对待spike §PostToolUse on tool errorUNVERIFIED auq-error-fallback-hook.ts 头注释如果你要为自己的工具链注册类似的 AUQ hook建议直接以 spike 文档中的 settings.json 片段为起点先实现弃权即空 stdout的最小安全版本再按需叠加allowupdatedInput或denyreason的决策分支——gstack 的三个实现文件就是这套协议在真实约束下超时 5 秒、永不阻塞会话、Windows 兼容、MCP 变体的完整参考。【免费下载链接】gstackUse Garry Tans exact Claude Code setup: 23 opinionated tools that serve as CEO, Designer, Eng Manager, Release Manager, Doc Engineer, and QA项目地址: https://gitcode.com/GitHub_Trending/gs/gstack创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/10/28 17:08:02

AI论文平台评测:提升学术写作效率的8大工具

1. 项目概述:AI论文平台如何改变学术写作生态作为一名经历过论文煎熬期的过来人,我深刻理解学生们面对毕业论文时的焦虑。去年指导表弟写本科论文时,偶然发现现在AI论文工具已经发展到可以真正提升学术写作效率的阶段。经过半年时间实测8个主…

2026/10/28 17:08:02

二叉树算法解析:从基础遍历到LeetCode Hot100实战

1. 二叉树基础与Hot100算法概述二叉树作为数据结构领域的核心基础,在算法面试和实际工程中占据着不可替代的地位。最近在整理LeetCode Hot100题目时,我发现二叉树相关题目出现频率极高,几乎每10道经典题中就包含2-3道二叉树问题。这促使我系统…

2026/10/28 17:08:02

Windows注册表修改:查找系统设置对应项的方法

1. 为什么需要查找系统设置对应的注册表项作为一名Windows系统管理员,我经常遇到需要批量修改系统配置的场景。图形界面设置虽然直观,但在以下三种情况下直接操作注册表会更高效:需要批量部署相同配置到多台电脑时某些隐藏的高级设置没有提供…

2026/10/28 17:08:02

二叉树算法精解:Hot100高频题与工程实践

1. 二叉树基础与Hot100算法题解析二叉树作为数据结构领域的常青树,在算法面试中出现的频率高达75%以上。最近在整理LeetCode Hot100榜单时,我发现前50题中有18道直接考察二叉树操作,这个比例在各类数据结构中遥遥领先。本文将结合高频考题&am…

2026/10/28 17:03:01

蓝桥杯Python组备考:真题刷题顺序与高频题型避坑指南

简介:面向Python方向蓝桥杯备赛的完整资料合集,涵盖历年真题及解析、模拟题题库、知识点思维导图等,适合备战蓝桥杯及NOC等竞赛的考生和指导教师。包内共2000个文件,包含in/out测试数据、Python与C源码、Word/PDF文档、PPT课件及压…

2026/10/28 14:42:12

全国计算机二级C语言真题PDF高效刷题指南

简介:这份PDF是全国计算机二级C语言考试的试题与答案解析,专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/28 15:28:56

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介:一份以实战为导向的Python爬虫入门资源,面向具备基本编程基础、希望快速掌握网络数据抓取的初学者,可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档,完整演示从网页中提取 标题…

2026/10/28 3:32:29

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介:这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档,压缩包大小七十八KB,内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开:先构造一个极点位于单位圆内…

2026/10/28 0:02:13

Python股票自动交易系统源码解析:从行情获取到风控的完整链路

简介:这份资源是面向高校计算机、金融相关专业毕业设计场景的完整项目源码包,主题为基于Python的股票自动交易系统,适合需要完成毕设选题、课程设计或自学金融量化交易开发的学生与开发者参考。压缩包共收录687个文件,整体约14.93…

2026/10/28 0:02:13

视酷酷信IM源码部署指南:Java 17+MySQL 8.0.33实战配置

简介:这是一套2024年亲测可部署的视酷酷信IM即时通讯系统源码,面向具备Java、OC、C#及Vue开发能力的技术人员,解决私有化即时通讯平台快速搭建与二次定制需求,尤其适用于需集成商城、保障数据主权的中小型企业或独立开发者。资源共…

2026/10/28 0:02:13

Python爬取百度迁徙数据:接口解析、批量采集与避坑指南

简介:这份资源是一个用Python爬取百度迁徙数据的脚本压缩包,面向具备Python基础、希望入门网络爬虫与数据解析的开发者,解决按日期获取全国热门城市人口迁徙数据的问题。包内共1个文件,为单个py脚本,压缩包约1KB&#…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑