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,409 @@
1
+ // templates/core/governance/review-checklist-guard.ts
2
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
3
+ import { join as join2, basename as basename2 } from "node:path";
4
+
5
+ // templates/core/governance/common.ts
6
+ import { basename, dirname, join } from "node:path";
7
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
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
+ // node_modules/.pnpm/find-up@8.0.0/node_modules/find-up/index.js
126
+ import path2 from "node:path";
127
+
128
+ // node_modules/.pnpm/locate-path@8.0.0/node_modules/locate-path/index.js
129
+ import process2 from "node:process";
130
+ import path from "node:path";
131
+ import fs, { promises as fsPromises } from "node:fs";
132
+ import { fileURLToPath } from "node:url";
133
+ var typeMappings = {
134
+ directory: "isDirectory",
135
+ file: "isFile"
136
+ };
137
+ function checkType(type) {
138
+ if (type === "both" || Object.hasOwn(typeMappings, type)) {
139
+ return;
140
+ }
141
+ throw new Error(`Invalid type specified: ${type}`);
142
+ }
143
+ var matchType = (type, stat) => type === "both" ? stat.isFile() || stat.isDirectory() : stat[typeMappings[type]]();
144
+ var toPath = (urlOrPath) => urlOrPath instanceof URL ? fileURLToPath(urlOrPath) : urlOrPath;
145
+ function locatePathSync(paths, {
146
+ cwd = process2.cwd(),
147
+ type = "file",
148
+ allowSymlinks = true
149
+ } = {}) {
150
+ checkType(type);
151
+ cwd = toPath(cwd);
152
+ const statFunction = allowSymlinks ? fs.statSync : fs.lstatSync;
153
+ for (const path_ of paths) {
154
+ try {
155
+ const stat = statFunction(path.resolve(cwd, path_), {
156
+ throwIfNoEntry: false
157
+ });
158
+ if (!stat) {
159
+ continue;
160
+ }
161
+ if (matchType(type, stat)) {
162
+ return path_;
163
+ }
164
+ } catch {
165
+ }
166
+ }
167
+ }
168
+
169
+ // node_modules/.pnpm/unicorn-magic@0.3.0/node_modules/unicorn-magic/node.js
170
+ import { promisify } from "node:util";
171
+ import { execFile as execFileCallback, execFileSync as execFileSyncOriginal } from "node:child_process";
172
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
173
+ var execFileOriginal = promisify(execFileCallback);
174
+ function toPath2(urlOrPath) {
175
+ return urlOrPath instanceof URL ? fileURLToPath2(urlOrPath) : urlOrPath;
176
+ }
177
+ var TEN_MEGABYTES_IN_BYTES = 10 * 1024 * 1024;
178
+
179
+ // node_modules/.pnpm/find-up@8.0.0/node_modules/find-up/index.js
180
+ var findUpStop = /* @__PURE__ */ Symbol("findUpStop");
181
+ function findUpMultipleSync(name, options = {}) {
182
+ let directory = path2.resolve(toPath2(options.cwd) ?? "");
183
+ const { root } = path2.parse(directory);
184
+ const stopAt = path2.resolve(directory, toPath2(options.stopAt) ?? root);
185
+ const limit = options.limit ?? Number.POSITIVE_INFINITY;
186
+ const paths = [name].flat();
187
+ const runMatcher = (locateOptions) => {
188
+ if (typeof name !== "function") {
189
+ return locatePathSync(paths, locateOptions);
190
+ }
191
+ const foundPath = name(locateOptions.cwd);
192
+ if (typeof foundPath === "string") {
193
+ return locatePathSync([foundPath], locateOptions);
194
+ }
195
+ return foundPath;
196
+ };
197
+ const matches = [];
198
+ while (true) {
199
+ const foundPath = runMatcher({ ...options, cwd: directory });
200
+ if (foundPath === findUpStop) {
201
+ break;
202
+ }
203
+ if (foundPath) {
204
+ matches.push(path2.resolve(directory, foundPath));
205
+ }
206
+ if (directory === stopAt || matches.length >= limit) {
207
+ break;
208
+ }
209
+ directory = path2.dirname(directory);
210
+ }
211
+ return matches;
212
+ }
213
+ function findUpSync(name, options = {}) {
214
+ const matches = findUpMultipleSync(name, { ...options, limit: 1 });
215
+ return matches[0];
216
+ }
217
+
218
+ // templates/core/governance/common.ts
219
+ var EXIT_PASS = protocol.exit_codes.pass;
220
+ var EXIT_BLOCK = protocol.exit_codes.block;
221
+ var STATE_SEP = protocol.output.field_separator;
222
+ function tryResolveMagicDir() {
223
+ const injected = process.env.MAGIC_DIR;
224
+ if (injected) return injected;
225
+ const startDir = dirname(fileURLToPath3(import.meta.url));
226
+ const hit = findUpSync((dir) => basename(dir).startsWith(".") ? dir : void 0, {
227
+ cwd: startDir,
228
+ type: "directory"
229
+ });
230
+ return hit ? basename(hit) : "";
231
+ }
232
+ function projectHash() {
233
+ try {
234
+ return createHash("md5").update(`${process.env.PROJECT_DIR || process.cwd()}
235
+ `).digest("hex").slice(0, 8);
236
+ } catch {
237
+ return "default";
238
+ }
239
+ }
240
+ var DEV_FLAG = `/tmp/add_dev_${projectHash()}`;
241
+
242
+ // templates/core/governance/review-checklist-guard.ts
243
+ function checkedLines(content) {
244
+ return content.split("\n").filter((l) => l.includes("[x]"));
245
+ }
246
+ function locateSpecFile(specRoot, planKw, name) {
247
+ const specDir = join2(specRoot, planKw);
248
+ const direct = join2(specDir, name);
249
+ if (existsSync(direct)) return direct;
250
+ if (existsSync(specRoot)) {
251
+ for (const dir of readdirSync(specRoot)) {
252
+ if (!dir.includes(planKw)) continue;
253
+ const cand = join2(specRoot, dir, name);
254
+ if (existsSync(cand)) return cand;
255
+ }
256
+ }
257
+ return direct;
258
+ }
259
+ function defaultSpecRoot() {
260
+ const projectDir = process.env.PROJECT_DIR || process.cwd();
261
+ const magicDir = tryResolveMagicDir();
262
+ return magicDir ? join2(projectDir, magicDir, "specs") : "";
263
+ }
264
+ function checkReviewQuality(handoff2, addRoute2, specRoot) {
265
+ if (handoff2 === "") return "";
266
+ const projectDir = process.env.PROJECT_DIR || process.cwd();
267
+ const planKw = basename2(handoff2).replace(/-handoff.*$/, "");
268
+ const root = specRoot ?? defaultSpecRoot();
269
+ const checklist = locateSpecFile(root, planKw, "checklist.md");
270
+ const tasksFile = locateSpecFile(root, planKw, "tasks.md");
271
+ let issues = "";
272
+ let missingDocs = "";
273
+ if (!existsSync(handoff2)) {
274
+ missingDocs += " Handoff";
275
+ } else {
276
+ const h = readFileSync(handoff2, "utf-8");
277
+ let hSections = 0;
278
+ if (h.includes("spec \u6587\u4EF6")) hSections++;
279
+ if (h.includes("\u4F60\u8981\u6539\u7684\u6587\u4EF6")) hSections++;
280
+ if (h.includes("\u9A8C\u8BC1\u6807\u51C6")) hSections++;
281
+ if (h.includes("\u5B8C\u6210\u540E\u8BB0\u5F55 ADD-7 \u5BA1\u8BA1")) hSections++;
282
+ if (hSections < 4) missingDocs += ` Handoff(\u7F3A\u7AE0\u8282:${hSections}/4)`;
283
+ }
284
+ if (!existsSync(addRoute2)) {
285
+ missingDocs += " add-route";
286
+ } else {
287
+ const ar = readFileSync(addRoute2, "utf-8");
288
+ if (!ar.includes("Task \u6620\u5C04\u8868") && !ar.includes("\u6587\u4EF6\u6E05\u5355")) {
289
+ missingDocs += " add-route(\u7F3ATask\u6620\u5C04/\u6587\u4EF6\u6E05\u5355)";
290
+ }
291
+ }
292
+ if (!existsSync(checklist)) {
293
+ missingDocs += " checklist";
294
+ } else {
295
+ const cl = readFileSync(checklist, "utf-8");
296
+ if (!cl.includes("[T]")) missingDocs += " checklist(\u65E0[T]\u9879)";
297
+ }
298
+ if (!existsSync(tasksFile)) {
299
+ missingDocs += " tasks";
300
+ } else {
301
+ const tk = readFileSync(tasksFile, "utf-8");
302
+ if (!/Task|\[ \]/.test(tk)) missingDocs += " tasks(\u65E0Task\u9879)";
303
+ }
304
+ if (missingDocs !== "") {
305
+ return ` \u274C Step 0 \u672A\u5B8C\u6210:${missingDocs.trim().replace(/ /g, ", ")} \u6587\u4EF6\u7F3A\u5931\u3002\u56DE\u9000 Step 0.5/Step 1 \u8865\u5EFA\u540E\u518D\u8FDB\u5165\u4EE3\u7801\u5B9E\u73B0\u3002`;
306
+ }
307
+ if (existsSync(checklist) && existsSync(tasksFile)) {
308
+ const cl = readFileSync(checklist, "utf-8");
309
+ const clChecked = checkedLines(cl).length;
310
+ const clOpen = (cl.match(/\[ \]/g) || []).length;
311
+ const tkChecked = (readFileSync(tasksFile, "utf-8").match(/\[x\]/g) || []).length;
312
+ if (clOpen > 0) {
313
+ issues += ` \u26A0\uFE0F checklist \u6709 ${clOpen} \u9879\u672A\u52FE\u9009
314
+ `;
315
+ }
316
+ const tItems = (cl.match(/\[T\]/g) || []).length;
317
+ const tChecked = checkedLines(cl).filter((l) => l.includes("[T]")).length;
318
+ if (tItems > tChecked) {
319
+ issues += ` \u26A0\uFE0F [T] \u7F16\u8BD1\u671F\u9A8C\u8BC1: ${tChecked}/${tItems} \u901A\u8FC7
320
+ `;
321
+ }
322
+ let noEvidence = 0;
323
+ for (const line of checkedLines(cl)) {
324
+ if (/—\s*证据:\s*\S/.test(line)) continue;
325
+ if (/npx|tsc|vitest|grep|✅|cmq[0-9a-z]{10}|18\/18|exit.*0/.test(line)) continue;
326
+ noEvidence++;
327
+ }
328
+ if (noEvidence > 0) {
329
+ issues += ` \u274C ${noEvidence} \u9879 [x] \u7F3A\u5C11\u9A8C\u6536\u8BC1\u636E\uFF08\u9700\u9644 \u2014 \u8BC1\u636E: tsc/vitest/grep/\u5BA1\u8BA1ID \u7B49\uFF09
330
+ `;
331
+ }
332
+ const withAudit = checkedLines(cl).filter((l) => /cmq[a-z0-9]{10,}/.test(l)).length;
333
+ const fakeAudit = checkedLines(cl).filter((l) => /cmq\.\.\.|cmqxxx|审计.*cmq\.\./.test(l)).length;
334
+ if (fakeAudit > 0) {
335
+ issues += ` \u274C ${fakeAudit} \u9879 [x] \u4F7F\u7528\u4E86\u5360\u4F4D\u7B26\u5BA1\u8BA1ID\uFF08cmq.../cmqxxx\uFF09\uFF0C\u5FC5\u987B\u8C03 record_dev_operation \u83B7\u53D6\u771F\u5B9E cuid \u540E\u66FF\u6362
336
+ `;
337
+ }
338
+ const withEvidence = checkedLines(cl).filter(
339
+ (l) => /tsc|vitest|npx|grep|✅|验证|确认|compgen|审计.*cmq[a-z0-9]{10}/.test(l)
340
+ ).length;
341
+ if (withEvidence > 0 && withAudit === 0) {
342
+ issues += ` \u{1F4CE} \u521D\u9A8C: ${withEvidence}/${clChecked} \u9879\u6709\u8BC1\u636E\u4F46\u672A\u5199\u5BA1\u8BA1 ID\uFF08\u9700\u8C03 record_dev_operation \u843D\u5E93\uFF09
343
+ `;
344
+ } else if (withAudit > 0 && withAudit < clChecked) {
345
+ issues += ` \u{1F4CE} \u590D\u9A8C: ${withAudit}/${clChecked} \u9879\u5F15\u7528\u5BA1\u8BA1 ID\u3002${withEvidence}/${clChecked} \u9879\u6709\u8BC1\u636E\u3002\u8BC1\u636E\u4E00\u81F4\u5219\u4E0D\u9700\u8FFD\u5199 devlog\u65E5\u5FD7(\u8D70mcp)
346
+ `;
347
+ }
348
+ }
349
+ if (existsSync(addRoute2)) {
350
+ const ar = readFileSync(addRoute2, "utf-8");
351
+ const arOpen = (ar.match(/\[ \]/g) || []).length;
352
+ if (arOpen > 0) {
353
+ issues += ` \u26A0\uFE0F add-route ${arOpen} Step \u672A\u95ED\u73AF
354
+ `;
355
+ }
356
+ }
357
+ if (existsSync(handoff2) && existsSync(checklist)) {
358
+ const h = readFileSync(handoff2, "utf-8");
359
+ const cl = readFileSync(checklist, "utf-8");
360
+ const cuids = [...new Set(cl.match(/cmq[a-z0-9]{10,}/g) || [])];
361
+ const newCuids = cuids.filter((c) => !h.includes(c));
362
+ if (newCuids.length > 0) {
363
+ issues += ` \u274C handoff \u5BA1\u8BA1\u8868\u672A\u540C\u6B65: ${newCuids.length} \u4E2A cuid \u5728 checklist \u4E2D\u5B58\u5728\u4F46 handoff \u4E2D\u7F3A\u5931\uFF08\u9700\u66F4\u65B0 handoff ADD-7 \u8868 + query_audit_logs \u547D\u4EE4\uFF09
364
+ `;
365
+ }
366
+ }
367
+ if (issues === "") {
368
+ return " \u2705 Review: checklist \u8D28\u91CF\u68C0\u67E5\u901A\u8FC7";
369
+ }
370
+ return ` \u{1F4CB} Review \u53D1\u73B0\u95EE\u9898:
371
+ ${issues}`;
372
+ }
373
+ var ReviewChecklistGuard = class {
374
+ specRoot;
375
+ constructor(specRoot) {
376
+ this.specRoot = specRoot;
377
+ }
378
+ /** 主入口:返回 exit code(0 放行 / 1 Step 0 准入失败) */
379
+ run(handoff2, addRoute2) {
380
+ if (handoff2 === "") return 0;
381
+ const out = checkReviewQuality(handoff2, addRoute2, this.specRoot);
382
+ process.stdout.write(out + "\n");
383
+ if (out.startsWith(" \u274C Step 0 \u672A\u5B8C\u6210")) return 1;
384
+ return 0;
385
+ }
386
+ };
387
+
388
+ // templates/adapters/qoder/hooks/lib/qoder-env.ts
389
+ function resolveQoderProjectDir() {
390
+ return process.env.CLAUDE_PROJECT_DIR || process.env.QODER_PROJECT_DIR || process.env.QODERCN_PROJECT_DIR || process.cwd();
391
+ }
392
+ function injectProjectDir() {
393
+ const dir = resolveQoderProjectDir();
394
+ process.env.PROJECT_DIR = dir;
395
+ return dir;
396
+ }
397
+
398
+ // templates/adapters/qoder/hooks/review-checklist.ts
399
+ var QoderReviewChecklistGuard = class extends ReviewChecklistGuard {
400
+ /** 协议差异封装: specRoot = ".qoder/specs"(qoder 端 spec 目录) */
401
+ constructor() {
402
+ super(".qoder/specs");
403
+ }
404
+ // 当前无 override(Step 0 准入 exit 1 语义与 core 基线一致);命名子类承载端身份 + 未来演进位
405
+ };
406
+ injectProjectDir();
407
+ var handoff = process.argv[2] ?? "";
408
+ var addRoute = process.argv[3] ?? "";
409
+ process.exit(new QoderReviewChecklistGuard().run(handoff, addRoute));