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,254 @@
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 jsonGet(json, field) {
131
+ const re = new RegExp(`"${field}"\\s*:\\s*"([^"]*)"`);
132
+ const m = re.exec(json);
133
+ return m?.[1] ?? "";
134
+ }
135
+ function queryPlanStatus() {
136
+ const magicDir = process.env.MAGIC_DIR;
137
+ if (!magicDir) {
138
+ return {
139
+ stdout: '{"availability":"STATUS_UNAVAILABLE","source":"database","reason":"magicDir \u672A\u6CE8\u5165\u4E14\u65E0\u6CD5\u4ECE\u7269\u7406\u4F4D\u7F6E\u63A8\u5BFC"}',
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: 1e4
158
+ });
159
+ return { stdout: r.stdout ?? "", exitCode: r.status ?? -1 };
160
+ }
161
+ function detectActiveAdd() {
162
+ const r = queryPlanStatus();
163
+ if (r.exitCode !== 0) {
164
+ let reason = "database status unavailable";
165
+ try {
166
+ const parsed = JSON.parse(r.stdout);
167
+ if (parsed.reason) reason = parsed.reason;
168
+ } catch {
169
+ }
170
+ return `__STATUS_UNAVAILABLE__::${reason}::database::none::none`;
171
+ }
172
+ let snapshot;
173
+ try {
174
+ snapshot = JSON.parse(r.stdout);
175
+ } catch {
176
+ return null;
177
+ }
178
+ if (!(snapshot.availability === "READY" && snapshot.isActive === true)) return null;
179
+ const done = snapshot.progress?.doneTasks ?? 0;
180
+ const total = snapshot.progress?.totalTasks ?? 0;
181
+ const approval = snapshot.approvalStatus ?? "none";
182
+ return `${snapshot.planName}::${done}/${total}::${approval}::none::none`;
183
+ }
184
+ function projectHash() {
185
+ try {
186
+ return createHash("md5").update(`${process.env.PROJECT_DIR || process.cwd()}
187
+ `).digest("hex").slice(0, 8);
188
+ } catch {
189
+ return "default";
190
+ }
191
+ }
192
+ var DEV_FLAG = `/tmp/add_dev_${projectHash()}`;
193
+
194
+ // templates/core/governance/notification-router.ts
195
+ import { existsSync as existsSync2, readdirSync } from "node:fs";
196
+ import { join as join2 } from "node:path";
197
+ var NotificationRouter = class {
198
+ projectDir;
199
+ fallbackMagicDir;
200
+ constructor(projectDir, fallbackMagicDir) {
201
+ this.projectDir = projectDir;
202
+ this.fallbackMagicDir = fallbackMagicDir;
203
+ }
204
+ /** 提醒输出(core 默认: 含 Plan 前缀——bash 原文逐字;qoder 子类 override 无前缀) */
205
+ emitReminder(plan, reviewsDir) {
206
+ process.stdout.write(`[ADD Notification] Plan: ${plan} \u2014 \u8BF7\u68C0\u67E5 Review \u6587\u6863: ${reviewsDir}
207
+ `);
208
+ }
209
+ /**
210
+ * magicDir 解析(core 默认: env 优先 → 跨端探测循环 → fallback;bash 原文逐字):
211
+ * 扩展点: qoder 子类 override 为固定值(qoder bash 原文硬编码 ${PROJECT_DIR}/.qoder/reviews,无探测循环)。
212
+ * Task 9.4 中立化: 探测列表从 hook-protocol-rules.toml [protocol.adapter_defaults.probe_magic_dirs] 读取——
213
+ * 原硬编码 [".claude", ".qoder", ".vscode", ".add"] 缺 .trae/.codex(新增端需改代码);声明化后随端演进。
214
+ */
215
+ resolveMagicDir() {
216
+ const env = process.env.MAGIC_DIR;
217
+ if (env) return env;
218
+ const defaults = protocol.adapter_defaults;
219
+ const probeList = defaults?.probe_magic_dirs ?? [".claude", ".qoder", ".vscode", ".add"];
220
+ for (const m of probeList) {
221
+ if (existsSync2(join2(this.projectDir, m))) return m;
222
+ }
223
+ return this.fallbackMagicDir;
224
+ }
225
+ /** 主路由:返回 exit code(0) */
226
+ run(input2) {
227
+ const ntype = jsonGet(input2, "notification_type");
228
+ if (ntype !== "result") return 0;
229
+ const state = detectActiveAdd();
230
+ if (state === null) return 0;
231
+ const plan = state.split("::")[0] ?? "";
232
+ const magicDir = this.resolveMagicDir();
233
+ const reviewsDir = join2(this.projectDir, magicDir, "reviews");
234
+ if (existsSync2(reviewsDir) && readdirSync(reviewsDir).some((f) => f.endsWith(".md"))) {
235
+ this.emitReminder(plan, reviewsDir);
236
+ }
237
+ return 0;
238
+ }
239
+ };
240
+
241
+ // templates/adapters/codex/hooks/notification.ts
242
+ var CodexNotificationRouter = class extends NotificationRouter {
243
+ /** ① codex 无跨端探测循环(bash 原文硬编码 .codex;唯一解析链 + 兜底) */
244
+ resolveMagicDir() {
245
+ return process.env.MAGIC_DIR ?? ".codex";
246
+ }
247
+ /** 协议差异封装: fallbackMagicDir = ".codex" */
248
+ constructor(projectDir) {
249
+ super(projectDir, ".codex");
250
+ }
251
+ };
252
+ var input = readHookInput();
253
+ var PROJECT_DIR = process.env.PROJECT_DIR || process.cwd();
254
+ process.exit(new CodexNotificationRouter(PROJECT_DIR).run(input));
@@ -0,0 +1,169 @@
1
+ // templates/core/governance/common.ts
2
+ import { existsSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
3
+ import { createHash } from "node:crypto";
4
+
5
+ // templates/core/governance/rules.ts
6
+ var protocol = {
7
+ "exit_codes": {
8
+ "pass": 0,
9
+ "block": 2,
10
+ "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"
11
+ },
12
+ "output": {
13
+ "stdout_json_only": true,
14
+ "stderr_human_text": true,
15
+ "field_separator": "::",
16
+ "magic_dir_resolution": "\u6CE8\u5165\u4F18\u5148 \u2192 \u7269\u7406\u4F4D\u7F6E\u63A8\u5BFC \u2192 failClosed\uFF08\u7981\u6B62\u731C\u6D4B adapter \u540D\uFF09"
17
+ },
18
+ "adapters": {
19
+ "claude": {
20
+ "stdout_form": "plain-text",
21
+ "project_dir_env": "CLAUDE_PROJECT_DIR",
22
+ "magic_dir": ".claude",
23
+ "handlerTypes": [
24
+ "command",
25
+ "mcp_tool"
26
+ ]
27
+ },
28
+ "qoder": {
29
+ "stdout_form": "json",
30
+ "project_dir_env": "QODER_PROJECT_DIR",
31
+ "magic_dir": ".qoder",
32
+ "handlerTypes": [
33
+ "command",
34
+ "http"
35
+ ]
36
+ },
37
+ "codex": {
38
+ "stdout_form": "systemMessage",
39
+ "project_dir_env": "git-toplevel",
40
+ "magic_dir": ".codex",
41
+ "handlerTypes": [
42
+ "command"
43
+ ]
44
+ },
45
+ "vscode": {
46
+ "stdout_form": "plain-text",
47
+ "project_dir_env": "PWD",
48
+ "magic_dir": ".vscode",
49
+ "handlerTypes": [
50
+ "command"
51
+ ]
52
+ },
53
+ "trae": {
54
+ "stdout_form": "plain-text",
55
+ "project_dir_env": "PWD",
56
+ "magic_dir": ".trae",
57
+ "handlerTypes": [
58
+ "command"
59
+ ]
60
+ }
61
+ },
62
+ "event_outputs": {
63
+ "qoder": {
64
+ "SessionStart": "additionalContext",
65
+ "UserPromptSubmit": "additionalContext",
66
+ "PreToolUse": "permissionDecision",
67
+ "Stop": "additionalContext",
68
+ "PostToolUse": "feedback",
69
+ "SubagentStart": "additionalContext",
70
+ "SubagentStop": "additionalContext",
71
+ "PostToolUseFailure": "text",
72
+ "PermissionRequest": "text",
73
+ "SessionEnd": "text",
74
+ "PreCompact": "text",
75
+ "Notification": "text"
76
+ },
77
+ "claude": {
78
+ "SessionStart": "additionalContext",
79
+ "UserPromptSubmit": "additionalContext",
80
+ "PreToolUse": "permissionDecision",
81
+ "Stop": "text",
82
+ "PostToolUse": "feedback",
83
+ "SubagentStart": "text",
84
+ "SubagentStop": "text",
85
+ "PostToolUseFailure": "text",
86
+ "PermissionRequest": "text",
87
+ "SessionEnd": "text",
88
+ "PreCompact": "text",
89
+ "Notification": "text"
90
+ },
91
+ "codex": {
92
+ "Stop": "systemMessage",
93
+ "PostToolUse": "text"
94
+ },
95
+ "vscode": {
96
+ "PostToolUse": "text"
97
+ },
98
+ "trae": {
99
+ "PostToolUse": "text"
100
+ }
101
+ },
102
+ "core": {
103
+ "stdout_form": "json",
104
+ "magic_dir": ".add",
105
+ "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"
106
+ },
107
+ "adapter_defaults": {
108
+ "magic_dir_fallback": ".qoder",
109
+ "probe_magic_dirs": [
110
+ ".claude",
111
+ ".qoder",
112
+ ".vscode",
113
+ ".add",
114
+ ".trae",
115
+ ".codex"
116
+ ]
117
+ }
118
+ };
119
+
120
+ // templates/core/governance/common.ts
121
+ var EXIT_PASS = protocol.exit_codes.pass;
122
+ var EXIT_BLOCK = protocol.exit_codes.block;
123
+ var STATE_SEP = protocol.output.field_separator;
124
+ function readHookInput() {
125
+ if (process.stdin.isTTY) return "{}";
126
+ return readFileSync(0, "utf-8");
127
+ }
128
+ function projectHash() {
129
+ try {
130
+ return createHash("md5").update(`${process.env.PROJECT_DIR || process.cwd()}
131
+ `).digest("hex").slice(0, 8);
132
+ } catch {
133
+ return "default";
134
+ }
135
+ }
136
+ var DEV_FLAG = `/tmp/add_dev_${projectHash()}`;
137
+
138
+ // templates/core/governance/permission-gate-router.ts
139
+ function fieldOr(json, field, fallback) {
140
+ const re = new RegExp(`"${field}"\\s*:\\s*"([^"]*)"`);
141
+ const m = re.exec(json);
142
+ return m ? m[1] : fallback;
143
+ }
144
+ var PermissionGateRouter = class {
145
+ /** 主路由:返回 exit code(0) */
146
+ run(input2) {
147
+ const toolName = input2.trim() === "" ? "" : fieldOr(input2, "tool_name", this.fallbackToolName());
148
+ this.emit(toolName);
149
+ return 0;
150
+ }
151
+ /** tool_name 缺失回退(core: ""——bash `// empty` 语义;qoder 子类 override: "unknown") */
152
+ fallbackToolName() {
153
+ return "";
154
+ }
155
+ /** 输出(core 默认: 仅高风险工具 stdout 二次确认提示;qoder 子类 override: 全量 stderr 日志) */
156
+ emit(toolName) {
157
+ if (toolName === "Bash" || toolName === "Write" || toolName === "Edit") {
158
+ process.stdout.write(`[ADD PermissionGate] \u9AD8\u98CE\u9669\u5DE5\u5177: ${toolName}\uFF0C\u8BF7\u786E\u8BA4\u64CD\u4F5C\u3002
159
+ `);
160
+ }
161
+ }
162
+ };
163
+
164
+ // templates/adapters/codex/hooks/permission-gate.ts
165
+ var CodexPermissionGateRouter = class extends PermissionGateRouter {
166
+ // 当前无 override(`// empty` 回退 + 高风险工具提示与 core 基线一致);命名子类承载端身份 + 未来演进位
167
+ };
168
+ var input = readHookInput();
169
+ process.exit(new CodexPermissionGateRouter().run(input));
@@ -0,0 +1,176 @@
1
+ // templates/core/governance/common.ts
2
+ import { existsSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
3
+ import { createHash } from "node:crypto";
4
+
5
+ // templates/core/governance/rules.ts
6
+ var protocol = {
7
+ "exit_codes": {
8
+ "pass": 0,
9
+ "block": 2,
10
+ "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"
11
+ },
12
+ "output": {
13
+ "stdout_json_only": true,
14
+ "stderr_human_text": true,
15
+ "field_separator": "::",
16
+ "magic_dir_resolution": "\u6CE8\u5165\u4F18\u5148 \u2192 \u7269\u7406\u4F4D\u7F6E\u63A8\u5BFC \u2192 failClosed\uFF08\u7981\u6B62\u731C\u6D4B adapter \u540D\uFF09"
17
+ },
18
+ "adapters": {
19
+ "claude": {
20
+ "stdout_form": "plain-text",
21
+ "project_dir_env": "CLAUDE_PROJECT_DIR",
22
+ "magic_dir": ".claude",
23
+ "handlerTypes": [
24
+ "command",
25
+ "mcp_tool"
26
+ ]
27
+ },
28
+ "qoder": {
29
+ "stdout_form": "json",
30
+ "project_dir_env": "QODER_PROJECT_DIR",
31
+ "magic_dir": ".qoder",
32
+ "handlerTypes": [
33
+ "command",
34
+ "http"
35
+ ]
36
+ },
37
+ "codex": {
38
+ "stdout_form": "systemMessage",
39
+ "project_dir_env": "git-toplevel",
40
+ "magic_dir": ".codex",
41
+ "handlerTypes": [
42
+ "command"
43
+ ]
44
+ },
45
+ "vscode": {
46
+ "stdout_form": "plain-text",
47
+ "project_dir_env": "PWD",
48
+ "magic_dir": ".vscode",
49
+ "handlerTypes": [
50
+ "command"
51
+ ]
52
+ },
53
+ "trae": {
54
+ "stdout_form": "plain-text",
55
+ "project_dir_env": "PWD",
56
+ "magic_dir": ".trae",
57
+ "handlerTypes": [
58
+ "command"
59
+ ]
60
+ }
61
+ },
62
+ "event_outputs": {
63
+ "qoder": {
64
+ "SessionStart": "additionalContext",
65
+ "UserPromptSubmit": "additionalContext",
66
+ "PreToolUse": "permissionDecision",
67
+ "Stop": "additionalContext",
68
+ "PostToolUse": "feedback",
69
+ "SubagentStart": "additionalContext",
70
+ "SubagentStop": "additionalContext",
71
+ "PostToolUseFailure": "text",
72
+ "PermissionRequest": "text",
73
+ "SessionEnd": "text",
74
+ "PreCompact": "text",
75
+ "Notification": "text"
76
+ },
77
+ "claude": {
78
+ "SessionStart": "additionalContext",
79
+ "UserPromptSubmit": "additionalContext",
80
+ "PreToolUse": "permissionDecision",
81
+ "Stop": "text",
82
+ "PostToolUse": "feedback",
83
+ "SubagentStart": "text",
84
+ "SubagentStop": "text",
85
+ "PostToolUseFailure": "text",
86
+ "PermissionRequest": "text",
87
+ "SessionEnd": "text",
88
+ "PreCompact": "text",
89
+ "Notification": "text"
90
+ },
91
+ "codex": {
92
+ "Stop": "systemMessage",
93
+ "PostToolUse": "text"
94
+ },
95
+ "vscode": {
96
+ "PostToolUse": "text"
97
+ },
98
+ "trae": {
99
+ "PostToolUse": "text"
100
+ }
101
+ },
102
+ "core": {
103
+ "stdout_form": "json",
104
+ "magic_dir": ".add",
105
+ "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"
106
+ },
107
+ "adapter_defaults": {
108
+ "magic_dir_fallback": ".qoder",
109
+ "probe_magic_dirs": [
110
+ ".claude",
111
+ ".qoder",
112
+ ".vscode",
113
+ ".add",
114
+ ".trae",
115
+ ".codex"
116
+ ]
117
+ }
118
+ };
119
+
120
+ // templates/core/governance/common.ts
121
+ var EXIT_PASS = protocol.exit_codes.pass;
122
+ var EXIT_BLOCK = protocol.exit_codes.block;
123
+ var STATE_SEP = protocol.output.field_separator;
124
+ function readHookInput() {
125
+ if (process.stdin.isTTY) return "{}";
126
+ return readFileSync(0, "utf-8");
127
+ }
128
+ function projectHash() {
129
+ try {
130
+ return createHash("md5").update(`${process.env.PROJECT_DIR || process.cwd()}
131
+ `).digest("hex").slice(0, 8);
132
+ } catch {
133
+ return "default";
134
+ }
135
+ }
136
+ var DEV_FLAG = `/tmp/add_dev_${projectHash()}`;
137
+
138
+ // templates/core/governance/post-tool-failure-router.ts
139
+ function fieldOr(json, field, fallback) {
140
+ const re = new RegExp(`"${field}"\\s*:\\s*"([^"]*)"`);
141
+ const m = re.exec(json);
142
+ return m ? m[1] : fallback;
143
+ }
144
+ var PostToolFailureRouter = class {
145
+ /** 主路由:返回 exit code(0) */
146
+ run(input2) {
147
+ const empty = input2.trim() === "";
148
+ const toolName = empty ? this.emptyUsesFallback() ? this.fallbackToolName() : "" : fieldOr(input2, "tool_name", this.fallbackToolName());
149
+ const error = empty ? this.emptyUsesFallback() ? this.fallbackError() : "" : fieldOr(input2, "error", this.fallbackError());
150
+ this.emit(toolName, error);
151
+ return 0;
152
+ }
153
+ /** 空输入是否走 fallback(core/qoder: false——空串;vscode 子类 override: true——bash grep || echo 语义) */
154
+ emptyUsesFallback() {
155
+ return false;
156
+ }
157
+ /** tool_name 缺失回退(core: "";qoder 子类 override: "unknown") */
158
+ fallbackToolName() {
159
+ return "";
160
+ }
161
+ /** error 缺失回退(core: "";qoder 子类 override: "unknown";vscode 子类 override: "未知错误") */
162
+ fallbackError() {
163
+ return "";
164
+ }
165
+ /** 输出(core 默认: stdout 固定文本——bash 原文逐字;adapter 子类 override 用 toolName/error) */
166
+ emit(_toolName, _error) {
167
+ process.stdout.write("[ADD PostToolFailure] \u5DE5\u5177\u8C03\u7528\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u9519\u8BEF\u4FE1\u606F\u5E76\u4FEE\u590D\u3002\n");
168
+ }
169
+ };
170
+
171
+ // templates/adapters/codex/hooks/post-tool-failure.ts
172
+ var CodexPostToolFailureRouter = class extends PostToolFailureRouter {
173
+ // 当前无 override(stdout 固定提示与 core 基线一致);命名子类承载端身份 + 未来演进位
174
+ };
175
+ var input = readHookInput();
176
+ process.exit(new CodexPostToolFailureRouter().run(input));