add-coder 0.3.31 → 0.3.32

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.
Files changed (74) hide show
  1. package/package.json +1 -1
  2. package/templates/.add-coder-src-hash.json +73 -1
  3. package/templates/adapters/claude/hooks/doc-format-guard.mjs +850 -0
  4. package/templates/adapters/claude/hooks/notification.mjs +256 -0
  5. package/templates/adapters/claude/hooks/permission-denied.mjs +167 -0
  6. package/templates/adapters/claude/hooks/permission-gate.mjs +150 -0
  7. package/templates/adapters/claude/hooks/post-tool-failure.mjs +3 -0
  8. package/templates/adapters/claude/hooks/post-tool-use.mjs +413 -0
  9. package/templates/adapters/claude/hooks/pre-compact.mjs +226 -0
  10. package/templates/adapters/claude/hooks/pre-tool-use.mjs +914 -0
  11. package/templates/adapters/claude/hooks/prompt-submit.mjs +847 -0
  12. package/templates/adapters/claude/hooks/review-checklist.mjs +3 -0
  13. package/templates/adapters/claude/hooks/session-end.mjs +287 -0
  14. package/templates/adapters/claude/hooks/session-start.mjs +566 -0
  15. package/templates/adapters/claude/hooks/stop-check.mjs +505 -0
  16. package/templates/adapters/claude/hooks/stop-failure.mjs +222 -0
  17. package/templates/adapters/claude/hooks/subagent-guard.mjs +224 -0
  18. package/templates/adapters/claude/hooks/subagent-stop.mjs +277 -0
  19. package/templates/adapters/codex/hooks/doc-format-guard.mjs +849 -0
  20. package/templates/adapters/codex/hooks/notification.mjs +254 -0
  21. package/templates/adapters/codex/hooks/permission-gate.mjs +169 -0
  22. package/templates/adapters/codex/hooks/post-tool-failure.mjs +176 -0
  23. package/templates/adapters/codex/hooks/post-tool-use.mjs +575 -0
  24. package/templates/adapters/codex/hooks/pre-compact.mjs +328 -0
  25. package/templates/adapters/codex/hooks/pre-tool-use.mjs +963 -0
  26. package/templates/adapters/codex/hooks/prompt-submit.mjs +623 -0
  27. package/templates/adapters/codex/hooks/review-checklist.mjs +3 -0
  28. package/templates/adapters/codex/hooks/session-end.mjs +389 -0
  29. package/templates/adapters/codex/hooks/session-start.mjs +583 -0
  30. package/templates/adapters/codex/hooks/stop-check.mjs +539 -0
  31. package/templates/adapters/codex/hooks/subagent-guard.mjs +218 -0
  32. package/templates/adapters/codex/hooks/subagent-stop.mjs +381 -0
  33. package/templates/adapters/qoder/hooks/doc-format-guard.mjs +858 -0
  34. package/templates/adapters/qoder/hooks/notification.mjs +269 -0
  35. package/templates/adapters/qoder/hooks/permission-gate.mjs +284 -0
  36. package/templates/adapters/qoder/hooks/post-tool-failure.mjs +294 -0
  37. package/templates/adapters/qoder/hooks/post-tool-use.mjs +535 -0
  38. package/templates/adapters/qoder/hooks/pre-compact.mjs +234 -0
  39. package/templates/adapters/qoder/hooks/pre-tool-use.mjs +948 -0
  40. package/templates/adapters/qoder/hooks/prompt-submit.mjs +789 -0
  41. package/templates/adapters/qoder/hooks/review-checklist.mjs +409 -0
  42. package/templates/adapters/qoder/hooks/session-end.mjs +301 -0
  43. package/templates/adapters/qoder/hooks/session-start.mjs +619 -0
  44. package/templates/adapters/qoder/hooks/stop-check.mjs +567 -0
  45. package/templates/adapters/qoder/hooks/subagent-guard.mjs +219 -0
  46. package/templates/adapters/qoder/hooks/subagent-stop.mjs +298 -0
  47. package/templates/adapters/trae/hooks/doc-format-guard.mjs +849 -0
  48. package/templates/adapters/trae/hooks/notification.mjs +251 -0
  49. package/templates/adapters/trae/hooks/permission-gate.mjs +169 -0
  50. package/templates/adapters/trae/hooks/post-tool-failure.mjs +176 -0
  51. package/templates/adapters/trae/hooks/post-tool-use.mjs +415 -0
  52. package/templates/adapters/trae/hooks/pre-compact.mjs +224 -0
  53. package/templates/adapters/trae/hooks/pre-tool-use.mjs +873 -0
  54. package/templates/adapters/trae/hooks/prompt-submit.mjs +614 -0
  55. package/templates/adapters/trae/hooks/review-checklist.mjs +3 -0
  56. package/templates/adapters/trae/hooks/session-end.mjs +285 -0
  57. package/templates/adapters/trae/hooks/session-start.mjs +555 -0
  58. package/templates/adapters/trae/hooks/stop-check.mjs +502 -0
  59. package/templates/adapters/trae/hooks/subagent-guard.mjs +218 -0
  60. package/templates/adapters/trae/hooks/subagent-stop.mjs +274 -0
  61. package/templates/adapters/vscode/hooks/doc-format-guard.mjs +847 -0
  62. package/templates/adapters/vscode/hooks/notification.mjs +251 -0
  63. package/templates/adapters/vscode/hooks/permission-gate.mjs +169 -0
  64. package/templates/adapters/vscode/hooks/post-tool-failure.mjs +192 -0
  65. package/templates/adapters/vscode/hooks/post-tool-use.mjs +415 -0
  66. package/templates/adapters/vscode/hooks/pre-compact.mjs +224 -0
  67. package/templates/adapters/vscode/hooks/pre-tool-use.mjs +906 -0
  68. package/templates/adapters/vscode/hooks/prompt-submit.mjs +838 -0
  69. package/templates/adapters/vscode/hooks/review-checklist.mjs +3 -0
  70. package/templates/adapters/vscode/hooks/session-end.mjs +285 -0
  71. package/templates/adapters/vscode/hooks/session-start.mjs +560 -0
  72. package/templates/adapters/vscode/hooks/stop-check.mjs +502 -0
  73. package/templates/adapters/vscode/hooks/subagent-guard.mjs +218 -0
  74. package/templates/adapters/vscode/hooks/subagent-stop.mjs +274 -0
@@ -0,0 +1,218 @@
1
+ // templates/core/governance/common.ts
2
+ import { existsSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
3
+ import { basename, dirname, join } from "node:path";
4
+ import { spawnSync } from "node:child_process";
5
+ import { createHash } from "node:crypto";
6
+
7
+ // templates/core/governance/rules.ts
8
+ var protocol = {
9
+ "exit_codes": {
10
+ "pass": 0,
11
+ "block": 2,
12
+ "note": "\u5192\u70DF\u6821\u9A8C: \u4EA7\u7269\u9000\u51FA\u7801 \u2208 {0,2}\uFF08\u5176\u4F59\u4E3A\u975E\u9884\u671F\uFF0C\u9700\u4FEE\u590D\uFF09"
13
+ },
14
+ "output": {
15
+ "stdout_json_only": true,
16
+ "stderr_human_text": true,
17
+ "field_separator": "::",
18
+ "magic_dir_resolution": "\u6CE8\u5165\u4F18\u5148 \u2192 \u7269\u7406\u4F4D\u7F6E\u63A8\u5BFC \u2192 failClosed\uFF08\u7981\u6B62\u731C\u6D4B adapter \u540D\uFF09"
19
+ },
20
+ "adapters": {
21
+ "claude": {
22
+ "stdout_form": "plain-text",
23
+ "project_dir_env": "CLAUDE_PROJECT_DIR",
24
+ "magic_dir": ".claude",
25
+ "handlerTypes": [
26
+ "command",
27
+ "mcp_tool"
28
+ ]
29
+ },
30
+ "qoder": {
31
+ "stdout_form": "json",
32
+ "project_dir_env": "QODER_PROJECT_DIR",
33
+ "magic_dir": ".qoder",
34
+ "handlerTypes": [
35
+ "command",
36
+ "http"
37
+ ]
38
+ },
39
+ "codex": {
40
+ "stdout_form": "systemMessage",
41
+ "project_dir_env": "git-toplevel",
42
+ "magic_dir": ".codex",
43
+ "handlerTypes": [
44
+ "command"
45
+ ]
46
+ },
47
+ "vscode": {
48
+ "stdout_form": "plain-text",
49
+ "project_dir_env": "PWD",
50
+ "magic_dir": ".vscode",
51
+ "handlerTypes": [
52
+ "command"
53
+ ]
54
+ },
55
+ "trae": {
56
+ "stdout_form": "plain-text",
57
+ "project_dir_env": "PWD",
58
+ "magic_dir": ".trae",
59
+ "handlerTypes": [
60
+ "command"
61
+ ]
62
+ }
63
+ },
64
+ "event_outputs": {
65
+ "qoder": {
66
+ "SessionStart": "additionalContext",
67
+ "UserPromptSubmit": "additionalContext",
68
+ "PreToolUse": "permissionDecision",
69
+ "Stop": "additionalContext",
70
+ "PostToolUse": "feedback",
71
+ "SubagentStart": "additionalContext",
72
+ "SubagentStop": "additionalContext",
73
+ "PostToolUseFailure": "text",
74
+ "PermissionRequest": "text",
75
+ "SessionEnd": "text",
76
+ "PreCompact": "text",
77
+ "Notification": "text"
78
+ },
79
+ "claude": {
80
+ "SessionStart": "additionalContext",
81
+ "UserPromptSubmit": "additionalContext",
82
+ "PreToolUse": "permissionDecision",
83
+ "Stop": "text",
84
+ "PostToolUse": "feedback",
85
+ "SubagentStart": "text",
86
+ "SubagentStop": "text",
87
+ "PostToolUseFailure": "text",
88
+ "PermissionRequest": "text",
89
+ "SessionEnd": "text",
90
+ "PreCompact": "text",
91
+ "Notification": "text"
92
+ },
93
+ "codex": {
94
+ "Stop": "systemMessage",
95
+ "PostToolUse": "text"
96
+ },
97
+ "vscode": {
98
+ "PostToolUse": "text"
99
+ },
100
+ "trae": {
101
+ "PostToolUse": "text"
102
+ }
103
+ },
104
+ "core": {
105
+ "stdout_form": "json",
106
+ "magic_dir": ".add",
107
+ "note": "core \u5165\u53E3\u534F\u8BAE = qoder \u540C\u6784\u53C2\u8003\u5B9E\u73B0\uFF1Badapter \u4EC5\u4FDD\u7559\u672C\u8868\u58F0\u660E\u7684\u79C1\u6709\u5DEE\u5F02"
108
+ },
109
+ "adapter_defaults": {
110
+ "magic_dir_fallback": ".qoder",
111
+ "probe_magic_dirs": [
112
+ ".claude",
113
+ ".qoder",
114
+ ".vscode",
115
+ ".add",
116
+ ".trae",
117
+ ".codex"
118
+ ]
119
+ }
120
+ };
121
+
122
+ // templates/core/governance/common.ts
123
+ var EXIT_PASS = protocol.exit_codes.pass;
124
+ var EXIT_BLOCK = protocol.exit_codes.block;
125
+ var STATE_SEP = protocol.output.field_separator;
126
+ function queryPlanStatus() {
127
+ const magicDir = process.env.MAGIC_DIR;
128
+ if (!magicDir) {
129
+ return {
130
+ stdout: '{"availability":"STATUS_UNAVAILABLE","source":"database","reason":"magicDir \u672A\u6CE8\u5165\u4E14\u65E0\u6CD5\u4ECE\u7269\u7406\u4F4D\u7F6E\u63A8\u5BFC"}',
131
+ exitCode: 3
132
+ };
133
+ }
134
+ const bridge = join(
135
+ process.env.PROJECT_DIR || process.cwd(),
136
+ magicDir,
137
+ "scripts",
138
+ "plan-status-bridge.ts"
139
+ );
140
+ if (!existsSync(bridge)) {
141
+ return {
142
+ stdout: '{"availability":"STATUS_UNAVAILABLE","source":"database","reason":"plan-status bridge missing"}',
143
+ exitCode: 3
144
+ };
145
+ }
146
+ const r = spawnSync("node", ["--import", "tsx", bridge], {
147
+ encoding: "utf-8",
148
+ timeout: 1e4
149
+ });
150
+ return { stdout: r.stdout ?? "", exitCode: r.status ?? -1 };
151
+ }
152
+ function detectActiveAdd() {
153
+ const r = queryPlanStatus();
154
+ if (r.exitCode !== 0) {
155
+ let reason = "database status unavailable";
156
+ try {
157
+ const parsed = JSON.parse(r.stdout);
158
+ if (parsed.reason) reason = parsed.reason;
159
+ } catch {
160
+ }
161
+ return `__STATUS_UNAVAILABLE__::${reason}::database::none::none`;
162
+ }
163
+ let snapshot;
164
+ try {
165
+ snapshot = JSON.parse(r.stdout);
166
+ } catch {
167
+ return null;
168
+ }
169
+ if (!(snapshot.availability === "READY" && snapshot.isActive === true)) return null;
170
+ const done = snapshot.progress?.doneTasks ?? 0;
171
+ const total = snapshot.progress?.totalTasks ?? 0;
172
+ const approval = snapshot.approvalStatus ?? "none";
173
+ return `${snapshot.planName}::${done}/${total}::${approval}::none::none`;
174
+ }
175
+ function projectHash() {
176
+ try {
177
+ return createHash("md5").update(`${process.env.PROJECT_DIR || process.cwd()}
178
+ `).digest("hex").slice(0, 8);
179
+ } catch {
180
+ return "default";
181
+ }
182
+ }
183
+ var DEV_FLAG = `/tmp/add_dev_${projectHash()}`;
184
+
185
+ // templates/core/governance/subagent-guard-router.ts
186
+ var SubagentGuardRouter = class {
187
+ /** 主路由:返回 exit code(0) */
188
+ run() {
189
+ const state = detectActiveAdd();
190
+ if (state === null) return 0;
191
+ this.emit(state);
192
+ return 0;
193
+ }
194
+ /** 上下文注入输出(core 协议: hookSpecificOutput JSON,qoder 同构) */
195
+ emit(state) {
196
+ const [plan, step, rounds, handoff] = state.split("::");
197
+ const ctx = `[ADD SubagentStart] Plan: ${plan}, Step: ${step}, Round: ${rounds}, handoff: ${handoff}\u3002\u9075\u5FAA ADD \u89C4\u8303\uFF0C\u5B8C\u6210\u540E\u68C0\u67E5 checklist\u3002`;
198
+ process.stdout.write(
199
+ JSON.stringify({ hookSpecificOutput: { hookEventName: "SubagentStart", additionalContext: ctx } }) + "\n"
200
+ );
201
+ }
202
+ };
203
+
204
+ // templates/adapters/trae/hooks/subagent-guard.ts
205
+ var TraeSubagentGuardRouter = class extends SubagentGuardRouter {
206
+ /** ① 纯文本状态块(trae 协议,与 claude 同构) */
207
+ emit(state) {
208
+ const [plan, step, rounds, handoff] = state.split("::");
209
+ process.stdout.write(`[ADD SubagentStart] \u5B50\u4EE3\u7406\u4E0A\u4E0B\u6587\u6CE8\u5165:
210
+ Plan: ${plan}
211
+ Step: ${step}
212
+ \u8F6E\u6B21: ${rounds}
213
+ handoff: ${handoff}
214
+ \u9075\u5FAA ADD \u89C4\u8303\uFF0C\u5B8C\u6210\u540E\u8BF7\u68C0\u67E5 checklist \u5BF9\u5E94\u9879
215
+ `);
216
+ }
217
+ };
218
+ process.exit(new TraeSubagentGuardRouter().run());
@@ -0,0 +1,274 @@
1
+ // templates/core/governance/common.ts
2
+ import { existsSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
3
+ import { basename, dirname, join } from "node:path";
4
+ import { spawnSync } from "node:child_process";
5
+ import { createHash } from "node:crypto";
6
+
7
+ // templates/core/governance/rules.ts
8
+ var protocol = {
9
+ "exit_codes": {
10
+ "pass": 0,
11
+ "block": 2,
12
+ "note": "\u5192\u70DF\u6821\u9A8C: \u4EA7\u7269\u9000\u51FA\u7801 \u2208 {0,2}\uFF08\u5176\u4F59\u4E3A\u975E\u9884\u671F\uFF0C\u9700\u4FEE\u590D\uFF09"
13
+ },
14
+ "output": {
15
+ "stdout_json_only": true,
16
+ "stderr_human_text": true,
17
+ "field_separator": "::",
18
+ "magic_dir_resolution": "\u6CE8\u5165\u4F18\u5148 \u2192 \u7269\u7406\u4F4D\u7F6E\u63A8\u5BFC \u2192 failClosed\uFF08\u7981\u6B62\u731C\u6D4B adapter \u540D\uFF09"
19
+ },
20
+ "adapters": {
21
+ "claude": {
22
+ "stdout_form": "plain-text",
23
+ "project_dir_env": "CLAUDE_PROJECT_DIR",
24
+ "magic_dir": ".claude",
25
+ "handlerTypes": [
26
+ "command",
27
+ "mcp_tool"
28
+ ]
29
+ },
30
+ "qoder": {
31
+ "stdout_form": "json",
32
+ "project_dir_env": "QODER_PROJECT_DIR",
33
+ "magic_dir": ".qoder",
34
+ "handlerTypes": [
35
+ "command",
36
+ "http"
37
+ ]
38
+ },
39
+ "codex": {
40
+ "stdout_form": "systemMessage",
41
+ "project_dir_env": "git-toplevel",
42
+ "magic_dir": ".codex",
43
+ "handlerTypes": [
44
+ "command"
45
+ ]
46
+ },
47
+ "vscode": {
48
+ "stdout_form": "plain-text",
49
+ "project_dir_env": "PWD",
50
+ "magic_dir": ".vscode",
51
+ "handlerTypes": [
52
+ "command"
53
+ ]
54
+ },
55
+ "trae": {
56
+ "stdout_form": "plain-text",
57
+ "project_dir_env": "PWD",
58
+ "magic_dir": ".trae",
59
+ "handlerTypes": [
60
+ "command"
61
+ ]
62
+ }
63
+ },
64
+ "event_outputs": {
65
+ "qoder": {
66
+ "SessionStart": "additionalContext",
67
+ "UserPromptSubmit": "additionalContext",
68
+ "PreToolUse": "permissionDecision",
69
+ "Stop": "additionalContext",
70
+ "PostToolUse": "feedback",
71
+ "SubagentStart": "additionalContext",
72
+ "SubagentStop": "additionalContext",
73
+ "PostToolUseFailure": "text",
74
+ "PermissionRequest": "text",
75
+ "SessionEnd": "text",
76
+ "PreCompact": "text",
77
+ "Notification": "text"
78
+ },
79
+ "claude": {
80
+ "SessionStart": "additionalContext",
81
+ "UserPromptSubmit": "additionalContext",
82
+ "PreToolUse": "permissionDecision",
83
+ "Stop": "text",
84
+ "PostToolUse": "feedback",
85
+ "SubagentStart": "text",
86
+ "SubagentStop": "text",
87
+ "PostToolUseFailure": "text",
88
+ "PermissionRequest": "text",
89
+ "SessionEnd": "text",
90
+ "PreCompact": "text",
91
+ "Notification": "text"
92
+ },
93
+ "codex": {
94
+ "Stop": "systemMessage",
95
+ "PostToolUse": "text"
96
+ },
97
+ "vscode": {
98
+ "PostToolUse": "text"
99
+ },
100
+ "trae": {
101
+ "PostToolUse": "text"
102
+ }
103
+ },
104
+ "core": {
105
+ "stdout_form": "json",
106
+ "magic_dir": ".add",
107
+ "note": "core \u5165\u53E3\u534F\u8BAE = qoder \u540C\u6784\u53C2\u8003\u5B9E\u73B0\uFF1Badapter \u4EC5\u4FDD\u7559\u672C\u8868\u58F0\u660E\u7684\u79C1\u6709\u5DEE\u5F02"
108
+ },
109
+ "adapter_defaults": {
110
+ "magic_dir_fallback": ".qoder",
111
+ "probe_magic_dirs": [
112
+ ".claude",
113
+ ".qoder",
114
+ ".vscode",
115
+ ".add",
116
+ ".trae",
117
+ ".codex"
118
+ ]
119
+ }
120
+ };
121
+
122
+ // templates/core/governance/common.ts
123
+ var EXIT_PASS = protocol.exit_codes.pass;
124
+ var EXIT_BLOCK = protocol.exit_codes.block;
125
+ var STATE_SEP = protocol.output.field_separator;
126
+ function readHookInput() {
127
+ if (process.stdin.isTTY) return "{}";
128
+ return readFileSync(0, "utf-8");
129
+ }
130
+ function queryPlanStatus() {
131
+ const magicDir = process.env.MAGIC_DIR;
132
+ if (!magicDir) {
133
+ return {
134
+ stdout: '{"availability":"STATUS_UNAVAILABLE","source":"database","reason":"magicDir \u672A\u6CE8\u5165\u4E14\u65E0\u6CD5\u4ECE\u7269\u7406\u4F4D\u7F6E\u63A8\u5BFC"}',
135
+ exitCode: 3
136
+ };
137
+ }
138
+ const bridge = join(
139
+ process.env.PROJECT_DIR || process.cwd(),
140
+ magicDir,
141
+ "scripts",
142
+ "plan-status-bridge.ts"
143
+ );
144
+ if (!existsSync(bridge)) {
145
+ return {
146
+ stdout: '{"availability":"STATUS_UNAVAILABLE","source":"database","reason":"plan-status bridge missing"}',
147
+ exitCode: 3
148
+ };
149
+ }
150
+ const r = spawnSync("node", ["--import", "tsx", bridge], {
151
+ encoding: "utf-8",
152
+ timeout: 1e4
153
+ });
154
+ return { stdout: r.stdout ?? "", exitCode: r.status ?? -1 };
155
+ }
156
+ function detectActiveAdd() {
157
+ const r = queryPlanStatus();
158
+ if (r.exitCode !== 0) {
159
+ let reason = "database status unavailable";
160
+ try {
161
+ const parsed = JSON.parse(r.stdout);
162
+ if (parsed.reason) reason = parsed.reason;
163
+ } catch {
164
+ }
165
+ return `__STATUS_UNAVAILABLE__::${reason}::database::none::none`;
166
+ }
167
+ let snapshot;
168
+ try {
169
+ snapshot = JSON.parse(r.stdout);
170
+ } catch {
171
+ return null;
172
+ }
173
+ if (!(snapshot.availability === "READY" && snapshot.isActive === true)) return null;
174
+ const done = snapshot.progress?.doneTasks ?? 0;
175
+ const total = snapshot.progress?.totalTasks ?? 0;
176
+ const approval = snapshot.approvalStatus ?? "none";
177
+ return `${snapshot.planName}::${done}/${total}::${approval}::none::none`;
178
+ }
179
+ function localIsoSeconds() {
180
+ const d = /* @__PURE__ */ new Date();
181
+ const pad = (n) => String(n).padStart(2, "0");
182
+ const offsetMin = -d.getTimezoneOffset();
183
+ const sign = offsetMin >= 0 ? "+" : "-";
184
+ const offAbs = Math.abs(offsetMin);
185
+ const off = `${sign}${pad(Math.floor(offAbs / 60))}:${pad(offAbs % 60)}`;
186
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}${off}`;
187
+ }
188
+ function projectHash() {
189
+ try {
190
+ return createHash("md5").update(`${process.env.PROJECT_DIR || process.cwd()}
191
+ `).digest("hex").slice(0, 8);
192
+ } catch {
193
+ return "default";
194
+ }
195
+ }
196
+ var DEV_FLAG = `/tmp/add_dev_${projectHash()}`;
197
+
198
+ // templates/core/governance/subagent-stop-router.ts
199
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
200
+ var SubagentStopRouter = class {
201
+ /** 对齐 bash jq -r '.agent_type // .subagent_name // "unknown"':
202
+ * 空输入/非法 JSON → jq 无输出 → 空串(不是 "unknown");字段缺失才回退 "unknown" */
203
+ agentNameFrom(input2) {
204
+ if (input2.trim() === "") return "";
205
+ try {
206
+ const parsed = JSON.parse(input2);
207
+ const v = parsed.agent_type ?? parsed.subagent_name;
208
+ return typeof v === "string" && v !== "" ? v : "unknown";
209
+ } catch {
210
+ return "";
211
+ }
212
+ }
213
+ // ─────────────────────────── 扩展点 ───────────────────────────
214
+ /** ① 无活跃 Plan 告警(core: stderr;qoder 子类 override: stdout JSON) */
215
+ emitNoPlanWarn(agentName) {
216
+ process.stderr.write(`[ADD SubagentStop] \u26A0\uFE0F ${agentName} \u5DF2\u5B8C\u6210\uFF0C\u4F46\u65E0\u6D3B\u8DC3 ADD Plan \u65E0\u6CD5\u6821\u9A8C\u8FB9\u754C
217
+ `);
218
+ }
219
+ /** ② 边界通过审计(core: stderr;qoder 子类 override: stdout JSON) */
220
+ emitBoundaryPass(agentName, planKw, _handoff) {
221
+ process.stderr.write(`[ADD SubagentStop] ${agentName} \u5DF2\u5B8C\u6210 \u2014 \u5173\u8054 Plan: ${planKw}
222
+ `);
223
+ process.stderr.write(`[ADD SubagentStop] ${agentName} \u8FB9\u754C\u6821\u9A8C\u901A\u8FC7 \u2014 ${localIsoSeconds()}
224
+ `);
225
+ }
226
+ /** 交付物越界检查开关(core: true;qoder: false——qoder 轻量提示无阻断) */
227
+ checkDeliverables() {
228
+ return true;
229
+ }
230
+ /** 主路由:返回 exit code(0 放行 / 2 阻断) */
231
+ run(input2) {
232
+ const agentName = this.agentNameFrom(input2);
233
+ const state = detectActiveAdd();
234
+ if (state === null) {
235
+ this.emitNoPlanWarn(agentName);
236
+ return 0;
237
+ }
238
+ const planKw = state.split("::")[0] ?? "";
239
+ const handoff = state.split("::")[3] ?? "";
240
+ if (this.checkDeliverables() && handoff && handoff !== "none" && existsSync2(handoff)) {
241
+ const deliverablesRaw = (() => {
242
+ try {
243
+ const parsed = JSON.parse(input2);
244
+ return typeof parsed.deliverables === "string" ? parsed.deliverables : "";
245
+ } catch {
246
+ return "";
247
+ }
248
+ })();
249
+ if (deliverablesRaw !== "") {
250
+ const handoffContent = readFileSync2(handoff, "utf-8");
251
+ const violations = [];
252
+ for (const f of deliverablesRaw.split(/\s+/)) {
253
+ if (f === "") continue;
254
+ if (!handoffContent.includes(f)) violations.push(f);
255
+ }
256
+ if (violations.length > 0) {
257
+ process.stderr.write(`[ADD SubagentStop] \u274C ${agentName} \u4EA4\u4ED8\u7269\u8D85\u51FA spec \u8FB9\u754C: ${violations.join(" ")}
258
+ `);
259
+ process.stderr.write("[ADD SubagentStop] \u8981\u6C42\u91CD\u505A\u2014\u2014\u8BF7\u68C0\u67E5\u8FD9\u4E9B\u6587\u4EF6\u662F\u5426\u5C5E\u4E8E\u672C\u8F6E spec \u8303\u56F4\n");
260
+ return EXIT_BLOCK;
261
+ }
262
+ }
263
+ }
264
+ this.emitBoundaryPass(agentName, planKw, handoff);
265
+ return 0;
266
+ }
267
+ };
268
+
269
+ // templates/adapters/trae/hooks/subagent-stop.ts
270
+ var TraeSubagentStopRouter = class extends SubagentStopRouter {
271
+ // 当前无 override(边界校验 + 审计聚合与 core 基线一致);命名子类承载端身份 + 未来演进位
272
+ };
273
+ var input = readHookInput();
274
+ process.exit(new TraeSubagentStopRouter().run(input));