add-coder 0.3.27 → 0.3.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +252 -150
- package/package.json +2 -1
- package/templates/.add-coder-src-hash.json +126 -148
- package/templates/adapters/claude/hooks/doc-format-guard.ts +32 -0
- package/templates/adapters/claude/hooks/lib/claude-env.ts +7 -0
- package/templates/adapters/claude/hooks/notification.ts +24 -0
- package/templates/adapters/claude/hooks/permission-denied.ts +33 -0
- package/templates/adapters/claude/hooks/permission-gate.ts +12 -0
- package/templates/adapters/claude/hooks/post-tool-failure.ts +4 -0
- package/templates/adapters/claude/hooks/post-tool-use.ts +17 -0
- package/templates/adapters/claude/hooks/pre-compact.ts +10 -0
- package/templates/adapters/claude/hooks/pre-tool-use.ts +81 -0
- package/templates/adapters/claude/hooks/prompt-submit.ts +82 -0
- package/templates/adapters/claude/hooks/review-checklist.ts +4 -0
- package/templates/adapters/claude/hooks/session-end.ts +10 -0
- package/templates/adapters/claude/hooks/session-start.ts +24 -0
- package/templates/adapters/claude/hooks/stop-check.ts +15 -0
- package/templates/adapters/claude/hooks/stop-failure.ts +34 -0
- package/templates/adapters/claude/hooks/subagent-guard.ts +25 -0
- package/templates/adapters/claude/hooks/subagent-stop.ts +12 -0
- package/templates/adapters/claude/settings.json +13 -13
- package/templates/adapters/codex/hooks/doc-format-guard.ts +30 -0
- package/templates/adapters/codex/hooks/notification.ts +26 -0
- package/templates/adapters/codex/hooks/permission-gate.ts +15 -0
- package/templates/adapters/codex/hooks/post-tool-failure.ts +14 -0
- package/templates/adapters/codex/hooks/post-tool-use.ts +85 -0
- package/templates/adapters/codex/hooks/pre-compact.ts +20 -0
- package/templates/adapters/codex/hooks/pre-tool-use.ts +141 -0
- package/templates/adapters/codex/hooks/prompt-submit.ts +38 -0
- package/templates/adapters/codex/hooks/review-checklist.ts +5 -0
- package/templates/adapters/codex/hooks/session-end.ts +19 -0
- package/templates/adapters/codex/hooks/session-start.ts +45 -0
- package/templates/adapters/codex/hooks/stop-check.ts +59 -0
- package/templates/adapters/codex/hooks/subagent-guard.ts +22 -0
- package/templates/adapters/codex/hooks/subagent-stop.ts +20 -0
- package/templates/adapters/codex/hooks.json +5 -5
- package/templates/adapters/qoder/hooks/doc-format-guard.ts +34 -0
- package/templates/adapters/qoder/hooks/lib/context-inject.ts +61 -0
- package/templates/adapters/qoder/hooks/lib/qoder-env.ts +23 -0
- package/templates/adapters/qoder/hooks/lib/review-checklist.ts +5 -0
- package/templates/adapters/qoder/hooks/notification.ts +34 -0
- package/templates/adapters/qoder/hooks/permission-gate.ts +26 -0
- package/templates/adapters/qoder/hooks/post-tool-failure.ts +30 -0
- package/templates/adapters/qoder/hooks/post-tool-use.ts +32 -0
- package/templates/adapters/qoder/hooks/pre-compact.ts +16 -0
- package/templates/adapters/qoder/hooks/pre-tool-use.ts +112 -0
- package/templates/adapters/qoder/hooks/prompt-submit.ts +66 -0
- package/templates/adapters/qoder/hooks/review-checklist.ts +24 -0
- package/templates/adapters/qoder/hooks/session-end.ts +27 -0
- package/templates/adapters/qoder/hooks/session-start.ts +83 -0
- package/templates/adapters/qoder/hooks/stop-check.ts +69 -0
- package/templates/adapters/qoder/hooks/subagent-guard.ts +16 -0
- package/templates/adapters/qoder/hooks/subagent-stop.ts +33 -0
- package/templates/adapters/qoder/settings.json +13 -13
- package/templates/adapters/trae/hooks/doc-format-guard.ts +30 -0
- package/templates/adapters/trae/hooks/notification.ts +22 -0
- package/templates/adapters/trae/hooks/permission-gate.ts +15 -0
- package/templates/adapters/trae/hooks/post-tool-failure.ts +14 -0
- package/templates/adapters/trae/hooks/post-tool-use.ts +24 -0
- package/templates/adapters/trae/hooks/pre-compact.ts +15 -0
- package/templates/adapters/trae/hooks/pre-tool-use.ts +40 -0
- package/templates/adapters/trae/hooks/prompt-submit.ts +29 -0
- package/templates/adapters/trae/hooks/review-checklist.ts +5 -0
- package/templates/adapters/trae/hooks/session-end.ts +16 -0
- package/templates/adapters/trae/hooks/session-start.ts +15 -0
- package/templates/adapters/trae/hooks/stop-check.ts +20 -0
- package/templates/adapters/trae/hooks/subagent-guard.ts +22 -0
- package/templates/adapters/trae/hooks/subagent-stop.ts +15 -0
- package/templates/adapters/trae/hooks.json +1 -1
- package/templates/adapters/vscode/hooks/doc-format-guard.ts +27 -0
- package/templates/adapters/vscode/hooks/notification.ts +22 -0
- package/templates/adapters/vscode/hooks/permission-gate.ts +15 -0
- package/templates/adapters/vscode/hooks/post-tool-failure.ts +32 -0
- package/templates/adapters/vscode/hooks/post-tool-use.ts +25 -0
- package/templates/adapters/vscode/hooks/pre-compact.ts +15 -0
- package/templates/adapters/vscode/hooks/pre-tool-use.ts +78 -0
- package/templates/adapters/vscode/hooks/prompt-submit.ts +74 -0
- package/templates/adapters/vscode/hooks/review-checklist.ts +5 -0
- package/templates/adapters/vscode/hooks/session-end.ts +16 -0
- package/templates/adapters/vscode/hooks/session-start.ts +24 -0
- package/templates/adapters/vscode/hooks/stop-check.ts +21 -0
- package/templates/adapters/vscode/hooks/subagent-guard.ts +21 -0
- package/templates/adapters/vscode/hooks/subagent-stop.ts +15 -0
- package/templates/core/docs/ADD-governance-claude-code.md +3 -3
- package/templates/core/docs/ADD-governance-codex.md +5 -5
- package/templates/core/docs/ADD-governance-qoder-cn.md +4 -4
- package/templates/core/docs/ADD-governance-trae.md +2 -2
- package/templates/core/docs/ADD-governance-vscode-copilot.md +21 -22
- package/templates/core/governance/audit-bridge.ts +47 -0
- package/templates/core/governance/common.ts +299 -0
- package/templates/core/governance/context-inject.ts +53 -0
- package/templates/core/governance/doc-format-guard.ts +440 -0
- package/templates/core/governance/notification-router.ts +62 -0
- package/templates/core/governance/notify.ts +66 -0
- package/templates/core/governance/permission-gate-router.ts +42 -0
- package/templates/core/governance/post-tool-failure-router.ts +56 -0
- package/templates/core/governance/post-tool-router.ts +212 -0
- package/templates/core/governance/pre-compact-guard.ts +41 -0
- package/templates/core/governance/pre-tool-guard.ts +309 -0
- package/templates/core/governance/preload-templates.ts +241 -0
- package/templates/core/governance/prompt-router.ts +222 -0
- package/templates/core/governance/review-checklist-guard.ts +186 -0
- package/templates/core/governance/rules.ts +720 -0
- package/templates/core/governance/session-end.ts +88 -0
- package/templates/core/governance/session-start-guard.ts +99 -0
- package/templates/core/governance/state-detect.ts +6 -0
- package/templates/core/governance/stop-router.ts +140 -0
- package/templates/core/governance/subagent-guard-router.ts +35 -0
- package/templates/core/governance/subagent-stop-router.ts +93 -0
- package/templates/core/governance/subagent-stop.ts +75 -0
- package/templates/core/governance/vocabulary.ts +109 -0
- package/templates/core/hooks/doc-format-guard.ts +21 -0
- package/templates/core/hooks/notification.ts +11 -0
- package/templates/core/hooks/permission-gate.ts +7 -0
- package/templates/core/hooks/post-tool-failure.ts +7 -0
- package/templates/core/hooks/post-tool-use.ts +23 -0
- package/templates/core/hooks/pre-compact.ts +9 -0
- package/templates/core/hooks/pre-tool-use.ts +30 -0
- package/templates/core/hooks/prompt-submit.ts +11 -0
- package/templates/core/hooks/review-checklist.ts +4 -0
- package/templates/core/hooks/session-end.ts +10 -0
- package/templates/core/hooks/session-start.ts +9 -0
- package/templates/core/hooks/stop-check.ts +14 -0
- package/templates/core/hooks/subagent-guard.ts +8 -0
- package/templates/core/hooks/subagent-stop.ts +9 -0
- package/templates/core/scripts/mcp-server/tools/gateway/check_rahs.ts +12 -1
- package/templates/core/scripts/mcp-server/tools/plan.ts +12 -2
- package/templates/adapters/claude/hooks/doc-format-guard.sh +0 -283
- package/templates/adapters/claude/hooks/lib/common.sh +0 -220
- package/templates/adapters/claude/hooks/lib/context-inject.sh +0 -96
- package/templates/adapters/claude/hooks/lib/notify.sh +0 -36
- package/templates/adapters/claude/hooks/lib/preload-templates.sh +0 -212
- package/templates/adapters/claude/hooks/lib/session-end.sh +0 -76
- package/templates/adapters/claude/hooks/lib/state-detect.sh +0 -13
- package/templates/adapters/claude/hooks/lib/subagent-stop.sh +0 -90
- package/templates/adapters/claude/hooks/lib/vocabulary.sh +0 -62
- package/templates/adapters/claude/hooks/notification.sh +0 -45
- package/templates/adapters/claude/hooks/permission-denied.sh +0 -42
- package/templates/adapters/claude/hooks/permission-gate.sh +0 -18
- package/templates/adapters/claude/hooks/post-tool-failure.sh +0 -10
- package/templates/adapters/claude/hooks/post-tool-use.sh +0 -35
- package/templates/adapters/claude/hooks/pre-compact.sh +0 -37
- package/templates/adapters/claude/hooks/pre-tool-use.sh +0 -153
- package/templates/adapters/claude/hooks/prompt-submit.sh +0 -99
- package/templates/adapters/claude/hooks/review-checklist.sh +0 -10
- package/templates/adapters/claude/hooks/session-end.sh +0 -21
- package/templates/adapters/claude/hooks/session-start.sh +0 -36
- package/templates/adapters/claude/hooks/stop-check.sh +0 -54
- package/templates/adapters/claude/hooks/stop-failure.sh +0 -44
- package/templates/adapters/claude/hooks/subagent-guard.sh +0 -36
- package/templates/adapters/claude/hooks/subagent-stop.sh +0 -20
- package/templates/adapters/codex/hooks/doc-format-guard.sh +0 -343
- package/templates/adapters/codex/hooks/lib/common.sh +0 -209
- package/templates/adapters/codex/hooks/lib/context-inject.sh +0 -96
- package/templates/adapters/codex/hooks/lib/notify.sh +0 -36
- package/templates/adapters/codex/hooks/lib/preload-templates.sh +0 -208
- package/templates/adapters/codex/hooks/lib/session-end.sh +0 -76
- package/templates/adapters/codex/hooks/lib/state-detect.sh +0 -8
- package/templates/adapters/codex/hooks/lib/subagent-stop.sh +0 -90
- package/templates/adapters/codex/hooks/lib/vocabulary.sh +0 -51
- package/templates/adapters/codex/hooks/notification.sh +0 -30
- package/templates/adapters/codex/hooks/permission-gate.sh +0 -18
- package/templates/adapters/codex/hooks/post-tool-failure.sh +0 -10
- package/templates/adapters/codex/hooks/post-tool-use.sh +0 -43
- package/templates/adapters/codex/hooks/pre-compact.sh +0 -37
- package/templates/adapters/codex/hooks/pre-tool-use.sh +0 -130
- package/templates/adapters/codex/hooks/prompt-submit.sh +0 -92
- package/templates/adapters/codex/hooks/review-checklist.sh +0 -10
- package/templates/adapters/codex/hooks/session-end.sh +0 -38
- package/templates/adapters/codex/hooks/session-start.sh +0 -63
- package/templates/adapters/codex/hooks/stop-check.sh +0 -71
- package/templates/adapters/codex/hooks/subagent-guard.sh +0 -15
- package/templates/adapters/codex/hooks/subagent-stop.sh +0 -47
- package/templates/adapters/qoder/hooks/doc-format-guard.sh +0 -341
- package/templates/adapters/qoder/hooks/lib/common.sh +0 -223
- package/templates/adapters/qoder/hooks/lib/context-inject.sh +0 -96
- package/templates/adapters/qoder/hooks/lib/notify.sh +0 -36
- package/templates/adapters/qoder/hooks/lib/preload-templates.sh +0 -212
- package/templates/adapters/qoder/hooks/lib/session-end.sh +0 -76
- package/templates/adapters/qoder/hooks/lib/state-detect.sh +0 -13
- package/templates/adapters/qoder/hooks/lib/subagent-stop.sh +0 -90
- package/templates/adapters/qoder/hooks/lib/vocabulary.sh +0 -62
- package/templates/adapters/qoder/hooks/notification.sh +0 -25
- package/templates/adapters/qoder/hooks/permission-gate.sh +0 -8
- package/templates/adapters/qoder/hooks/post-tool-failure.sh +0 -8
- package/templates/adapters/qoder/hooks/post-tool-use.sh +0 -121
- package/templates/adapters/qoder/hooks/pre-compact.sh +0 -37
- package/templates/adapters/qoder/hooks/pre-tool-use.sh +0 -154
- package/templates/adapters/qoder/hooks/prompt-submit.sh +0 -96
- package/templates/adapters/qoder/hooks/review-checklist.sh +0 -157
- package/templates/adapters/qoder/hooks/session-end.sh +0 -24
- package/templates/adapters/qoder/hooks/session-start.sh +0 -45
- package/templates/adapters/qoder/hooks/stop-check.sh +0 -59
- package/templates/adapters/qoder/hooks/subagent-guard.sh +0 -27
- package/templates/adapters/qoder/hooks/subagent-stop.sh +0 -26
- package/templates/adapters/trae/hooks/doc-format-guard.sh +0 -343
- package/templates/adapters/trae/hooks/lib/common.sh +0 -220
- package/templates/adapters/trae/hooks/lib/context-inject.sh +0 -96
- package/templates/adapters/trae/hooks/lib/notify.sh +0 -36
- package/templates/adapters/trae/hooks/lib/preload-templates.sh +0 -212
- package/templates/adapters/trae/hooks/lib/session-end.sh +0 -76
- package/templates/adapters/trae/hooks/lib/state-detect.sh +0 -10
- package/templates/adapters/trae/hooks/lib/subagent-stop.sh +0 -90
- package/templates/adapters/trae/hooks/lib/vocabulary.sh +0 -62
- package/templates/adapters/trae/hooks/notification.sh +0 -45
- package/templates/adapters/trae/hooks/permission-gate.sh +0 -18
- package/templates/adapters/trae/hooks/post-tool-failure.sh +0 -10
- package/templates/adapters/trae/hooks/post-tool-use.sh +0 -138
- package/templates/adapters/trae/hooks/pre-compact.sh +0 -37
- package/templates/adapters/trae/hooks/pre-tool-use.sh +0 -223
- package/templates/adapters/trae/hooks/prompt-submit.sh +0 -87
- package/templates/adapters/trae/hooks/review-checklist.sh +0 -10
- package/templates/adapters/trae/hooks/session-end.sh +0 -38
- package/templates/adapters/trae/hooks/session-start.sh +0 -53
- package/templates/adapters/trae/hooks/stop-check.sh +0 -54
- package/templates/adapters/trae/hooks/subagent-guard.sh +0 -15
- package/templates/adapters/trae/hooks/subagent-stop.sh +0 -47
- package/templates/adapters/vscode/hooks/doc-format-guard.sh +0 -282
- package/templates/adapters/vscode/hooks/lib/common.sh +0 -220
- package/templates/adapters/vscode/hooks/lib/context-inject.sh +0 -96
- package/templates/adapters/vscode/hooks/lib/notify.sh +0 -36
- package/templates/adapters/vscode/hooks/lib/preload-templates.sh +0 -212
- package/templates/adapters/vscode/hooks/lib/session-end.sh +0 -76
- package/templates/adapters/vscode/hooks/lib/state-detect.sh +0 -13
- package/templates/adapters/vscode/hooks/lib/subagent-stop.sh +0 -90
- package/templates/adapters/vscode/hooks/lib/vocabulary.sh +0 -62
- package/templates/adapters/vscode/hooks/notification.sh +0 -43
- package/templates/adapters/vscode/hooks/permission-gate.sh +0 -18
- package/templates/adapters/vscode/hooks/post-tool-failure.sh +0 -28
- package/templates/adapters/vscode/hooks/post-tool-use.sh +0 -35
- package/templates/adapters/vscode/hooks/pre-compact.sh +0 -37
- package/templates/adapters/vscode/hooks/pre-tool-use.sh +0 -153
- package/templates/adapters/vscode/hooks/prompt-submit.sh +0 -99
- package/templates/adapters/vscode/hooks/review-checklist.sh +0 -10
- package/templates/adapters/vscode/hooks/session-end.sh +0 -13
- package/templates/adapters/vscode/hooks/session-start.sh +0 -35
- package/templates/adapters/vscode/hooks/stop-check.sh +0 -62
- package/templates/adapters/vscode/hooks/subagent-guard.sh +0 -37
- package/templates/adapters/vscode/hooks/subagent-stop.sh +0 -12
- package/templates/core/hooks/doc-format-guard.sh +0 -343
- package/templates/core/hooks/lib/common.sh +0 -220
- package/templates/core/hooks/lib/context-inject.sh +0 -96
- package/templates/core/hooks/lib/notify.sh +0 -36
- package/templates/core/hooks/lib/preload-templates.sh +0 -212
- package/templates/core/hooks/lib/session-end.sh +0 -76
- package/templates/core/hooks/lib/state-detect.sh +0 -10
- package/templates/core/hooks/lib/subagent-stop.sh +0 -90
- package/templates/core/hooks/lib/vocabulary.sh +0 -62
- package/templates/core/hooks/notification.sh +0 -45
- package/templates/core/hooks/permission-gate.sh +0 -18
- package/templates/core/hooks/post-tool-failure.sh +0 -10
- package/templates/core/hooks/post-tool-use.sh +0 -138
- package/templates/core/hooks/pre-compact.sh +0 -37
- package/templates/core/hooks/pre-tool-use.sh +0 -223
- package/templates/core/hooks/prompt-submit.sh +0 -87
- package/templates/core/hooks/review-checklist.sh +0 -10
- package/templates/core/hooks/session-end.sh +0 -38
- package/templates/core/hooks/session-start.sh +0 -53
- package/templates/core/hooks/stop-check.sh +0 -54
- package/templates/core/hooks/subagent-guard.sh +0 -15
- package/templates/core/hooks/subagent-stop.sh +0 -47
- package/templates/shared/hooks-lib/common.sh +0 -22
|
@@ -19,9 +19,9 @@ VS Code Copilot 支持 10 种 Agent Hook 事件(官方 8 + Cloud Agent 2):
|
|
|
19
19
|
| 子 agent | **SubagentStart**、**SubagentStop** |
|
|
20
20
|
| 其他 | **PreCompact**、errorOccurred(Cloud Agent 专属) |
|
|
21
21
|
|
|
22
|
-
配置位置:`.
|
|
22
|
+
配置位置:`.vscode/hooks/*.mjs`(TS 源码 esbuild 烘焙零依赖产物,node 直调——2026-08-14 node 化实态,bash 待退役)
|
|
23
23
|
|
|
24
|
-
**关键差异 vs Claude Code**:不支持 PostToolUseFailure / StopFailure / Notification / PermissionRequest。但 **VS Code 1.129+ Agent Host 同时读取 `.claude/settings.json`**,`npx add-coder init --adapter=vscode` 会同步产出 `.claude/`
|
|
24
|
+
**关键差异 vs Claude Code**:不支持 PostToolUseFailure / StopFailure / Notification / PermissionRequest。但 **VS Code 1.129+ Agent Host 同时读取 `.claude/settings.json`**,`npx add-coder init --adapter=vscode` 会同步产出 `.claude/` 目录,双通道共享同一套 `.mjs` 产物(node 直调)。
|
|
25
25
|
|
|
26
26
|
---
|
|
27
27
|
|
|
@@ -48,36 +48,35 @@ errorOccurred ────────────────→ ⑮ 错误分
|
|
|
48
48
|
|
|
49
49
|
## 注入通道
|
|
50
50
|
|
|
51
|
-
VS Code Copilot 的注入通道为
|
|
51
|
+
VS Code Copilot 的注入通道为 **hook 配置的 `command` stdout**(node 直调 `.mjs` 产物)。
|
|
52
52
|
|
|
53
53
|
VS Code 1.129+ 的 Agent Host 架构让同一项目支持多 Agent 并行运行——Copilot、Claude Code 各走各的通道:
|
|
54
54
|
|
|
55
|
-
| 通道 | 配置位置 |
|
|
55
|
+
| 通道 | 配置位置 | 产物路径 | 说明 |
|
|
56
56
|
|---|---|---|---|
|
|
57
|
-
| **VS Code 原生** | `.
|
|
58
|
-
| **Agent Host (Claude)** | `.claude/settings.json` | `.claude/hooks
|
|
57
|
+
| **VS Code 原生** | `.vscode/hooks/` | `.vscode/hooks/*.mjs` | 14 入口 node 产物,command 直调 |
|
|
58
|
+
| **Agent Host (Claude)** | `.claude/settings.json` | `.claude/hooks/*.mjs` | 同一套产物,Claude Code agent 直接读取 |
|
|
59
59
|
|
|
60
60
|
```
|
|
61
61
|
npx add-coder init --adapter=vscode
|
|
62
62
|
│
|
|
63
|
-
├──→ .
|
|
63
|
+
├──→ .vscode/hooks/*.mjs (14 入口,node 直调)
|
|
64
64
|
├──→ .vscode/settings.json (MCP 配置)
|
|
65
|
-
├──→ .vscode/hooks/lib/ (共享库)
|
|
66
65
|
└──→ .claude/ ★ (Agent Host 双通道,含完整 hooks + settings.json + mcp.json)
|
|
67
66
|
```
|
|
68
67
|
|
|
69
68
|
| 注入场景 | 触发事件 | 注入内容 | 配置 |
|
|
70
69
|
|---|---|---|---|
|
|
71
|
-
| 会话启动 | SessionStart | 模板索引 |
|
|
72
|
-
| 开发触发 | UserPromptSubmit | 13 个模板全文 |
|
|
70
|
+
| 会话启动 | SessionStart | 模板索引 | `.vscode/hooks/session-start.mjs --index` |
|
|
71
|
+
| 开发触发 | UserPromptSubmit | 13 个模板全文 | `.vscode/hooks/preload-templates.mjs --full --top 5 --mark` |
|
|
73
72
|
|
|
74
73
|
---
|
|
75
74
|
|
|
76
75
|
## 路径约定
|
|
77
76
|
|
|
78
|
-
VS Code Copilot hooks
|
|
77
|
+
VS Code Copilot hooks 产物位于 **项目根目录** 的 `.vscode/hooks/`(14 入口 node 产物)。`npx add-coder init --adapter vscode` 会将 TS 源码烘焙分发到此路径;Agent Host(1.129+)同时读取 `.claude/` 双通道。
|
|
79
78
|
|
|
80
|
-
**Issue #6 背景**:本端的 429 并发问题是最初触发源。轮次 1 优先交付 `session-start
|
|
79
|
+
**Issue #6 背景**:本端的 429 并发问题是最初触发源。轮次 1 优先交付 `session-start` + `user-prompt-submit` 两个入口,仅这两个即可消灭模板读取风暴(429 不再触发)。
|
|
81
80
|
|
|
82
81
|
---
|
|
83
82
|
|
|
@@ -85,9 +84,9 @@ VS Code Copilot hooks 必须位于 **项目根目录** 的 `.github/hooks/` 下
|
|
|
85
84
|
|
|
86
85
|
| 维度 | VS Code Copilot | Claude Code |
|
|
87
86
|
|---|---|---|
|
|
88
|
-
| 配置格式 | `.
|
|
89
|
-
| 注入通道 |
|
|
90
|
-
|
|
|
87
|
+
| 配置格式 | `.vscode/hooks/*.mjs`(command 直调) | `.claude/settings.json` |
|
|
88
|
+
| 注入通道 | command stdout | stdout → additionalContext |
|
|
89
|
+
| 产物路径 | `.vscode/hooks/*.mjs`(与 Claude 共享治理层) | `.claude/hooks/*.mjs` |
|
|
91
90
|
| 独有事件 | errorOccurred | PermissionRequest/Denied / StopFailure / Notification / PostToolUseFailure |
|
|
92
91
|
| 子 agent 启动 | ✅ SubagentStart | ✅ SubagentStart |
|
|
93
92
|
| Agent Host 双通道 | ✅ `.claude/` 同步产出 | — |
|
|
@@ -96,7 +95,7 @@ VS Code Copilot hooks 必须位于 **项目根目录** 的 `.github/hooks/` 下
|
|
|
96
95
|
|
|
97
96
|
## 自定义 Hook 源切换
|
|
98
97
|
|
|
99
|
-
VS Code Copilot 的 `.
|
|
98
|
+
VS Code Copilot 的 `.vscode/hooks/*.mjs` 为 VS Code 原生 node 产物(14 入口)。项目同时产出 `.claude/` 目录(含 Claude Code 完整 hook 体系 + settings.json),两套体系可切换。
|
|
100
99
|
|
|
101
100
|
### 两套体系对比
|
|
102
101
|
|
|
@@ -107,21 +106,21 @@ VS Code Copilot 的 `.github/hooks/*.json` 默认指向 `.vscode/hooks/xxx.sh`
|
|
|
107
106
|
| 环境变量 | `$PWD`(VS Code cwd=项目根) | `$CLAUDE_PROJECT_DIR` |
|
|
108
107
|
| 退出码阻断 | ✅ exit 2(与 Claude 相同) | ✅ exit 2 |
|
|
109
108
|
| 上下文注入 | stdout 纯文本(VS Code 格式) | stdout → additionalContext(Claude 格式) |
|
|
110
|
-
|
|
|
109
|
+
| 共享治理层 | ✅ core governance 内联(0 复制) | ✅ core governance 内联(0 复制) |
|
|
111
110
|
| 治理能力 | 完整四路守卫 + 验收阻断 + 审计 | 完整四路守卫 + 验收阻断 + 审计(与 VS Code 版同等) |
|
|
112
111
|
|
|
113
|
-
>
|
|
112
|
+
> **两套实现质量同等,能力完整,差异仅在于环境变量和输出格式。**
|
|
114
113
|
|
|
115
114
|
### 切换方式
|
|
116
115
|
|
|
117
|
-
编辑
|
|
116
|
+
编辑 hook 配置(settings.json / Agent Host 通道),将 `command` 中的路径从 `.vscode/hooks/` 改为 `.claude/hooks/` 即可:
|
|
118
117
|
|
|
119
118
|
```json
|
|
120
119
|
// 默认(VS Code 原生,推荐)
|
|
121
|
-
"
|
|
120
|
+
"command": "node .vscode/hooks/pre-tool-use.mjs"
|
|
122
121
|
|
|
123
|
-
// 切换为 Claude Code 体系(如果同时使用 Claude Code
|
|
124
|
-
"
|
|
122
|
+
// 切换为 Claude Code 体系(如果同时使用 Claude Code 并希望统一产物)
|
|
123
|
+
"command": "node .claude/hooks/pre-tool-use.mjs"
|
|
125
124
|
```
|
|
126
125
|
|
|
127
126
|
**切换场景建议**:
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// audit-bridge.ts — AuditBridge(审计桥接,Task 8.1 事件面扩展,ADD-7 自动化)
|
|
2
|
+
// 设计(Spec §4 嵌入式三层,回流 R1):
|
|
3
|
+
// ① 主路径(现状不动): 拦截事件 writeHookEvent(jsonl) → MCP 常驻 fs.watch 消费 → devOperation 落库
|
|
4
|
+
// ② 事件面扩展(本类): post-tool-use 文件写入 → 写 hook 事件(decision="write")→ 同一 jsonl 主路径
|
|
5
|
+
// → MCP 常驻侧自动消费落库(幂等 dedupKey: hook::ts::planKeyword + decision + cmd)
|
|
6
|
+
// ③ 自然演化: Claude mcp_tool 直调 record_dev_operation 由 handlerTypes 维度声明驱动(TOML 一行切换)
|
|
7
|
+
//
|
|
8
|
+
// 与 writeHookEvent 的关系: 复用其 jsonl 写入 + 轮转逻辑(event.file 真源),
|
|
9
|
+
// 但路径必须用本类 magicDir 构造参数(writeHookEvent 默认 env.MAGIC_DIR || ".qoder"
|
|
10
|
+
// 兜底——core/adapter 入口多数未回写 env.MAGIC_DIR,依赖 env 会错写 .qoder/reports)。
|
|
11
|
+
|
|
12
|
+
import { writeHookEvent } from "./notify.js"
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 审计桥接:post-tool-use 文件写入事件面扩展。
|
|
16
|
+
* emit() 幂等——同文件同秒重复写入由 MCP 消费端 dedupKey 去重,落库唯一。
|
|
17
|
+
*/
|
|
18
|
+
export class AuditBridge {
|
|
19
|
+
private readonly projectDir: string
|
|
20
|
+
private readonly magicDir: string
|
|
21
|
+
|
|
22
|
+
constructor(projectDir: string, magicDir: string) {
|
|
23
|
+
this.projectDir = projectDir
|
|
24
|
+
this.magicDir = magicDir
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 文件写入审计事件(decision="write")。
|
|
29
|
+
* @param filePath 被写入文件路径(绝对/相对均可,MCP 消费端以 cmd 为 targetId)
|
|
30
|
+
*/
|
|
31
|
+
emit(filePath: string): void {
|
|
32
|
+
if (!filePath || filePath === "") return
|
|
33
|
+
const planKeyword = this.extractPlanKeyword(filePath)
|
|
34
|
+
writeHookEvent("post-tool-use", "write", filePath, "文件写入审计(ADD-7 自动化)", planKeyword, "none", "", this.magicDir)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 从写入路径提取 planKeyword(plans/specs/reviews 目录下命名规范文件):
|
|
39
|
+
* xxx-plan-v1.md / xxx-add-route-v1.md / xxx-review-v1.md → xxx-{suffix}-vN
|
|
40
|
+
* 其余路径 → "unknown"(与拦截事件缺省语义一致)。
|
|
41
|
+
*/
|
|
42
|
+
private extractPlanKeyword(filePath: string): string {
|
|
43
|
+
void this.projectDir
|
|
44
|
+
const m = filePath.match(/([^/]+-(?:plan|add-route|review)-v\d+)\.md$/)
|
|
45
|
+
return m ? m[1] : "unknown"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
// lib/common.ts — ADD Hook 协议壳(轮次 1 / Task 1.3)
|
|
2
|
+
// 与 bash 版 lib/common.sh 行为逐字对齐:
|
|
3
|
+
// stdin 解析 / stdout JSON / stderr 文本 / 退出码 0·2 / failClosed
|
|
4
|
+
// 契约红线(Plan §1.2): stdout 仅 JSON、stderr 仅人类可读文本、exit 0/2
|
|
5
|
+
// 烘焙: scripts/hook-bake.ts bundle 为各 magicDir hooks/lib/common.mjs
|
|
6
|
+
// 本文件被各入口 .ts 与 adapter 私有协议层 import(bundle 时内联,产物自包含)
|
|
7
|
+
|
|
8
|
+
import { existsSync, readFileSync, writeFileSync, unlinkSync } from "node:fs"
|
|
9
|
+
import { basename, dirname, join } from "node:path"
|
|
10
|
+
import { fileURLToPath } from "node:url"
|
|
11
|
+
import { spawnSync } from "node:child_process"
|
|
12
|
+
import { createHash } from "node:crypto"
|
|
13
|
+
import { protocol } from "./rules.js"
|
|
14
|
+
import { findUpSync } from "find-up"
|
|
15
|
+
|
|
16
|
+
/** 退出码常量(真源: hook-protocol-rules.toml [protocol.exit_codes]) */
|
|
17
|
+
export const EXIT_PASS = protocol.exit_codes.pass as number
|
|
18
|
+
export const EXIT_BLOCK = protocol.exit_codes.block as number
|
|
19
|
+
|
|
20
|
+
/** state 字段分隔符(真源: hook-protocol-rules.toml [protocol.output.field_separator]) */
|
|
21
|
+
export const STATE_SEP = protocol.output.field_separator as string
|
|
22
|
+
|
|
23
|
+
/** 从 stdin 读取完整 JSON(IDE hook 事件 payload);TTY 时返回 "{}"(对齐 bash parse_input) */
|
|
24
|
+
export function readHookInput(): string {
|
|
25
|
+
if (process.stdin.isTTY) return "{}"
|
|
26
|
+
return readFileSync(0, "utf-8")
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** 从 JSON 提取首个字符串字段值(对齐 bash json_get 的 grep/sed 语义) */
|
|
30
|
+
export function jsonGet(json: string, field: string): string {
|
|
31
|
+
const re = new RegExp(`"${field}"\\s*:\\s*"([^"]*)"`)
|
|
32
|
+
const m = re.exec(json)
|
|
33
|
+
return m?.[1] ?? ""
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** stdout 输出 JSON(机器契约一字不变: additionalContext / hookSpecificOutput / decision) */
|
|
37
|
+
export function emitJson(payload: Record<string, unknown> | string): void {
|
|
38
|
+
process.stdout.write(
|
|
39
|
+
(typeof payload === "string" ? payload : JSON.stringify(payload)) + "\n"
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** stderr 输出文本(Qoder 规范: stderr=人类可读,stdout=机器 JSON) */
|
|
44
|
+
export function emitText(text: string): void {
|
|
45
|
+
process.stderr.write(text + "\n")
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** 放行(退出码 0) */
|
|
49
|
+
export function exitOk(): never {
|
|
50
|
+
process.exit(EXIT_PASS)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** 阻断(退出码 2) */
|
|
54
|
+
export function exitBlock(): never {
|
|
55
|
+
process.exit(EXIT_BLOCK)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* STATUS_UNAVAILABLE 语义: 非零退出 + stderr 说明,禁止静默吞掉。
|
|
60
|
+
* 默认阻断码 2(hook 冒烟契约 ∈ {0,2});bridge 场景可传 3(对齐 bash return 3)。
|
|
61
|
+
*/
|
|
62
|
+
export function failClosed(msg: string, code: number = EXIT_BLOCK): never {
|
|
63
|
+
process.stderr.write(msg + "\n")
|
|
64
|
+
process.exit(code)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* magicDir 统一解析(Plan §3.2 缺陷修复要求 TS 版唯一解析链):
|
|
69
|
+
* ① 入口注入 MAGIC_DIR 优先(sync 烘焙时 bakeMagicRefs 已硬编码)
|
|
70
|
+
* ② 物理位置推导: <projectDir>/<magicDir>/hooks/... 向上找以 . 开头的目录名
|
|
71
|
+
* ③ 推导失败 → failClosed(禁止猜测 adapter 名 / 禁止孤儿变量)
|
|
72
|
+
*/
|
|
73
|
+
export function resolveMagicDir(): string {
|
|
74
|
+
const hit = tryResolveMagicDir()
|
|
75
|
+
if (hit) return hit
|
|
76
|
+
return failClosed(
|
|
77
|
+
`magicDir 未注入且无法从物理位置推导: ${fileURLToPath(import.meta.url)}`
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 非阻断版 magicDir 解析(唯一解析链的柔和变体):
|
|
83
|
+
* 与 resolveMagicDir 同一条链,但推导失败时返回 "" 而非 failClosed——
|
|
84
|
+
* 供 vocabulary 等「文件缺失静默返回空」语义的调用方使用(对齐 bash:禁止猜测 adapter 名)。
|
|
85
|
+
* find-up 语义(2026-08-14 Task 5.1 补齐): 以当前文件为锚点向上找「以 . 开头的目录名」
|
|
86
|
+
* (magicDir 约定形态),替代手写 for 循环——层级变动零漂移(对齐 src/shared/paths.ts 范式)。
|
|
87
|
+
*/
|
|
88
|
+
export function tryResolveMagicDir(): string {
|
|
89
|
+
const injected = process.env.MAGIC_DIR
|
|
90
|
+
if (injected) return injected
|
|
91
|
+
const startDir = dirname(fileURLToPath(import.meta.url))
|
|
92
|
+
// find-up v8 matcher 约定: 返回命中值(string)或 undefined(未命中)
|
|
93
|
+
const hit = findUpSync((dir) => (basename(dir).startsWith(".") ? dir : undefined), {
|
|
94
|
+
cwd: startDir,
|
|
95
|
+
type: "directory",
|
|
96
|
+
})
|
|
97
|
+
return hit ? basename(hit) : ""
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* PROJECT_DIR 统一解析: 注入优先;否则以 magicDir 为锚点向上查找其父目录(项目根)。
|
|
102
|
+
* 失败 → failClosed(不猜测项目路径)。
|
|
103
|
+
* find-up 语义(2026-08-14 Task 5.1 补齐): 替代手写 for 循环,层级零漂移。
|
|
104
|
+
*/
|
|
105
|
+
export function resolveProjectDir(): string {
|
|
106
|
+
const injected = process.env.PROJECT_DIR
|
|
107
|
+
if (injected) return injected
|
|
108
|
+
const magicDir = resolveMagicDir()
|
|
109
|
+
const startDir = dirname(fileURLToPath(import.meta.url))
|
|
110
|
+
const hit = findUpSync((dir) => (basename(dir) === magicDir ? dir : undefined), {
|
|
111
|
+
cwd: startDir,
|
|
112
|
+
type: "directory",
|
|
113
|
+
})
|
|
114
|
+
return hit ? dirname(hit) : failClosed("PROJECT_DIR 未注入且无法从物理位置推导")
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ── ADD 活跃 Plan 检测(对齐 bash common.sh query_plan_status / detect_active_add)──
|
|
118
|
+
//
|
|
119
|
+
// lifecycle 的唯一真相源是 scoped DB。短生命周期 Hook 不常驻 LISTEN;每次触发
|
|
120
|
+
// 都通过当前 adapter 生成态的机器桥(plan-status-bridge)调用 shared resolver。
|
|
121
|
+
// Handoff/add-route 不参与 active 裁决,禁止跨 adapter 或文件系统 fallback。
|
|
122
|
+
|
|
123
|
+
export interface PlanStatusResult {
|
|
124
|
+
stdout: string
|
|
125
|
+
exitCode: number
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 查询活跃 Plan 状态(对齐 bash query_plan_status):
|
|
130
|
+
* - MAGIC_DIR 未注入 → STATUS_UNAVAILABLE(exitCode 3)——对齐 bash:
|
|
131
|
+
* 未注入且无 HOOK_DIR 时直接放弃,不做物理推导(入口 hook 保证注入)
|
|
132
|
+
* - bridge 缺失 → STATUS_UNAVAILABLE(exitCode 3)
|
|
133
|
+
* - 否则 spawn `node --import tsx <magicDir>/scripts/plan-status-bridge.ts`
|
|
134
|
+
*/
|
|
135
|
+
export function queryPlanStatus(): PlanStatusResult {
|
|
136
|
+
const magicDir = process.env.MAGIC_DIR
|
|
137
|
+
if (!magicDir) {
|
|
138
|
+
return {
|
|
139
|
+
stdout: '{"availability":"STATUS_UNAVAILABLE","source":"database","reason":"magicDir 未注入且无法从物理位置推导"}',
|
|
140
|
+
exitCode: 3,
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const bridge = join(
|
|
144
|
+
process.env.PROJECT_DIR || process.cwd(),
|
|
145
|
+
magicDir,
|
|
146
|
+
"scripts",
|
|
147
|
+
"plan-status-bridge.ts"
|
|
148
|
+
)
|
|
149
|
+
if (!existsSync(bridge)) {
|
|
150
|
+
return {
|
|
151
|
+
stdout: '{"availability":"STATUS_UNAVAILABLE","source":"database","reason":"plan-status bridge missing"}',
|
|
152
|
+
exitCode: 3,
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const r = spawnSync("node", ["--import", "tsx", bridge], {
|
|
156
|
+
encoding: "utf-8",
|
|
157
|
+
timeout: 10_000,
|
|
158
|
+
})
|
|
159
|
+
return { stdout: r.stdout ?? "", exitCode: r.status ?? -1 }
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* 活跃 Plan 检测(对齐 bash detect_active_add):
|
|
164
|
+
* - bridge 失败 → "__STATUS_UNAVAILABLE__::<reason>::database::none::none"(调用方 fail-closed)
|
|
165
|
+
* - 非 READY/isActive → null(对齐 bash return 1)
|
|
166
|
+
* - 活跃 → "plan::done/total::approval::none::none"
|
|
167
|
+
*/
|
|
168
|
+
export function detectActiveAdd(): string | null {
|
|
169
|
+
const r = queryPlanStatus()
|
|
170
|
+
if (r.exitCode !== 0) {
|
|
171
|
+
let reason = "database status unavailable"
|
|
172
|
+
try {
|
|
173
|
+
const parsed = JSON.parse(r.stdout) as { reason?: string }
|
|
174
|
+
if (parsed.reason) reason = parsed.reason
|
|
175
|
+
} catch {
|
|
176
|
+
/* 非 JSON 输出 → 默认 reason */
|
|
177
|
+
}
|
|
178
|
+
return `__STATUS_UNAVAILABLE__::${reason}::database::none::none`
|
|
179
|
+
}
|
|
180
|
+
let snapshot: {
|
|
181
|
+
availability?: string
|
|
182
|
+
isActive?: boolean
|
|
183
|
+
planName?: string
|
|
184
|
+
approvalStatus?: string
|
|
185
|
+
progress?: { doneTasks?: number; totalTasks?: number }
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
snapshot = JSON.parse(r.stdout) as typeof snapshot
|
|
189
|
+
} catch {
|
|
190
|
+
return null
|
|
191
|
+
}
|
|
192
|
+
if (!(snapshot.availability === "READY" && snapshot.isActive === true)) return null
|
|
193
|
+
const done = snapshot.progress?.doneTasks ?? 0
|
|
194
|
+
const total = snapshot.progress?.totalTasks ?? 0
|
|
195
|
+
const approval = snapshot.approvalStatus ?? "none"
|
|
196
|
+
return `${snapshot.planName}::${done}/${total}::${approval}::none::none`
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// ── 时间格式(对齐 bash date 工具链)──
|
|
200
|
+
|
|
201
|
+
/** 对齐 bash `date -Iseconds`:本地时间 + 时区偏移(如 2026-08-14T11:15:41+08:00) */
|
|
202
|
+
export function localIsoSeconds(): string {
|
|
203
|
+
const d = new Date()
|
|
204
|
+
const pad = (n: number) => String(n).padStart(2, "0")
|
|
205
|
+
const offsetMin = -d.getTimezoneOffset()
|
|
206
|
+
const sign = offsetMin >= 0 ? "+" : "-"
|
|
207
|
+
const offAbs = Math.abs(offsetMin)
|
|
208
|
+
const off = `${sign}${pad(Math.floor(offAbs / 60))}:${pad(offAbs % 60)}`
|
|
209
|
+
return (
|
|
210
|
+
`${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}` +
|
|
211
|
+
`T${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}${off}`
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ── Dev Action 追踪(对齐 bash common.sh 标记文件管理)──
|
|
216
|
+
|
|
217
|
+
/** 项目 hash(对齐 bash echo "${PROJECT_DIR:-$PWD}" | md5sum | cut -c1-8:echo 含换行)——标记文件命名的唯一算法 */
|
|
218
|
+
export function projectHash(): string {
|
|
219
|
+
try {
|
|
220
|
+
// bash echo 输出带 \n,md5 对象为 dir + 换行
|
|
221
|
+
return createHash("md5")
|
|
222
|
+
.update(`${process.env.PROJECT_DIR || process.cwd()}\n`)
|
|
223
|
+
.digest("hex")
|
|
224
|
+
.slice(0, 8)
|
|
225
|
+
} catch {
|
|
226
|
+
return "default"
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const DEV_FLAG = `/tmp/add_dev_${projectHash()}`
|
|
231
|
+
|
|
232
|
+
export function markDevAction(): void {
|
|
233
|
+
try {
|
|
234
|
+
writeFileSync(DEV_FLAG, "")
|
|
235
|
+
} catch {
|
|
236
|
+
/* ignore */
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export function hasDevAction(): boolean {
|
|
241
|
+
return existsSync(DEV_FLAG)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export function clearDevAction(): void {
|
|
245
|
+
try {
|
|
246
|
+
unlinkSync(DEV_FLAG)
|
|
247
|
+
} catch {
|
|
248
|
+
/* ignore */
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ── 验收完整度检查(对齐 bash check_add_completeness)──
|
|
253
|
+
|
|
254
|
+
/** 检查 handoff + add-route 的验收完整度,返回问题列表(无问题 → 空数组) */
|
|
255
|
+
export function checkAddCompleteness(handoff: string, addRoute: string): string[] {
|
|
256
|
+
const issues: string[] = []
|
|
257
|
+
|
|
258
|
+
// devlog(内容已回流至 handoff,检查 handoff 是否含验收结果)
|
|
259
|
+
if (handoff && existsSync(handoff)) {
|
|
260
|
+
const content = readFileSync(handoff, "utf-8")
|
|
261
|
+
if (!/验收|收敛|闭环|本轮改了什么|devlog/.test(content)) {
|
|
262
|
+
issues.push(" [ ] devlog 缺失(handoff 无验收记录)")
|
|
263
|
+
}
|
|
264
|
+
const unchecked = (content.match(/\[ \]/g) || []).length
|
|
265
|
+
if (unchecked > 0) {
|
|
266
|
+
issues.push(` [ ] handoff ${unchecked} 项未勾选`)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// add-route Step
|
|
271
|
+
if (addRoute && existsSync(addRoute)) {
|
|
272
|
+
const content = readFileSync(addRoute, "utf-8")
|
|
273
|
+
const unchecked = (content.match(/\[ \]/g) || []).length
|
|
274
|
+
if (unchecked > 0) {
|
|
275
|
+
issues.push(` [ ] add-route ${unchecked} Step 未闭环`)
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return issues
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* 验收幂等保护(对齐 bash is_already_accepted):
|
|
283
|
+
* add-route Step 8 区段含 "[x].*验证并更新项目状态" 且 handoff 含验收字样 → 已验收。
|
|
284
|
+
*/
|
|
285
|
+
export function isAlreadyAccepted(addRoute: string, handoff: string): boolean {
|
|
286
|
+
if (addRoute && existsSync(addRoute)) {
|
|
287
|
+
const content = readFileSync(addRoute, "utf-8")
|
|
288
|
+
const step8 = content.match(/Step 8[\s\S]{0,2000}/)?.[0] ?? ""
|
|
289
|
+
if (/\[x\].*验证并更新项目状态/.test(step8)) {
|
|
290
|
+
if (handoff && existsSync(handoff)) {
|
|
291
|
+
const h = readFileSync(handoff, "utf-8")
|
|
292
|
+
if (/✅.*验收|收敛|全部闭环|全部.*完成/.test(h)) {
|
|
293
|
+
return true
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return false
|
|
299
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// context-inject.ts — 上下文注入模板(bash 版 context-inject.sh 的 TS 同语义实现)
|
|
2
|
+
// 共享库: SessionStart JSON 注入 + Stop 七象限分流 few-shot + 写操作前置守卫
|
|
3
|
+
// 规则真源: hook-context-rules.toml([context.quadrants] / [context.pretool])
|
|
4
|
+
// consumed 语义(Task 2.3 死代码象限归档): true = 有入口消费;false = 死代码象限
|
|
5
|
+
// (2026-08-14 实态核验: 全端 stop-check 仅消费 no_add_has_dev + has_add_dev_unclosed)
|
|
6
|
+
|
|
7
|
+
import { context } from "./rules.js"
|
|
8
|
+
|
|
9
|
+
/** 象限条目(rules.context.quadrants 结构) */
|
|
10
|
+
interface Quadrant {
|
|
11
|
+
id: string
|
|
12
|
+
consumed: boolean
|
|
13
|
+
text: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** 结构化 SessionStart JSON 注入(与 bash build_session_start_json 逐字对齐) */
|
|
17
|
+
export function buildSessionStartJson(
|
|
18
|
+
plan: string,
|
|
19
|
+
step: string,
|
|
20
|
+
round: string,
|
|
21
|
+
handoff: string
|
|
22
|
+
): string {
|
|
23
|
+
return `{
|
|
24
|
+
"continue": true,
|
|
25
|
+
"hookSpecificOutput": {
|
|
26
|
+
"hookEventName": "SessionStart",
|
|
27
|
+
"additionalContext": "上次 ADD 流程未完成:\\n Plan: ${plan}\\n 轮次: ${round}\\n 当前 Step: ${step} (add-route)\\n 恢复命令: query_audit_logs({ planKeyword: '${plan}' })\\n handoff: ${handoff}"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
`
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** 象限文本索引(真源: hook-context-rules.toml [context.quadrants])
|
|
34
|
+
* Task 9.4.3 裁剪: 仅加载 consumed=true 象限(no_add_has_dev / has_add_dev_unclosed)——
|
|
35
|
+
* 死代码象限(consumed=false ×5)从运行索引移除(不可达),文本保留 TOML 真源待归档。 */
|
|
36
|
+
const QUADRANTS: Map<string, Quadrant> = new Map(
|
|
37
|
+
(((context.quadrants as unknown as Quadrant[]) ?? [])
|
|
38
|
+
.filter((q) => q.consumed === true)
|
|
39
|
+
.map((q) => [q.id, q]) as Array<[string, Quadrant]>)
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
/** Stop 七象限分流 few-shot 上下文(文本真源 = TOML;{{info}} 占位符 replace 插值) */
|
|
43
|
+
export function buildStopContext(quadrant: string, info: string): string {
|
|
44
|
+
const q = QUADRANTS.get(quadrant)
|
|
45
|
+
if (!q) return ""
|
|
46
|
+
return q.text.replaceAll("{{info}}", info)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** 写操作前置守卫上下文(真源: hook-context-rules.toml [context.pretool]) */
|
|
50
|
+
export function buildPretoolContext(plan: string, round: string): string {
|
|
51
|
+
const tpl = (context.pretool as { text: string }).text
|
|
52
|
+
return tpl.replaceAll("{{plan}}", plan).replaceAll("{{round}}", round)
|
|
53
|
+
}
|