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,301 @@
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 localIsoSeconds() {
176
+ const d = /* @__PURE__ */ new Date();
177
+ const pad = (n) => String(n).padStart(2, "0");
178
+ const offsetMin = -d.getTimezoneOffset();
179
+ const sign = offsetMin >= 0 ? "+" : "-";
180
+ const offAbs = Math.abs(offsetMin);
181
+ const off = `${sign}${pad(Math.floor(offAbs / 60))}:${pad(offAbs % 60)}`;
182
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}${off}`;
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
+ function hasDevAction() {
194
+ return existsSync(DEV_FLAG);
195
+ }
196
+ function clearDevAction() {
197
+ try {
198
+ unlinkSync(DEV_FLAG);
199
+ } catch {
200
+ }
201
+ }
202
+ function checkAddCompleteness(handoff, addRoute) {
203
+ const issues = [];
204
+ if (handoff && existsSync(handoff)) {
205
+ const content = readFileSync(handoff, "utf-8");
206
+ if (!/验收|收敛|闭环|本轮改了什么|devlog/.test(content)) {
207
+ issues.push(" [ ] devlog \u7F3A\u5931\uFF08handoff \u65E0\u9A8C\u6536\u8BB0\u5F55\uFF09");
208
+ }
209
+ const unchecked = (content.match(/\[ \]/g) || []).length;
210
+ if (unchecked > 0) {
211
+ issues.push(` [ ] handoff ${unchecked} \u9879\u672A\u52FE\u9009`);
212
+ }
213
+ }
214
+ if (addRoute && existsSync(addRoute)) {
215
+ const content = readFileSync(addRoute, "utf-8");
216
+ const unchecked = (content.match(/\[ \]/g) || []).length;
217
+ if (unchecked > 0) {
218
+ issues.push(` [ ] add-route ${unchecked} Step \u672A\u95ED\u73AF`);
219
+ }
220
+ }
221
+ return issues;
222
+ }
223
+
224
+ // templates/core/governance/session-end.ts
225
+ import { unlinkSync as unlinkSync2 } from "node:fs";
226
+ function stateField(state, index) {
227
+ return state.split("::")[index] ?? "";
228
+ }
229
+ var SessionEndGuard = class {
230
+ projectDir;
231
+ tplFlag;
232
+ constructor(projectDir) {
233
+ this.projectDir = projectDir ?? process.env.PROJECT_DIR ?? process.cwd();
234
+ this.tplFlag = `/tmp/add_tpl_${projectHash()}`;
235
+ }
236
+ /** ① 清理 tpl-injected 标记 + dev action 标记(对齐 cleanup_tpl_flag/cleanup_dev_flag) */
237
+ cleanupFlags() {
238
+ try {
239
+ unlinkSync2(this.tplFlag);
240
+ } catch {
241
+ }
242
+ clearDevAction();
243
+ }
244
+ /** ② 审计结算(core 默认: stderr 文本;qoder 子类 override: stdout JSON additionalContext) */
245
+ emitSettle() {
246
+ process.stderr.write(`[ADD SessionEnd] \u4F1A\u8BDD\u7ED3\u675F \u2014 ${localIsoSeconds()}
247
+ `);
248
+ }
249
+ /** ② 审计结算(输出到 stderr 供日志记录,对齐 bash date -Iseconds) */
250
+ settle() {
251
+ this.emitSettle();
252
+ }
253
+ /** ③ Stop 未触发兜底(对齐 stop_fallback:dev action 标记还在 → 补 checklist 快照) */
254
+ stopFallback() {
255
+ if (!hasDevAction()) return;
256
+ process.stderr.write("[ADD SessionEnd] \u26A0\uFE0F \u68C0\u6D4B\u5230 dev action \u6807\u8BB0\u672A\u6E05\u9664\u2014\u2014Stop \u53EF\u80FD\u672A\u89E6\u53D1\u9A8C\u6536\u68C0\u67E5\n");
257
+ const state = detectActiveAdd();
258
+ if (state === null) return;
259
+ const handoff = stateField(state, 3);
260
+ const addRoute = stateField(state, 4);
261
+ if (handoff && handoff !== "none") {
262
+ process.stderr.write("[ADD SessionEnd] \u8865\u6267\u884C checklist \u5FEB\u7167\uFF08best-effort\uFF0C\u4E0D\u963B\u65AD\uFF09\n");
263
+ const issues = checkAddCompleteness(handoff, addRoute !== "none" ? addRoute : "");
264
+ for (const issue of issues) {
265
+ process.stderr.write(issue + "\n");
266
+ }
267
+ }
268
+ }
269
+ /** 主入口:①清理 → ②结算 → ③兜底(对齐 bash main) */
270
+ run() {
271
+ this.cleanupFlags();
272
+ this.settle();
273
+ this.stopFallback();
274
+ return 0;
275
+ }
276
+ };
277
+
278
+ // templates/adapters/qoder/hooks/lib/qoder-env.ts
279
+ function resolveQoderProjectDir() {
280
+ return process.env.CLAUDE_PROJECT_DIR || process.env.QODER_PROJECT_DIR || process.env.QODERCN_PROJECT_DIR || process.cwd();
281
+ }
282
+ function injectProjectDir() {
283
+ const dir = resolveQoderProjectDir();
284
+ process.env.PROJECT_DIR = dir;
285
+ return dir;
286
+ }
287
+
288
+ // templates/adapters/qoder/hooks/session-end.ts
289
+ var QoderSessionEndGuard = class extends SessionEndGuard {
290
+ /** ② qoder 协议: stdout JSON additionalContext(含 fallbackMsg 检测——bash 缺陷照搬顺序) */
291
+ emitSettle() {
292
+ let fallbackMsg = "";
293
+ if (hasDevAction()) {
294
+ fallbackMsg = "\u26A0\uFE0F \u68C0\u6D4B\u5230 dev action \u6807\u8BB0\u672A\u6E05\u9664\u2014\u2014Stop \u53EF\u80FD\u672A\u89E6\u53D1\u9A8C\u6536\u68C0\u67E5";
295
+ }
296
+ const ctx = `[ADD SessionEnd] \u4F1A\u8BDD\u7ED3\u675F \u2014 ${localIsoSeconds()}\u3002\u6807\u8BB0\u5DF2\u6E05\u7406\u3002${fallbackMsg}`;
297
+ process.stdout.write(JSON.stringify({ hookSpecificOutput: { hookEventName: "SessionEnd", additionalContext: ctx } }) + "\n");
298
+ }
299
+ };
300
+ injectProjectDir();
301
+ process.exit(new QoderSessionEndGuard().run());