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,3 @@
1
+ // templates/adapters/claude/hooks/review-checklist.ts
2
+ process.stdout.write("[ADD ReviewChecklist] \u8BF7\u5728\u63D0\u4EA4\u524D\u786E\u8BA4 checklist.md \u5168\u90E8\u9879\u5DF2\u52FE\u9009\u3002\n");
3
+ process.exit(0);
@@ -0,0 +1,287 @@
1
+ // templates/core/governance/session-end.ts
2
+ import { unlinkSync as unlinkSync2 } from "node:fs";
3
+
4
+ // templates/core/governance/common.ts
5
+ import { existsSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
6
+ import { basename, dirname, join } from "node:path";
7
+ import { spawnSync } from "node:child_process";
8
+ import { createHash } from "node:crypto";
9
+
10
+ // templates/core/governance/rules.ts
11
+ var protocol = {
12
+ "exit_codes": {
13
+ "pass": 0,
14
+ "block": 2,
15
+ "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"
16
+ },
17
+ "output": {
18
+ "stdout_json_only": true,
19
+ "stderr_human_text": true,
20
+ "field_separator": "::",
21
+ "magic_dir_resolution": "\u6CE8\u5165\u4F18\u5148 \u2192 \u7269\u7406\u4F4D\u7F6E\u63A8\u5BFC \u2192 failClosed\uFF08\u7981\u6B62\u731C\u6D4B adapter \u540D\uFF09"
22
+ },
23
+ "adapters": {
24
+ "claude": {
25
+ "stdout_form": "plain-text",
26
+ "project_dir_env": "CLAUDE_PROJECT_DIR",
27
+ "magic_dir": ".claude",
28
+ "handlerTypes": [
29
+ "command",
30
+ "mcp_tool"
31
+ ]
32
+ },
33
+ "qoder": {
34
+ "stdout_form": "json",
35
+ "project_dir_env": "QODER_PROJECT_DIR",
36
+ "magic_dir": ".qoder",
37
+ "handlerTypes": [
38
+ "command",
39
+ "http"
40
+ ]
41
+ },
42
+ "codex": {
43
+ "stdout_form": "systemMessage",
44
+ "project_dir_env": "git-toplevel",
45
+ "magic_dir": ".codex",
46
+ "handlerTypes": [
47
+ "command"
48
+ ]
49
+ },
50
+ "vscode": {
51
+ "stdout_form": "plain-text",
52
+ "project_dir_env": "PWD",
53
+ "magic_dir": ".vscode",
54
+ "handlerTypes": [
55
+ "command"
56
+ ]
57
+ },
58
+ "trae": {
59
+ "stdout_form": "plain-text",
60
+ "project_dir_env": "PWD",
61
+ "magic_dir": ".trae",
62
+ "handlerTypes": [
63
+ "command"
64
+ ]
65
+ }
66
+ },
67
+ "event_outputs": {
68
+ "qoder": {
69
+ "SessionStart": "additionalContext",
70
+ "UserPromptSubmit": "additionalContext",
71
+ "PreToolUse": "permissionDecision",
72
+ "Stop": "additionalContext",
73
+ "PostToolUse": "feedback",
74
+ "SubagentStart": "additionalContext",
75
+ "SubagentStop": "additionalContext",
76
+ "PostToolUseFailure": "text",
77
+ "PermissionRequest": "text",
78
+ "SessionEnd": "text",
79
+ "PreCompact": "text",
80
+ "Notification": "text"
81
+ },
82
+ "claude": {
83
+ "SessionStart": "additionalContext",
84
+ "UserPromptSubmit": "additionalContext",
85
+ "PreToolUse": "permissionDecision",
86
+ "Stop": "text",
87
+ "PostToolUse": "feedback",
88
+ "SubagentStart": "text",
89
+ "SubagentStop": "text",
90
+ "PostToolUseFailure": "text",
91
+ "PermissionRequest": "text",
92
+ "SessionEnd": "text",
93
+ "PreCompact": "text",
94
+ "Notification": "text"
95
+ },
96
+ "codex": {
97
+ "Stop": "systemMessage",
98
+ "PostToolUse": "text"
99
+ },
100
+ "vscode": {
101
+ "PostToolUse": "text"
102
+ },
103
+ "trae": {
104
+ "PostToolUse": "text"
105
+ }
106
+ },
107
+ "core": {
108
+ "stdout_form": "json",
109
+ "magic_dir": ".add",
110
+ "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"
111
+ },
112
+ "adapter_defaults": {
113
+ "magic_dir_fallback": ".qoder",
114
+ "probe_magic_dirs": [
115
+ ".claude",
116
+ ".qoder",
117
+ ".vscode",
118
+ ".add",
119
+ ".trae",
120
+ ".codex"
121
+ ]
122
+ }
123
+ };
124
+
125
+ // templates/core/governance/common.ts
126
+ var EXIT_PASS = protocol.exit_codes.pass;
127
+ var EXIT_BLOCK = protocol.exit_codes.block;
128
+ var STATE_SEP = protocol.output.field_separator;
129
+ function queryPlanStatus() {
130
+ const magicDir = process.env.MAGIC_DIR;
131
+ if (!magicDir) {
132
+ return {
133
+ stdout: '{"availability":"STATUS_UNAVAILABLE","source":"database","reason":"magicDir \u672A\u6CE8\u5165\u4E14\u65E0\u6CD5\u4ECE\u7269\u7406\u4F4D\u7F6E\u63A8\u5BFC"}',
134
+ exitCode: 3
135
+ };
136
+ }
137
+ const bridge = join(
138
+ process.env.PROJECT_DIR || process.cwd(),
139
+ magicDir,
140
+ "scripts",
141
+ "plan-status-bridge.ts"
142
+ );
143
+ if (!existsSync(bridge)) {
144
+ return {
145
+ stdout: '{"availability":"STATUS_UNAVAILABLE","source":"database","reason":"plan-status bridge missing"}',
146
+ exitCode: 3
147
+ };
148
+ }
149
+ const r = spawnSync("node", ["--import", "tsx", bridge], {
150
+ encoding: "utf-8",
151
+ timeout: 1e4
152
+ });
153
+ return { stdout: r.stdout ?? "", exitCode: r.status ?? -1 };
154
+ }
155
+ function detectActiveAdd() {
156
+ const r = queryPlanStatus();
157
+ if (r.exitCode !== 0) {
158
+ let reason = "database status unavailable";
159
+ try {
160
+ const parsed = JSON.parse(r.stdout);
161
+ if (parsed.reason) reason = parsed.reason;
162
+ } catch {
163
+ }
164
+ return `__STATUS_UNAVAILABLE__::${reason}::database::none::none`;
165
+ }
166
+ let snapshot;
167
+ try {
168
+ snapshot = JSON.parse(r.stdout);
169
+ } catch {
170
+ return null;
171
+ }
172
+ if (!(snapshot.availability === "READY" && snapshot.isActive === true)) return null;
173
+ const done = snapshot.progress?.doneTasks ?? 0;
174
+ const total = snapshot.progress?.totalTasks ?? 0;
175
+ const approval = snapshot.approvalStatus ?? "none";
176
+ return `${snapshot.planName}::${done}/${total}::${approval}::none::none`;
177
+ }
178
+ function localIsoSeconds() {
179
+ const d = /* @__PURE__ */ new Date();
180
+ const pad = (n) => String(n).padStart(2, "0");
181
+ const offsetMin = -d.getTimezoneOffset();
182
+ const sign = offsetMin >= 0 ? "+" : "-";
183
+ const offAbs = Math.abs(offsetMin);
184
+ const off = `${sign}${pad(Math.floor(offAbs / 60))}:${pad(offAbs % 60)}`;
185
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}${off}`;
186
+ }
187
+ function projectHash() {
188
+ try {
189
+ return createHash("md5").update(`${process.env.PROJECT_DIR || process.cwd()}
190
+ `).digest("hex").slice(0, 8);
191
+ } catch {
192
+ return "default";
193
+ }
194
+ }
195
+ var DEV_FLAG = `/tmp/add_dev_${projectHash()}`;
196
+ function hasDevAction() {
197
+ return existsSync(DEV_FLAG);
198
+ }
199
+ function clearDevAction() {
200
+ try {
201
+ unlinkSync(DEV_FLAG);
202
+ } catch {
203
+ }
204
+ }
205
+ function checkAddCompleteness(handoff, addRoute) {
206
+ const issues = [];
207
+ if (handoff && existsSync(handoff)) {
208
+ const content = readFileSync(handoff, "utf-8");
209
+ if (!/验收|收敛|闭环|本轮改了什么|devlog/.test(content)) {
210
+ issues.push(" [ ] devlog \u7F3A\u5931\uFF08handoff \u65E0\u9A8C\u6536\u8BB0\u5F55\uFF09");
211
+ }
212
+ const unchecked = (content.match(/\[ \]/g) || []).length;
213
+ if (unchecked > 0) {
214
+ issues.push(` [ ] handoff ${unchecked} \u9879\u672A\u52FE\u9009`);
215
+ }
216
+ }
217
+ if (addRoute && existsSync(addRoute)) {
218
+ const content = readFileSync(addRoute, "utf-8");
219
+ const unchecked = (content.match(/\[ \]/g) || []).length;
220
+ if (unchecked > 0) {
221
+ issues.push(` [ ] add-route ${unchecked} Step \u672A\u95ED\u73AF`);
222
+ }
223
+ }
224
+ return issues;
225
+ }
226
+
227
+ // templates/core/governance/session-end.ts
228
+ function stateField(state, index) {
229
+ return state.split("::")[index] ?? "";
230
+ }
231
+ var SessionEndGuard = class {
232
+ projectDir;
233
+ tplFlag;
234
+ constructor(projectDir) {
235
+ this.projectDir = projectDir ?? process.env.PROJECT_DIR ?? process.cwd();
236
+ this.tplFlag = `/tmp/add_tpl_${projectHash()}`;
237
+ }
238
+ /** ① 清理 tpl-injected 标记 + dev action 标记(对齐 cleanup_tpl_flag/cleanup_dev_flag) */
239
+ cleanupFlags() {
240
+ try {
241
+ unlinkSync2(this.tplFlag);
242
+ } catch {
243
+ }
244
+ clearDevAction();
245
+ }
246
+ /** ② 审计结算(core 默认: stderr 文本;qoder 子类 override: stdout JSON additionalContext) */
247
+ emitSettle() {
248
+ process.stderr.write(`[ADD SessionEnd] \u4F1A\u8BDD\u7ED3\u675F \u2014 ${localIsoSeconds()}
249
+ `);
250
+ }
251
+ /** ② 审计结算(输出到 stderr 供日志记录,对齐 bash date -Iseconds) */
252
+ settle() {
253
+ this.emitSettle();
254
+ }
255
+ /** ③ Stop 未触发兜底(对齐 stop_fallback:dev action 标记还在 → 补 checklist 快照) */
256
+ stopFallback() {
257
+ if (!hasDevAction()) return;
258
+ 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");
259
+ const state = detectActiveAdd();
260
+ if (state === null) return;
261
+ const handoff = stateField(state, 3);
262
+ const addRoute = stateField(state, 4);
263
+ if (handoff && handoff !== "none") {
264
+ process.stderr.write("[ADD SessionEnd] \u8865\u6267\u884C checklist \u5FEB\u7167\uFF08best-effort\uFF0C\u4E0D\u963B\u65AD\uFF09\n");
265
+ const issues = checkAddCompleteness(handoff, addRoute !== "none" ? addRoute : "");
266
+ for (const issue of issues) {
267
+ process.stderr.write(issue + "\n");
268
+ }
269
+ }
270
+ }
271
+ /** 主入口:①清理 → ②结算 → ③兜底(对齐 bash main) */
272
+ run() {
273
+ this.cleanupFlags();
274
+ this.settle();
275
+ this.stopFallback();
276
+ return 0;
277
+ }
278
+ };
279
+
280
+ // templates/adapters/claude/hooks/lib/claude-env.ts
281
+ function resolveClaudeProjectDir() {
282
+ return process.env.CLAUDE_PROJECT_DIR || process.cwd();
283
+ }
284
+
285
+ // templates/adapters/claude/hooks/session-end.ts
286
+ process.env.PROJECT_DIR = resolveClaudeProjectDir();
287
+ process.exit(new SessionEndGuard(process.env.PROJECT_DIR).run());