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/codex/hooks/subagent-guard.ts
205
+ var CodexSubagentGuardRouter = class extends SubagentGuardRouter {
206
+ /** ① 纯文本状态块(codex 协议,与 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 CodexSubagentGuardRouter().run());
@@ -0,0 +1,381 @@
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 { fileURLToPath as fileURLToPath3 } from "node:url";
5
+ import { spawnSync } from "node:child_process";
6
+ import { createHash } from "node:crypto";
7
+
8
+ // templates/core/governance/rules.ts
9
+ var protocol = {
10
+ "exit_codes": {
11
+ "pass": 0,
12
+ "block": 2,
13
+ "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"
14
+ },
15
+ "output": {
16
+ "stdout_json_only": true,
17
+ "stderr_human_text": true,
18
+ "field_separator": "::",
19
+ "magic_dir_resolution": "\u6CE8\u5165\u4F18\u5148 \u2192 \u7269\u7406\u4F4D\u7F6E\u63A8\u5BFC \u2192 failClosed\uFF08\u7981\u6B62\u731C\u6D4B adapter \u540D\uFF09"
20
+ },
21
+ "adapters": {
22
+ "claude": {
23
+ "stdout_form": "plain-text",
24
+ "project_dir_env": "CLAUDE_PROJECT_DIR",
25
+ "magic_dir": ".claude",
26
+ "handlerTypes": [
27
+ "command",
28
+ "mcp_tool"
29
+ ]
30
+ },
31
+ "qoder": {
32
+ "stdout_form": "json",
33
+ "project_dir_env": "QODER_PROJECT_DIR",
34
+ "magic_dir": ".qoder",
35
+ "handlerTypes": [
36
+ "command",
37
+ "http"
38
+ ]
39
+ },
40
+ "codex": {
41
+ "stdout_form": "systemMessage",
42
+ "project_dir_env": "git-toplevel",
43
+ "magic_dir": ".codex",
44
+ "handlerTypes": [
45
+ "command"
46
+ ]
47
+ },
48
+ "vscode": {
49
+ "stdout_form": "plain-text",
50
+ "project_dir_env": "PWD",
51
+ "magic_dir": ".vscode",
52
+ "handlerTypes": [
53
+ "command"
54
+ ]
55
+ },
56
+ "trae": {
57
+ "stdout_form": "plain-text",
58
+ "project_dir_env": "PWD",
59
+ "magic_dir": ".trae",
60
+ "handlerTypes": [
61
+ "command"
62
+ ]
63
+ }
64
+ },
65
+ "event_outputs": {
66
+ "qoder": {
67
+ "SessionStart": "additionalContext",
68
+ "UserPromptSubmit": "additionalContext",
69
+ "PreToolUse": "permissionDecision",
70
+ "Stop": "additionalContext",
71
+ "PostToolUse": "feedback",
72
+ "SubagentStart": "additionalContext",
73
+ "SubagentStop": "additionalContext",
74
+ "PostToolUseFailure": "text",
75
+ "PermissionRequest": "text",
76
+ "SessionEnd": "text",
77
+ "PreCompact": "text",
78
+ "Notification": "text"
79
+ },
80
+ "claude": {
81
+ "SessionStart": "additionalContext",
82
+ "UserPromptSubmit": "additionalContext",
83
+ "PreToolUse": "permissionDecision",
84
+ "Stop": "text",
85
+ "PostToolUse": "feedback",
86
+ "SubagentStart": "text",
87
+ "SubagentStop": "text",
88
+ "PostToolUseFailure": "text",
89
+ "PermissionRequest": "text",
90
+ "SessionEnd": "text",
91
+ "PreCompact": "text",
92
+ "Notification": "text"
93
+ },
94
+ "codex": {
95
+ "Stop": "systemMessage",
96
+ "PostToolUse": "text"
97
+ },
98
+ "vscode": {
99
+ "PostToolUse": "text"
100
+ },
101
+ "trae": {
102
+ "PostToolUse": "text"
103
+ }
104
+ },
105
+ "core": {
106
+ "stdout_form": "json",
107
+ "magic_dir": ".add",
108
+ "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"
109
+ },
110
+ "adapter_defaults": {
111
+ "magic_dir_fallback": ".qoder",
112
+ "probe_magic_dirs": [
113
+ ".claude",
114
+ ".qoder",
115
+ ".vscode",
116
+ ".add",
117
+ ".trae",
118
+ ".codex"
119
+ ]
120
+ }
121
+ };
122
+
123
+ // node_modules/.pnpm/find-up@8.0.0/node_modules/find-up/index.js
124
+ import path2 from "node:path";
125
+
126
+ // node_modules/.pnpm/locate-path@8.0.0/node_modules/locate-path/index.js
127
+ import process2 from "node:process";
128
+ import path from "node:path";
129
+ import fs, { promises as fsPromises } from "node:fs";
130
+ import { fileURLToPath } from "node:url";
131
+ var typeMappings = {
132
+ directory: "isDirectory",
133
+ file: "isFile"
134
+ };
135
+ function checkType(type) {
136
+ if (type === "both" || Object.hasOwn(typeMappings, type)) {
137
+ return;
138
+ }
139
+ throw new Error(`Invalid type specified: ${type}`);
140
+ }
141
+ var matchType = (type, stat) => type === "both" ? stat.isFile() || stat.isDirectory() : stat[typeMappings[type]]();
142
+ var toPath = (urlOrPath) => urlOrPath instanceof URL ? fileURLToPath(urlOrPath) : urlOrPath;
143
+ function locatePathSync(paths, {
144
+ cwd = process2.cwd(),
145
+ type = "file",
146
+ allowSymlinks = true
147
+ } = {}) {
148
+ checkType(type);
149
+ cwd = toPath(cwd);
150
+ const statFunction = allowSymlinks ? fs.statSync : fs.lstatSync;
151
+ for (const path_ of paths) {
152
+ try {
153
+ const stat = statFunction(path.resolve(cwd, path_), {
154
+ throwIfNoEntry: false
155
+ });
156
+ if (!stat) {
157
+ continue;
158
+ }
159
+ if (matchType(type, stat)) {
160
+ return path_;
161
+ }
162
+ } catch {
163
+ }
164
+ }
165
+ }
166
+
167
+ // node_modules/.pnpm/unicorn-magic@0.3.0/node_modules/unicorn-magic/node.js
168
+ import { promisify } from "node:util";
169
+ import { execFile as execFileCallback, execFileSync as execFileSyncOriginal } from "node:child_process";
170
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
171
+ var execFileOriginal = promisify(execFileCallback);
172
+ function toPath2(urlOrPath) {
173
+ return urlOrPath instanceof URL ? fileURLToPath2(urlOrPath) : urlOrPath;
174
+ }
175
+ var TEN_MEGABYTES_IN_BYTES = 10 * 1024 * 1024;
176
+
177
+ // node_modules/.pnpm/find-up@8.0.0/node_modules/find-up/index.js
178
+ var findUpStop = /* @__PURE__ */ Symbol("findUpStop");
179
+ function findUpMultipleSync(name, options = {}) {
180
+ let directory = path2.resolve(toPath2(options.cwd) ?? "");
181
+ const { root } = path2.parse(directory);
182
+ const stopAt = path2.resolve(directory, toPath2(options.stopAt) ?? root);
183
+ const limit = options.limit ?? Number.POSITIVE_INFINITY;
184
+ const paths = [name].flat();
185
+ const runMatcher = (locateOptions) => {
186
+ if (typeof name !== "function") {
187
+ return locatePathSync(paths, locateOptions);
188
+ }
189
+ const foundPath = name(locateOptions.cwd);
190
+ if (typeof foundPath === "string") {
191
+ return locatePathSync([foundPath], locateOptions);
192
+ }
193
+ return foundPath;
194
+ };
195
+ const matches = [];
196
+ while (true) {
197
+ const foundPath = runMatcher({ ...options, cwd: directory });
198
+ if (foundPath === findUpStop) {
199
+ break;
200
+ }
201
+ if (foundPath) {
202
+ matches.push(path2.resolve(directory, foundPath));
203
+ }
204
+ if (directory === stopAt || matches.length >= limit) {
205
+ break;
206
+ }
207
+ directory = path2.dirname(directory);
208
+ }
209
+ return matches;
210
+ }
211
+ function findUpSync(name, options = {}) {
212
+ const matches = findUpMultipleSync(name, { ...options, limit: 1 });
213
+ return matches[0];
214
+ }
215
+
216
+ // templates/core/governance/common.ts
217
+ var EXIT_PASS = protocol.exit_codes.pass;
218
+ var EXIT_BLOCK = protocol.exit_codes.block;
219
+ var STATE_SEP = protocol.output.field_separator;
220
+ function readHookInput() {
221
+ if (process.stdin.isTTY) return "{}";
222
+ return readFileSync(0, "utf-8");
223
+ }
224
+ function tryResolveMagicDir() {
225
+ const injected = process.env.MAGIC_DIR;
226
+ if (injected) return injected;
227
+ const startDir = dirname(fileURLToPath3(import.meta.url));
228
+ const hit = findUpSync((dir) => basename(dir).startsWith(".") ? dir : void 0, {
229
+ cwd: startDir,
230
+ type: "directory"
231
+ });
232
+ return hit ? basename(hit) : "";
233
+ }
234
+ function queryPlanStatus() {
235
+ const magicDir = process.env.MAGIC_DIR;
236
+ if (!magicDir) {
237
+ return {
238
+ stdout: '{"availability":"STATUS_UNAVAILABLE","source":"database","reason":"magicDir \u672A\u6CE8\u5165\u4E14\u65E0\u6CD5\u4ECE\u7269\u7406\u4F4D\u7F6E\u63A8\u5BFC"}',
239
+ exitCode: 3
240
+ };
241
+ }
242
+ const bridge = join(
243
+ process.env.PROJECT_DIR || process.cwd(),
244
+ magicDir,
245
+ "scripts",
246
+ "plan-status-bridge.ts"
247
+ );
248
+ if (!existsSync(bridge)) {
249
+ return {
250
+ stdout: '{"availability":"STATUS_UNAVAILABLE","source":"database","reason":"plan-status bridge missing"}',
251
+ exitCode: 3
252
+ };
253
+ }
254
+ const r = spawnSync("node", ["--import", "tsx", bridge], {
255
+ encoding: "utf-8",
256
+ timeout: 1e4
257
+ });
258
+ return { stdout: r.stdout ?? "", exitCode: r.status ?? -1 };
259
+ }
260
+ function detectActiveAdd() {
261
+ const r = queryPlanStatus();
262
+ if (r.exitCode !== 0) {
263
+ let reason = "database status unavailable";
264
+ try {
265
+ const parsed = JSON.parse(r.stdout);
266
+ if (parsed.reason) reason = parsed.reason;
267
+ } catch {
268
+ }
269
+ return `__STATUS_UNAVAILABLE__::${reason}::database::none::none`;
270
+ }
271
+ let snapshot;
272
+ try {
273
+ snapshot = JSON.parse(r.stdout);
274
+ } catch {
275
+ return null;
276
+ }
277
+ if (!(snapshot.availability === "READY" && snapshot.isActive === true)) return null;
278
+ const done = snapshot.progress?.doneTasks ?? 0;
279
+ const total = snapshot.progress?.totalTasks ?? 0;
280
+ const approval = snapshot.approvalStatus ?? "none";
281
+ return `${snapshot.planName}::${done}/${total}::${approval}::none::none`;
282
+ }
283
+ function localIsoSeconds() {
284
+ const d = /* @__PURE__ */ new Date();
285
+ const pad = (n) => String(n).padStart(2, "0");
286
+ const offsetMin = -d.getTimezoneOffset();
287
+ const sign = offsetMin >= 0 ? "+" : "-";
288
+ const offAbs = Math.abs(offsetMin);
289
+ const off = `${sign}${pad(Math.floor(offAbs / 60))}:${pad(offAbs % 60)}`;
290
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}${off}`;
291
+ }
292
+ function projectHash() {
293
+ try {
294
+ return createHash("md5").update(`${process.env.PROJECT_DIR || process.cwd()}
295
+ `).digest("hex").slice(0, 8);
296
+ } catch {
297
+ return "default";
298
+ }
299
+ }
300
+ var DEV_FLAG = `/tmp/add_dev_${projectHash()}`;
301
+
302
+ // templates/core/governance/subagent-stop-router.ts
303
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
304
+ var SubagentStopRouter = class {
305
+ /** 对齐 bash jq -r '.agent_type // .subagent_name // "unknown"':
306
+ * 空输入/非法 JSON → jq 无输出 → 空串(不是 "unknown");字段缺失才回退 "unknown" */
307
+ agentNameFrom(input2) {
308
+ if (input2.trim() === "") return "";
309
+ try {
310
+ const parsed = JSON.parse(input2);
311
+ const v = parsed.agent_type ?? parsed.subagent_name;
312
+ return typeof v === "string" && v !== "" ? v : "unknown";
313
+ } catch {
314
+ return "";
315
+ }
316
+ }
317
+ // ─────────────────────────── 扩展点 ───────────────────────────
318
+ /** ① 无活跃 Plan 告警(core: stderr;qoder 子类 override: stdout JSON) */
319
+ emitNoPlanWarn(agentName) {
320
+ process.stderr.write(`[ADD SubagentStop] \u26A0\uFE0F ${agentName} \u5DF2\u5B8C\u6210\uFF0C\u4F46\u65E0\u6D3B\u8DC3 ADD Plan \u65E0\u6CD5\u6821\u9A8C\u8FB9\u754C
321
+ `);
322
+ }
323
+ /** ② 边界通过审计(core: stderr;qoder 子类 override: stdout JSON) */
324
+ emitBoundaryPass(agentName, planKw, _handoff) {
325
+ process.stderr.write(`[ADD SubagentStop] ${agentName} \u5DF2\u5B8C\u6210 \u2014 \u5173\u8054 Plan: ${planKw}
326
+ `);
327
+ process.stderr.write(`[ADD SubagentStop] ${agentName} \u8FB9\u754C\u6821\u9A8C\u901A\u8FC7 \u2014 ${localIsoSeconds()}
328
+ `);
329
+ }
330
+ /** 交付物越界检查开关(core: true;qoder: false——qoder 轻量提示无阻断) */
331
+ checkDeliverables() {
332
+ return true;
333
+ }
334
+ /** 主路由:返回 exit code(0 放行 / 2 阻断) */
335
+ run(input2) {
336
+ const agentName = this.agentNameFrom(input2);
337
+ const state = detectActiveAdd();
338
+ if (state === null) {
339
+ this.emitNoPlanWarn(agentName);
340
+ return 0;
341
+ }
342
+ const planKw = state.split("::")[0] ?? "";
343
+ const handoff = state.split("::")[3] ?? "";
344
+ if (this.checkDeliverables() && handoff && handoff !== "none" && existsSync2(handoff)) {
345
+ const deliverablesRaw = (() => {
346
+ try {
347
+ const parsed = JSON.parse(input2);
348
+ return typeof parsed.deliverables === "string" ? parsed.deliverables : "";
349
+ } catch {
350
+ return "";
351
+ }
352
+ })();
353
+ if (deliverablesRaw !== "") {
354
+ const handoffContent = readFileSync2(handoff, "utf-8");
355
+ const violations = [];
356
+ for (const f of deliverablesRaw.split(/\s+/)) {
357
+ if (f === "") continue;
358
+ if (!handoffContent.includes(f)) violations.push(f);
359
+ }
360
+ if (violations.length > 0) {
361
+ process.stderr.write(`[ADD SubagentStop] \u274C ${agentName} \u4EA4\u4ED8\u7269\u8D85\u51FA spec \u8FB9\u754C: ${violations.join(" ")}
362
+ `);
363
+ 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");
364
+ return EXIT_BLOCK;
365
+ }
366
+ }
367
+ }
368
+ this.emitBoundaryPass(agentName, planKw, handoff);
369
+ return 0;
370
+ }
371
+ };
372
+
373
+ // templates/adapters/codex/hooks/subagent-stop.ts
374
+ var CodexSubagentStopRouter = class extends SubagentStopRouter {
375
+ // 当前无 override(边界校验 + 审计聚合与 core 基线一致);命名子类承载端身份 + 未来演进位
376
+ };
377
+ process.env.PROJECT_DIR = process.env.PROJECT_DIR || process.cwd();
378
+ var inferred = tryResolveMagicDir();
379
+ if (inferred && !process.env.MAGIC_DIR) process.env.MAGIC_DIR = inferred;
380
+ var input = readHookInput();
381
+ process.exit(new CodexSubagentStopRouter().run(input));