父 RFC: #679
背景
当前 internal/config/runtime_hooks.go 已为 kind=command 留出校验骨架(要求 params.command 非空),但 internal/runtime/hooks/types.go 的 normalizeAndValidate 与 executor 尚未实装真实 handler,且 user/repo scope 显式拒绝 command kind。这是与 Claude Code hook 系统对齐的核心缺口——没有 command kind,用户写不了任意脚本扩展。
目标
开放 kind=command 给 user/repo scope,定义稳定的 stdin/stdout JSON 协议,使任意可执行脚本可作为 hook 接入,并保持当前安全边界(workdir 约束、context 白名单裁剪、failure policy)。
范围
协议
- stdin: 一行 JSON,字段为
sanitizeUserHookContext 输出的白名单 metadata + point/hook_id/payload_version
- stdout: 一行 JSON,字段:
status: pass / block / failed
message: string (可选)
update_input: 仅 CanUpdateInput=true 的点位允许 (本任务范围内可暂只接受 user_prompt_submit)
annotations: 进入 runtime annotation buffer
- 非零 exit + 空 stdout → 按 failure policy 处理
执行约束
- 通过
os/exec 调用,启用 cancel + timeout + max_in_flight
- 默认禁用 shell 拼接;
params.command 必须是 []string argv 形式 (拒绝单字符串 shell 模式),或显式 params.shell=true 才走 shell
- workdir = 当前 run 的 workspace
- 环境变量白名单:仅注入
NEOCODE_HOOK_* 前缀变量,不透传宿主环境
- repo scope command hook 受 trust gate 保护(与现 builtin 相同)
实装文件
internal/runtime/hooks/types.go: 解锁 user/repo + command 校验
internal/runtime/hooks/command_handler.go (新): exec wrapper
internal/config/runtime_hooks.go: 解除 external kind 拒绝(仅 command,保持 prompt/agent 仍拒绝)
docs/runtime-hooks-design.md: 协议章节
验收
依赖
- 依赖 #Task-A 完成后才有清晰能力查询
- 与 #Task-Schema (payload 版本化) 并行设计,落地需对齐
不在本任务范围
kind=prompt / kind=agent (仍延后)
- 非 loopback 的 http kind(仍延后)
父 RFC: #679
背景
当前
internal/config/runtime_hooks.go已为kind=command留出校验骨架(要求params.command非空),但internal/runtime/hooks/types.go的normalizeAndValidate与 executor 尚未实装真实 handler,且 user/repo scope 显式拒绝commandkind。这是与 Claude Code hook 系统对齐的核心缺口——没有 command kind,用户写不了任意脚本扩展。目标
开放
kind=command给 user/repo scope,定义稳定的 stdin/stdout JSON 协议,使任意可执行脚本可作为 hook 接入,并保持当前安全边界(workdir 约束、context 白名单裁剪、failure policy)。范围
协议
sanitizeUserHookContext输出的白名单 metadata +point/hook_id/payload_versionstatus:pass/block/failedmessage: string (可选)update_input: 仅CanUpdateInput=true的点位允许 (本任务范围内可暂只接受user_prompt_submit)annotations: 进入 runtime annotation buffer执行约束
os/exec调用,启用cancel + timeout + max_in_flightparams.command必须是[]stringargv 形式 (拒绝单字符串 shell 模式),或显式params.shell=true才走 shellNEOCODE_HOOK_*前缀变量,不透传宿主环境实装文件
internal/runtime/hooks/types.go: 解锁 user/repo + command 校验internal/runtime/hooks/command_handler.go(新): exec wrapperinternal/config/runtime_hooks.go: 解除 external kind 拒绝(仅 command,保持 prompt/agent 仍拒绝)docs/runtime-hooks-design.md: 协议章节验收
;/|/&的字符串拼接payload_version字段稳定(对齐 #Task-Schema)依赖
不在本任务范围
kind=prompt/kind=agent(仍延后)