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.
- package/package.json +1 -1
- package/templates/.add-coder-src-hash.json +73 -1
- package/templates/adapters/claude/hooks/doc-format-guard.mjs +850 -0
- package/templates/adapters/claude/hooks/notification.mjs +256 -0
- package/templates/adapters/claude/hooks/permission-denied.mjs +167 -0
- package/templates/adapters/claude/hooks/permission-gate.mjs +150 -0
- package/templates/adapters/claude/hooks/post-tool-failure.mjs +3 -0
- package/templates/adapters/claude/hooks/post-tool-use.mjs +413 -0
- package/templates/adapters/claude/hooks/pre-compact.mjs +226 -0
- package/templates/adapters/claude/hooks/pre-tool-use.mjs +914 -0
- package/templates/adapters/claude/hooks/prompt-submit.mjs +847 -0
- package/templates/adapters/claude/hooks/review-checklist.mjs +3 -0
- package/templates/adapters/claude/hooks/session-end.mjs +287 -0
- package/templates/adapters/claude/hooks/session-start.mjs +566 -0
- package/templates/adapters/claude/hooks/stop-check.mjs +505 -0
- package/templates/adapters/claude/hooks/stop-failure.mjs +222 -0
- package/templates/adapters/claude/hooks/subagent-guard.mjs +224 -0
- package/templates/adapters/claude/hooks/subagent-stop.mjs +277 -0
- package/templates/adapters/codex/hooks/doc-format-guard.mjs +849 -0
- package/templates/adapters/codex/hooks/notification.mjs +254 -0
- package/templates/adapters/codex/hooks/permission-gate.mjs +169 -0
- package/templates/adapters/codex/hooks/post-tool-failure.mjs +176 -0
- package/templates/adapters/codex/hooks/post-tool-use.mjs +575 -0
- package/templates/adapters/codex/hooks/pre-compact.mjs +328 -0
- package/templates/adapters/codex/hooks/pre-tool-use.mjs +963 -0
- package/templates/adapters/codex/hooks/prompt-submit.mjs +623 -0
- package/templates/adapters/codex/hooks/review-checklist.mjs +3 -0
- package/templates/adapters/codex/hooks/session-end.mjs +389 -0
- package/templates/adapters/codex/hooks/session-start.mjs +583 -0
- package/templates/adapters/codex/hooks/stop-check.mjs +539 -0
- package/templates/adapters/codex/hooks/subagent-guard.mjs +218 -0
- package/templates/adapters/codex/hooks/subagent-stop.mjs +381 -0
- package/templates/adapters/qoder/hooks/doc-format-guard.mjs +858 -0
- package/templates/adapters/qoder/hooks/notification.mjs +269 -0
- package/templates/adapters/qoder/hooks/permission-gate.mjs +284 -0
- package/templates/adapters/qoder/hooks/post-tool-failure.mjs +294 -0
- package/templates/adapters/qoder/hooks/post-tool-use.mjs +535 -0
- package/templates/adapters/qoder/hooks/pre-compact.mjs +234 -0
- package/templates/adapters/qoder/hooks/pre-tool-use.mjs +948 -0
- package/templates/adapters/qoder/hooks/prompt-submit.mjs +789 -0
- package/templates/adapters/qoder/hooks/review-checklist.mjs +409 -0
- package/templates/adapters/qoder/hooks/session-end.mjs +301 -0
- package/templates/adapters/qoder/hooks/session-start.mjs +619 -0
- package/templates/adapters/qoder/hooks/stop-check.mjs +567 -0
- package/templates/adapters/qoder/hooks/subagent-guard.mjs +219 -0
- package/templates/adapters/qoder/hooks/subagent-stop.mjs +298 -0
- package/templates/adapters/trae/hooks/doc-format-guard.mjs +849 -0
- package/templates/adapters/trae/hooks/notification.mjs +251 -0
- package/templates/adapters/trae/hooks/permission-gate.mjs +169 -0
- package/templates/adapters/trae/hooks/post-tool-failure.mjs +176 -0
- package/templates/adapters/trae/hooks/post-tool-use.mjs +415 -0
- package/templates/adapters/trae/hooks/pre-compact.mjs +224 -0
- package/templates/adapters/trae/hooks/pre-tool-use.mjs +873 -0
- package/templates/adapters/trae/hooks/prompt-submit.mjs +614 -0
- package/templates/adapters/trae/hooks/review-checklist.mjs +3 -0
- package/templates/adapters/trae/hooks/session-end.mjs +285 -0
- package/templates/adapters/trae/hooks/session-start.mjs +555 -0
- package/templates/adapters/trae/hooks/stop-check.mjs +502 -0
- package/templates/adapters/trae/hooks/subagent-guard.mjs +218 -0
- package/templates/adapters/trae/hooks/subagent-stop.mjs +274 -0
- package/templates/adapters/vscode/hooks/doc-format-guard.mjs +847 -0
- package/templates/adapters/vscode/hooks/notification.mjs +251 -0
- package/templates/adapters/vscode/hooks/permission-gate.mjs +169 -0
- package/templates/adapters/vscode/hooks/post-tool-failure.mjs +192 -0
- package/templates/adapters/vscode/hooks/post-tool-use.mjs +415 -0
- package/templates/adapters/vscode/hooks/pre-compact.mjs +224 -0
- package/templates/adapters/vscode/hooks/pre-tool-use.mjs +906 -0
- package/templates/adapters/vscode/hooks/prompt-submit.mjs +838 -0
- package/templates/adapters/vscode/hooks/review-checklist.mjs +3 -0
- package/templates/adapters/vscode/hooks/session-end.mjs +285 -0
- package/templates/adapters/vscode/hooks/session-start.mjs +560 -0
- package/templates/adapters/vscode/hooks/stop-check.mjs +502 -0
- package/templates/adapters/vscode/hooks/subagent-guard.mjs +218 -0
- package/templates/adapters/vscode/hooks/subagent-stop.mjs +274 -0
|
@@ -0,0 +1,269 @@
|
|
|
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/qoder/hooks/lib/qoder-env.ts
|
|
242
|
+
function resolveQoderProjectDir() {
|
|
243
|
+
return process.env.CLAUDE_PROJECT_DIR || process.env.QODER_PROJECT_DIR || process.env.QODERCN_PROJECT_DIR || process.cwd();
|
|
244
|
+
}
|
|
245
|
+
function injectProjectDir() {
|
|
246
|
+
const dir = resolveQoderProjectDir();
|
|
247
|
+
process.env.PROJECT_DIR = dir;
|
|
248
|
+
return dir;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// templates/adapters/qoder/hooks/notification.ts
|
|
252
|
+
var QoderNotificationRouter = class extends NotificationRouter {
|
|
253
|
+
/** 协议差异封装: fallbackMagicDir = ".qoder"(qoder 端兜底,bash 原文硬编码语义) */
|
|
254
|
+
constructor(projectDir) {
|
|
255
|
+
super(projectDir, ".qoder");
|
|
256
|
+
}
|
|
257
|
+
/** ① qoder bash 原文硬编码 ${PROJECT_DIR}/.qoder/reviews——无跨端探测循环(协议差异,不做探测) */
|
|
258
|
+
resolveMagicDir() {
|
|
259
|
+
return process.env.MAGIC_DIR ?? ".qoder";
|
|
260
|
+
}
|
|
261
|
+
/** ② qoder 提醒文本: 无 Plan 前缀(bash 原文逐字) */
|
|
262
|
+
emitReminder(_plan, reviewsDir) {
|
|
263
|
+
process.stdout.write(`[ADD Notification] \u8BF7\u68C0\u67E5 Review \u6587\u6863: ${reviewsDir}
|
|
264
|
+
`);
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
var input = readHookInput();
|
|
268
|
+
var PROJECT_DIR = injectProjectDir();
|
|
269
|
+
process.exit(new QoderNotificationRouter(PROJECT_DIR).run(input));
|
|
@@ -0,0 +1,284 @@
|
|
|
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 { 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
|
+
// node_modules/.pnpm/find-up@8.0.0/node_modules/find-up/index.js
|
|
123
|
+
import path2 from "node:path";
|
|
124
|
+
|
|
125
|
+
// node_modules/.pnpm/locate-path@8.0.0/node_modules/locate-path/index.js
|
|
126
|
+
import process2 from "node:process";
|
|
127
|
+
import path from "node:path";
|
|
128
|
+
import fs, { promises as fsPromises } from "node:fs";
|
|
129
|
+
import { fileURLToPath } from "node:url";
|
|
130
|
+
var typeMappings = {
|
|
131
|
+
directory: "isDirectory",
|
|
132
|
+
file: "isFile"
|
|
133
|
+
};
|
|
134
|
+
function checkType(type) {
|
|
135
|
+
if (type === "both" || Object.hasOwn(typeMappings, type)) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
throw new Error(`Invalid type specified: ${type}`);
|
|
139
|
+
}
|
|
140
|
+
var matchType = (type, stat) => type === "both" ? stat.isFile() || stat.isDirectory() : stat[typeMappings[type]]();
|
|
141
|
+
var toPath = (urlOrPath) => urlOrPath instanceof URL ? fileURLToPath(urlOrPath) : urlOrPath;
|
|
142
|
+
function locatePathSync(paths, {
|
|
143
|
+
cwd = process2.cwd(),
|
|
144
|
+
type = "file",
|
|
145
|
+
allowSymlinks = true
|
|
146
|
+
} = {}) {
|
|
147
|
+
checkType(type);
|
|
148
|
+
cwd = toPath(cwd);
|
|
149
|
+
const statFunction = allowSymlinks ? fs.statSync : fs.lstatSync;
|
|
150
|
+
for (const path_ of paths) {
|
|
151
|
+
try {
|
|
152
|
+
const stat = statFunction(path.resolve(cwd, path_), {
|
|
153
|
+
throwIfNoEntry: false
|
|
154
|
+
});
|
|
155
|
+
if (!stat) {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (matchType(type, stat)) {
|
|
159
|
+
return path_;
|
|
160
|
+
}
|
|
161
|
+
} catch {
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// node_modules/.pnpm/unicorn-magic@0.3.0/node_modules/unicorn-magic/node.js
|
|
167
|
+
import { promisify } from "node:util";
|
|
168
|
+
import { execFile as execFileCallback, execFileSync as execFileSyncOriginal } from "node:child_process";
|
|
169
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
170
|
+
var execFileOriginal = promisify(execFileCallback);
|
|
171
|
+
function toPath2(urlOrPath) {
|
|
172
|
+
return urlOrPath instanceof URL ? fileURLToPath2(urlOrPath) : urlOrPath;
|
|
173
|
+
}
|
|
174
|
+
var TEN_MEGABYTES_IN_BYTES = 10 * 1024 * 1024;
|
|
175
|
+
|
|
176
|
+
// node_modules/.pnpm/find-up@8.0.0/node_modules/find-up/index.js
|
|
177
|
+
var findUpStop = /* @__PURE__ */ Symbol("findUpStop");
|
|
178
|
+
function findUpMultipleSync(name, options = {}) {
|
|
179
|
+
let directory = path2.resolve(toPath2(options.cwd) ?? "");
|
|
180
|
+
const { root } = path2.parse(directory);
|
|
181
|
+
const stopAt = path2.resolve(directory, toPath2(options.stopAt) ?? root);
|
|
182
|
+
const limit = options.limit ?? Number.POSITIVE_INFINITY;
|
|
183
|
+
const paths = [name].flat();
|
|
184
|
+
const runMatcher = (locateOptions) => {
|
|
185
|
+
if (typeof name !== "function") {
|
|
186
|
+
return locatePathSync(paths, locateOptions);
|
|
187
|
+
}
|
|
188
|
+
const foundPath = name(locateOptions.cwd);
|
|
189
|
+
if (typeof foundPath === "string") {
|
|
190
|
+
return locatePathSync([foundPath], locateOptions);
|
|
191
|
+
}
|
|
192
|
+
return foundPath;
|
|
193
|
+
};
|
|
194
|
+
const matches = [];
|
|
195
|
+
while (true) {
|
|
196
|
+
const foundPath = runMatcher({ ...options, cwd: directory });
|
|
197
|
+
if (foundPath === findUpStop) {
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
if (foundPath) {
|
|
201
|
+
matches.push(path2.resolve(directory, foundPath));
|
|
202
|
+
}
|
|
203
|
+
if (directory === stopAt || matches.length >= limit) {
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
directory = path2.dirname(directory);
|
|
207
|
+
}
|
|
208
|
+
return matches;
|
|
209
|
+
}
|
|
210
|
+
function findUpSync(name, options = {}) {
|
|
211
|
+
const matches = findUpMultipleSync(name, { ...options, limit: 1 });
|
|
212
|
+
return matches[0];
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// templates/core/governance/common.ts
|
|
216
|
+
var EXIT_PASS = protocol.exit_codes.pass;
|
|
217
|
+
var EXIT_BLOCK = protocol.exit_codes.block;
|
|
218
|
+
var STATE_SEP = protocol.output.field_separator;
|
|
219
|
+
function readHookInput() {
|
|
220
|
+
if (process.stdin.isTTY) return "{}";
|
|
221
|
+
return readFileSync(0, "utf-8");
|
|
222
|
+
}
|
|
223
|
+
function tryResolveMagicDir() {
|
|
224
|
+
const injected = process.env.MAGIC_DIR;
|
|
225
|
+
if (injected) return injected;
|
|
226
|
+
const startDir = dirname(fileURLToPath3(import.meta.url));
|
|
227
|
+
const hit = findUpSync((dir) => basename(dir).startsWith(".") ? dir : void 0, {
|
|
228
|
+
cwd: startDir,
|
|
229
|
+
type: "directory"
|
|
230
|
+
});
|
|
231
|
+
return hit ? basename(hit) : "";
|
|
232
|
+
}
|
|
233
|
+
function projectHash() {
|
|
234
|
+
try {
|
|
235
|
+
return createHash("md5").update(`${process.env.PROJECT_DIR || process.cwd()}
|
|
236
|
+
`).digest("hex").slice(0, 8);
|
|
237
|
+
} catch {
|
|
238
|
+
return "default";
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
var DEV_FLAG = `/tmp/add_dev_${projectHash()}`;
|
|
242
|
+
|
|
243
|
+
// templates/core/governance/permission-gate-router.ts
|
|
244
|
+
function fieldOr(json, field, fallback) {
|
|
245
|
+
const re = new RegExp(`"${field}"\\s*:\\s*"([^"]*)"`);
|
|
246
|
+
const m = re.exec(json);
|
|
247
|
+
return m ? m[1] : fallback;
|
|
248
|
+
}
|
|
249
|
+
var PermissionGateRouter = class {
|
|
250
|
+
/** 主路由:返回 exit code(0) */
|
|
251
|
+
run(input2) {
|
|
252
|
+
const toolName = input2.trim() === "" ? "" : fieldOr(input2, "tool_name", this.fallbackToolName());
|
|
253
|
+
this.emit(toolName);
|
|
254
|
+
return 0;
|
|
255
|
+
}
|
|
256
|
+
/** tool_name 缺失回退(core: ""——bash `// empty` 语义;qoder 子类 override: "unknown") */
|
|
257
|
+
fallbackToolName() {
|
|
258
|
+
return "";
|
|
259
|
+
}
|
|
260
|
+
/** 输出(core 默认: 仅高风险工具 stdout 二次确认提示;qoder 子类 override: 全量 stderr 日志) */
|
|
261
|
+
emit(toolName) {
|
|
262
|
+
if (toolName === "Bash" || toolName === "Write" || toolName === "Edit") {
|
|
263
|
+
process.stdout.write(`[ADD PermissionGate] \u9AD8\u98CE\u9669\u5DE5\u5177: ${toolName}\uFF0C\u8BF7\u786E\u8BA4\u64CD\u4F5C\u3002
|
|
264
|
+
`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
// templates/adapters/qoder/hooks/permission-gate.ts
|
|
270
|
+
var QoderPermissionGateRouter = class extends PermissionGateRouter {
|
|
271
|
+
/** ① tool_name 缺失回退 "unknown"(bash jq `// \"unknown\"` 语义) */
|
|
272
|
+
fallbackToolName() {
|
|
273
|
+
return "unknown";
|
|
274
|
+
}
|
|
275
|
+
/** ② qoder 协议: 全量工具 stderr 日志(bash 原文逐字) */
|
|
276
|
+
emit(toolName) {
|
|
277
|
+
process.stderr.write(`[ADD PermissionRequest] \u5DE5\u5177 ${toolName} \u8BF7\u6C42\u6743\u9650\u3002\u5982\u6709 Review \u6587\u6863\u5F85\u786E\u8BA4\uFF0C\u8BF7\u5148\u68C0\u67E5\u3002
|
|
278
|
+
`);
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
var inferred = tryResolveMagicDir();
|
|
282
|
+
if (inferred && !process.env.MAGIC_DIR) process.env.MAGIC_DIR = inferred;
|
|
283
|
+
var input = readHookInput();
|
|
284
|
+
process.exit(new QoderPermissionGateRouter().run(input));
|