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,906 @@
|
|
|
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 { spawnSync } from "node:child_process";
|
|
6
|
+
import { createHash } from "node:crypto";
|
|
7
|
+
|
|
8
|
+
// templates/core/governance/rules.ts
|
|
9
|
+
var guard = {
|
|
10
|
+
"detectors": [
|
|
11
|
+
{
|
|
12
|
+
"id": "dangerous-command",
|
|
13
|
+
"regex": "rm\\s+-rf\\s+/|DROP\\s+TABLE|git\\s+push\\s+--force|mkfs\\.|dd\\s+if=",
|
|
14
|
+
"flags": "i",
|
|
15
|
+
"reason": "\u5371\u9669\u547D\u4EE4\u5DF2\u88AB\u963B\u6B62",
|
|
16
|
+
"stderr": "\u26D4 \u5371\u9669\u547D\u4EE4\u5DF2\u88AB\u963B\u6B62: {{cmd}}\n"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "script-interpreter",
|
|
20
|
+
"regex": "(^|;|\\|\\||&&|\\|)\\s*(python3?|node|ruby|perl|php)(\\s|$)",
|
|
21
|
+
"reason": "\u7981\u6B62\u901A\u8FC7\u811A\u672C\u89E3\u91CA\u5668\u76F4\u63A5\u4FEE\u6539\u6587\u4EF6\u3002\u8BF7\u4F7F\u7528 Write \u6216 SearchReplace \u5DE5\u5177\u64CD\u4F5C\u6587\u4EF6\u3002",
|
|
22
|
+
"stderr": "\u26D4 [ADD PreToolUse \xA7A] \u963B\u65AD: \u7981\u6B62\u901A\u8FC7\u811A\u672C\u89E3\u91CA\u5668\u76F4\u63A5\u4FEE\u6539\u6587\u4EF6\u3002\n\n python/node/ruby/perl/php \u53EF\u5728\u811A\u672C\u4E2D\u5199\u5165\u4EFB\u610F\u6587\u4EF6\uFF0C\u7ED5\u8FC7:\n \xB7 Plan \u5173\u8054\u68C0\u67E5\uFF08\u54EA\u4E2A\u6587\u4EF6\u5C5E\u4E8E\u54EA\u4E2A ADD Plan\uFF1F\uFF09\n \xB7 doc-format-guard\uFF08\u7AE0\u8282/\u5360\u4F4D\u7B26/\u7981\u6B62\u8BCD\u6821\u9A8C\uFF09\n \xB7 \u5BA1\u8BA1\u8FFD\u8E2A\uFF08agentAudit \u65E0\u6CD5\u611F\u77E5 Bash \u5185\u90E8\u7684\u6587\u4EF6\u53D8\u66F4\uFF09\n\n \u2192 \u8BF7\u6539\u7528 Write \u6216 SearchReplace \u5DE5\u5177\u64CD\u4F5C\u6587\u4EF6\u3002\n \u2192 \u5982\u9700\u8FD0\u884C\u6784\u5EFA/\u6D4B\u8BD5\u811A\u672C\uFF0C\u4F7F\u7528 npx/pnpm/npm \u547D\u4EE4\u3002\n"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": "sed-in-place",
|
|
26
|
+
"regex": "(^|[;&|][ \\t]*)sed[ \\t]+([^;&|]*[ \\t])?(-[A-Za-z]*i[^ \\t;&|]*|--in-place(=[^ \\t;&|]*)?)([ \\t;&|]|$)",
|
|
27
|
+
"reason": "\u7981\u6B62\u901A\u8FC7 sed -i \u76F4\u63A5\u7F16\u8F91\u6587\u4EF6\u3002\u8BF7\u4F7F\u7528 SearchReplace \u5DE5\u5177\u3002",
|
|
28
|
+
"stderr": "\u26D4 [ADD PreToolUse \xA7A] \u963B\u65AD: \u7981\u6B62\u901A\u8FC7 sed -i \u539F\u5730\u7F16\u8F91\u6587\u4EF6\u3002\n\n sed -i \u76F4\u63A5\u5199\u5165\u6587\u4EF6\uFF0C\u7ED5\u8FC7 IDE \u5DE5\u5177\u5C42\u7684\u6240\u6709\u6821\u9A8C\u3002\n \u2192 \u8BF7\u6539\u7528 SearchReplace \u5DE5\u5177\u3002\n"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"id": "redirect",
|
|
32
|
+
"regex": "[>]{1,2}\\s+\\S",
|
|
33
|
+
"reason": "\u7981\u6B62\u901A\u8FC7\u91CD\u5B9A\u5411\u5199\u5165\u6587\u4EF6\u3002\u8BF7\u4F7F\u7528 Write \u5DE5\u5177\u3002",
|
|
34
|
+
"stderr": "\u26D4 [ADD PreToolUse \xA7A] \u963B\u65AD: \u7981\u6B62\u901A\u8FC7\u91CD\u5B9A\u5411(>/>>)\u5199\u5165\u6587\u4EF6\u3002\n\n \u91CD\u5B9A\u5411\u5199\u5165\u7ED5\u8FC7 IDE \u5DE5\u5177\u5C42\uFF0C\u53D8\u66F4\u65E0\u6CD5\u8FFD\u8E2A\u3002\n \u2192 \u8BF7\u6539\u7528 Write \u5DE5\u5177\u3002\n"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"id": "tee-dd",
|
|
38
|
+
"regex": "\\btee\\b|\\bdd\\b.*of=",
|
|
39
|
+
"reason": "\u7981\u6B62\u901A\u8FC7 tee/dd \u5199\u5165\u6587\u4EF6\u3002\u8BF7\u4F7F\u7528 Write \u6216 SearchReplace \u5DE5\u5177\u3002",
|
|
40
|
+
"stderr": "\u26D4 [ADD PreToolUse \xA7A] \u963B\u65AD: \u7981\u6B62\u901A\u8FC7 tee/dd \u5199\u5165\u6587\u4EF6\u3002\n\n \u2192 \u8BF7\u6539\u7528 Write \u6216 SearchReplace \u5DE5\u5177\u3002\n"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "cp-mv-touch",
|
|
44
|
+
"regex": "(^|;|\\|\\||&&|\\|)\\s*(cp|mv|touch)\\b",
|
|
45
|
+
"reason": "\u7981\u6B62\u901A\u8FC7 cp/mv/touch \u64CD\u4F5C\u6587\u4EF6\u3002\u8BF7\u4F7F\u7528 Write \u6216 SearchReplace \u5DE5\u5177\u3002",
|
|
46
|
+
"stderr": "\u26D4 [ADD PreToolUse \xA7A] \u963B\u65AD: \u7981\u6B62\u901A\u8FC7 cp/mv/touch \u64CD\u4F5C\u6587\u4EF6\u3002\n\n \u2192 \u8BF7\u6539\u7528 Write \u6216 SearchReplace \u5DE5\u5177\u3002\n"
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
"adapters": {
|
|
50
|
+
"trae": [
|
|
51
|
+
"script-interpreter",
|
|
52
|
+
"sed-in-place",
|
|
53
|
+
"redirect",
|
|
54
|
+
"tee-dd",
|
|
55
|
+
"cp-mv-touch"
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
"adapter_detectors": [
|
|
59
|
+
{
|
|
60
|
+
"adapter": "trae",
|
|
61
|
+
"id": "dangerous-command",
|
|
62
|
+
"regex": "rm\\s+-rf\\s+/|DROP\\s+TABLE|git\\s+push\\s+--force|mkfs\\.|dd\\s+if=",
|
|
63
|
+
"flags": "i",
|
|
64
|
+
"reason": "\u5371\u9669\u547D\u4EE4\u5DF2\u88AB\u963B\u6B62",
|
|
65
|
+
"stderr": "\u26D4 \u5371\u9669\u547D\u4EE4\u5DF2\u88AB\u963B\u6B62: {{cmd}}\n"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"adapter": "trae",
|
|
69
|
+
"id": "script-interpreter",
|
|
70
|
+
"regex": "(^|;|\\|\\||&&|\\|)\\s*(python3?|node|ruby|perl|php)(\\s|$)",
|
|
71
|
+
"reason": "\u7981\u6B62\u901A\u8FC7\u811A\u672C\u89E3\u91CA\u5668\u76F4\u63A5\u4FEE\u6539\u6587\u4EF6\u3002\u8BF7\u4F7F\u7528 Write \u6216 SearchReplace \u5DE5\u5177\u64CD\u4F5C\u6587\u4EF6\u3002",
|
|
72
|
+
"stderr": "\u26D4 [ADD PreToolUse \xA7A] \u963B\u65AD: \u7981\u6B62\u901A\u8FC7\u811A\u672C\u89E3\u91CA\u5668\u76F4\u63A5\u4FEE\u6539\u6587\u4EF6\u3002\n\n python/node/ruby/perl/php \u53EF\u5728\u811A\u672C\u4E2D\u5199\u5165\u4EFB\u610F\u6587\u4EF6\uFF0C\u7ED5\u8FC7:\n \xB7 Plan \u5173\u8054\u68C0\u67E5\uFF08\u54EA\u4E2A\u6587\u4EF6\u5C5E\u4E8E\u54EA\u4E2A ADD Plan\uFF1F\uFF09\n \xB7 doc-format-guard\uFF08\u7AE0\u8282/\u5360\u4F4D\u7B26/\u7981\u6B62\u8BCD\u6821\u9A8C\uFF09\n \xB7 \u5BA1\u8BA1\u8FFD\u8E2A\uFF08agentAudit \u65E0\u6CD5\u611F\u77E5 Bash \u5185\u90E8\u7684\u6587\u4EF6\u53D8\u66F4\uFF09\n\n \u2192 \u8BF7\u6539\u7528 Write \u6216 SearchReplace \u5DE5\u5177\u64CD\u4F5C\u6587\u4EF6\u3002\n \u2192 \u5982\u9700\u8FD0\u884C\u6784\u5EFA/\u6D4B\u8BD5\u811A\u672C\uFF0C\u4F7F\u7528 npx/pnpm/npm \u547D\u4EE4\u3002\n"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"adapter": "trae",
|
|
76
|
+
"id": "sed-in-place",
|
|
77
|
+
"regex": "(^|[;&|]\\s*)sed\\s+([^;&|]*\\s)?(-[a-zA-Z]*i[^;&|]*|--in-place(=[^;&|]*)?)([\\s;&|]|$)",
|
|
78
|
+
"reason": "\u7981\u6B62\u901A\u8FC7 sed -i \u76F4\u63A5\u7F16\u8F91\u6587\u4EF6\u3002\u8BF7\u4F7F\u7528 SearchReplace \u5DE5\u5177\u3002",
|
|
79
|
+
"stderr": "\u26D4 [ADD PreToolUse \xA7A] \u963B\u65AD: \u7981\u6B62\u901A\u8FC7 sed -i \u539F\u5730\u7F16\u8F91\u6587\u4EF6\u3002\n\n sed -i \u76F4\u63A5\u5199\u5165\u6587\u4EF6\uFF0C\u7ED5\u8FC7 IDE \u5DE5\u5177\u5C42\u7684\u6240\u6709\u6821\u9A8C\u3002\n \u2192 \u8BF7\u6539\u7528 SearchReplace \u5DE5\u5177\u3002\n"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"adapter": "trae",
|
|
83
|
+
"id": "redirect",
|
|
84
|
+
"regex": "[>]{1,2}\\s+\\S",
|
|
85
|
+
"reason": "\u7981\u6B62\u901A\u8FC7\u91CD\u5B9A\u5411\u5199\u5165\u6587\u4EF6\u3002\u8BF7\u4F7F\u7528 Write \u5DE5\u5177\u3002",
|
|
86
|
+
"stderr": "\u26D4 [ADD PreToolUse \xA7A] \u963B\u65AD: \u7981\u6B62\u901A\u8FC7\u91CD\u5B9A\u5411(>/>>)\u5199\u5165\u6587\u4EF6\u3002\n\n \u91CD\u5B9A\u5411\u5199\u5165\u7ED5\u8FC7 IDE \u5DE5\u5177\u5C42\uFF0C\u53D8\u66F4\u65E0\u6CD5\u8FFD\u8E2A\u3002\n \u2192 \u8BF7\u6539\u7528 Write \u5DE5\u5177\u3002\n"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"adapter": "trae",
|
|
90
|
+
"id": "tee-dd",
|
|
91
|
+
"regex": "\\btee\\b|\\bdd\\b.*of=",
|
|
92
|
+
"reason": "\u7981\u6B62\u901A\u8FC7 tee/dd \u5199\u5165\u6587\u4EF6\u3002\u8BF7\u4F7F\u7528 Write \u6216 SearchReplace \u5DE5\u5177\u3002",
|
|
93
|
+
"stderr": "\u26D4 [ADD PreToolUse \xA7A] \u963B\u65AD: \u7981\u6B62\u901A\u8FC7 tee/dd \u5199\u5165\u6587\u4EF6\u3002\n\n \u2192 \u8BF7\u6539\u7528 Write \u6216 SearchReplace \u5DE5\u5177\u3002\n"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"adapter": "trae",
|
|
97
|
+
"id": "cp-mv-touch",
|
|
98
|
+
"regex": "(^|;|\\|\\||&&|\\|)\\s*(cp|mv|touch)\\b",
|
|
99
|
+
"reason": "\u7981\u6B62\u901A\u8FC7 cp/mv/touch \u64CD\u4F5C\u6587\u4EF6\u3002\u8BF7\u4F7F\u7528 Write \u6216 SearchReplace \u5DE5\u5177\u3002",
|
|
100
|
+
"stderr": "\u26D4 [ADD PreToolUse \xA7A] \u963B\u65AD: \u7981\u6B62\u901A\u8FC7 cp/mv/touch \u64CD\u4F5C\u6587\u4EF6\u3002\n\n \u2192 \u8BF7\u6539\u7528 Write \u6216 SearchReplace \u5DE5\u5177\u3002\n"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"adapter": "claude",
|
|
104
|
+
"id": "dangerous-command",
|
|
105
|
+
"regex": "rm\\s+-rf\\s+/|DROP\\s+TABLE|git\\s+push\\s+--force|mkfs\\.|dd\\s+if=",
|
|
106
|
+
"flags": "i",
|
|
107
|
+
"reason": "\u5371\u9669\u547D\u4EE4\u5DF2\u88AB\u963B\u6B62",
|
|
108
|
+
"stderr": "\u26D4 \u5371\u9669\u547D\u4EE4\u5DF2\u88AB\u963B\u6B62: {{cmd}}\n"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"adapter": "claude",
|
|
112
|
+
"id": "terminal-write",
|
|
113
|
+
"regex": "(cat|echo|tee|sed\\s+-i|awk|printf|cp|mv|dd|touch)\\s*.*([>]{1,2}|[|]\\s*tee|<<)",
|
|
114
|
+
"flags": "",
|
|
115
|
+
"reason": "\u7981\u6B62\u901A\u8FC7\u7EC8\u7AEF\u76F4\u63A5\u5199\u6587\u4EF6",
|
|
116
|
+
"stderr": "\u26D4 \u7981\u6B62\u901A\u8FC7\u7EC8\u7AEF\u547D\u4EE4\u76F4\u63A5\u5199\u6587\u4EF6: {{cmd}}\u3002\u8BF7\u4F7F\u7528 Write/Edit/SearchReplace \u5DE5\u5177\u3002\n"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"adapter": "claude",
|
|
120
|
+
"id": "cp-mv-touch",
|
|
121
|
+
"regex": "(^|;|\\|\\||&&|\\|)\\s*(cp|mv|touch)\\b",
|
|
122
|
+
"flags": "",
|
|
123
|
+
"reason": "\u7981\u6B62\u901A\u8FC7 cp/mv/touch \u64CD\u4F5C\u6587\u4EF6",
|
|
124
|
+
"stderr": "\u26D4 \u7981\u6B62\u901A\u8FC7 cp/mv/touch \u64CD\u4F5C\u6587\u4EF6: {{cmd}}\u3002\u8BF7\u4F7F\u7528 Write \u6216 SearchReplace \u5DE5\u5177\u3002\n"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"adapter": "claude",
|
|
128
|
+
"id": "script-interpreter",
|
|
129
|
+
"regex": "(^|;|\\|\\||&&|\\|)\\s*(python3?|node|ruby|perl|php)(\\s|$)",
|
|
130
|
+
"flags": "",
|
|
131
|
+
"reason": "\u7981\u6B62\u901A\u8FC7\u811A\u672C\u89E3\u91CA\u5668\u76F4\u63A5\u4FEE\u6539\u6587\u4EF6",
|
|
132
|
+
"stderr": "\u26D4 \u7981\u6B62\u901A\u8FC7\u811A\u672C\u89E3\u91CA\u5668\u76F4\u63A5\u5199\u6587\u4EF6: {{cmd}}\u3002\u8BF7\u4F7F\u7528 Write \u6216 SearchReplace \u5DE5\u5177\u3002\n"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"adapter": "qoder",
|
|
136
|
+
"id": "dangerous-command",
|
|
137
|
+
"regex": "rm\\s+-rf\\s+/|DROP\\s+TABLE|git\\s+push\\s+--force|mkfs\\.|dd\\s+if=",
|
|
138
|
+
"flags": "i",
|
|
139
|
+
"reason": "\u5371\u9669\u547D\u4EE4\u5DF2\u88AB\u963B\u6B62",
|
|
140
|
+
"stderr": "\u26D4 \u5371\u9669\u547D\u4EE4\u5DF2\u88AB\u963B\u6B62: {{cmd}}\n"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"adapter": "qoder",
|
|
144
|
+
"id": "terminal-write",
|
|
145
|
+
"regex": "(cat|echo|tee|sed\\s+-i|awk|printf|cp|mv|dd|touch)\\s*.*([>]{1,2}|[|]\\s*tee|<<)",
|
|
146
|
+
"flags": "",
|
|
147
|
+
"reason": "\u7981\u6B62\u901A\u8FC7\u7EC8\u7AEF\u76F4\u63A5\u5199\u6587\u4EF6",
|
|
148
|
+
"stderr": "\u26D4 \u7981\u6B62\u901A\u8FC7\u7EC8\u7AEF\u547D\u4EE4\u76F4\u63A5\u5199\u6587\u4EF6: {{cmd}}\u3002\u8BF7\u4F7F\u7528 Write/Edit/SearchReplace \u5DE5\u5177\u3002\n"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"adapter": "qoder",
|
|
152
|
+
"id": "cp-mv-touch",
|
|
153
|
+
"regex": "(^|;|\\|\\||&&|\\|)\\s*(cp|mv|touch)\\b",
|
|
154
|
+
"flags": "",
|
|
155
|
+
"reason": "\u7981\u6B62\u901A\u8FC7 cp/mv/touch \u64CD\u4F5C\u6587\u4EF6",
|
|
156
|
+
"stderr": "\u26D4 \u7981\u6B62\u901A\u8FC7 cp/mv/touch \u64CD\u4F5C\u6587\u4EF6: {{cmd}}\u3002\u8BF7\u4F7F\u7528 Write \u6216 SearchReplace \u5DE5\u5177\u3002\n"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"adapter": "qoder",
|
|
160
|
+
"id": "script-interpreter",
|
|
161
|
+
"regex": "(^|;|\\|\\||&&|\\|)\\s*(python3?|node|ruby|perl|php)(\\s|$)",
|
|
162
|
+
"flags": "",
|
|
163
|
+
"reason": "\u7981\u6B62\u901A\u8FC7\u811A\u672C\u89E3\u91CA\u5668\u76F4\u63A5\u4FEE\u6539\u6587\u4EF6",
|
|
164
|
+
"stderr": "\u26D4 \u7981\u6B62\u901A\u8FC7\u811A\u672C\u89E3\u91CA\u5668\u76F4\u63A5\u5199\u6587\u4EF6: {{cmd}}\u3002\u8BF7\u4F7F\u7528 Write \u6216 SearchReplace \u5DE5\u5177\u3002\n"
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"adapter": "codex",
|
|
168
|
+
"id": "dangerous-command",
|
|
169
|
+
"regex": "rm\\s+-rf\\s+/|DROP\\s+TABLE|git\\s+push\\s+--force|mkfs\\.|dd\\s+if=",
|
|
170
|
+
"flags": "i",
|
|
171
|
+
"reason": "\u5371\u9669\u547D\u4EE4\u5DF2\u88AB\u963B\u6B62\uFF1B\u8BF7\u4F7F\u7528 apply_patch\u3002"
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"adapter": "codex",
|
|
175
|
+
"id": "script-interpreter",
|
|
176
|
+
"regex": "(^|;|\\|\\||&&|\\|)\\s*(python3?|node|ruby|perl|php)(\\s|$)",
|
|
177
|
+
"reason": "\u7981\u6B62\u901A\u8FC7\u811A\u672C\u89E3\u91CA\u5668\u76F4\u63A5\u4FEE\u6539\u6587\u4EF6\uFF1B\u8BF7\u4F7F\u7528 apply_patch\u3002"
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
"adapter": "codex",
|
|
181
|
+
"id": "sed-in-place",
|
|
182
|
+
"regex": "(^|[;&|]\\s*)sed\\s+([^;&|]*\\s)?(-[a-zA-Z]*i[^;&|]*|--in-place(=[^;&|]*)?)([\\s;&|]|$)",
|
|
183
|
+
"reason": "\u7981\u6B62\u901A\u8FC7 sed -i \u76F4\u63A5\u7F16\u8F91\u6587\u4EF6\uFF1B\u8BF7\u4F7F\u7528 apply_patch\u3002"
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"adapter": "codex",
|
|
187
|
+
"id": "redirect",
|
|
188
|
+
"regex": "[>]{1,2}\\s+\\S",
|
|
189
|
+
"reason": "\u7981\u6B62\u901A\u8FC7\u91CD\u5B9A\u5411\u5199\u5165\u6587\u4EF6\uFF1B\u8BF7\u4F7F\u7528 apply_patch\u3002"
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"adapter": "codex",
|
|
193
|
+
"id": "tee-dd",
|
|
194
|
+
"regex": "\\btee\\b|\\bdd\\b.*of=",
|
|
195
|
+
"reason": "\u7981\u6B62\u901A\u8FC7 tee/dd \u5199\u5165\u6587\u4EF6\uFF1B\u8BF7\u4F7F\u7528 apply_patch\u3002"
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"adapter": "codex",
|
|
199
|
+
"id": "cp-mv-touch",
|
|
200
|
+
"regex": "(^|;|\\|\\||&&|\\|)\\s*(cp|mv|touch)\\b",
|
|
201
|
+
"reason": "\u7981\u6B62\u901A\u8FC7 cp/mv/touch \u6539\u53D8\u6587\u4EF6\uFF1B\u8BF7\u4F7F\u7528 apply_patch\u3002"
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
"adapter": "vscode",
|
|
205
|
+
"id": "dangerous-command",
|
|
206
|
+
"regex": "rm\\s+-rf\\s+/|DROP\\s+TABLE|git\\s+push\\s+--force|mkfs\\.|dd\\s+if=",
|
|
207
|
+
"flags": "i",
|
|
208
|
+
"reason": "\u5371\u9669\u547D\u4EE4\u5DF2\u88AB\u963B\u6B62",
|
|
209
|
+
"stderr": "\u26D4 \u5371\u9669\u547D\u4EE4\u5DF2\u88AB\u963B\u6B62: {{cmd}}\n"
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
"adapter": "vscode",
|
|
213
|
+
"id": "terminal-write",
|
|
214
|
+
"regex": "(cat|echo|tee|sed\\s+-i|awk|printf|cp|mv|dd|touch)\\s*.*([>]{1,2}|[|]\\s*tee|<<)",
|
|
215
|
+
"flags": "",
|
|
216
|
+
"reason": "\u7981\u6B62\u901A\u8FC7\u7EC8\u7AEF\u76F4\u63A5\u5199\u6587\u4EF6",
|
|
217
|
+
"stderr": "\u26D4 \u7981\u6B62\u901A\u8FC7\u7EC8\u7AEF\u547D\u4EE4\u76F4\u63A5\u5199\u6587\u4EF6: {{cmd}}\u3002\u8BF7\u4F7F\u7528 Write/Edit/SearchReplace \u5DE5\u5177\u3002\n"
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"adapter": "vscode",
|
|
221
|
+
"id": "cp-mv-touch",
|
|
222
|
+
"regex": "(^|;|\\|\\||&&|\\|)\\s*(cp|mv|touch)\\b",
|
|
223
|
+
"flags": "",
|
|
224
|
+
"reason": "\u7981\u6B62\u901A\u8FC7 cp/mv/touch \u64CD\u4F5C\u6587\u4EF6",
|
|
225
|
+
"stderr": "\u26D4 \u7981\u6B62\u901A\u8FC7 cp/mv/touch \u64CD\u4F5C\u6587\u4EF6: {{cmd}}\u3002\u8BF7\u4F7F\u7528 Write \u6216 SearchReplace \u5DE5\u5177\u3002\n"
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
"adapter": "vscode",
|
|
229
|
+
"id": "script-interpreter",
|
|
230
|
+
"regex": "(^|;|\\|\\||&&|\\|)\\s*(python3?|node|ruby|perl|php)(\\s|$)",
|
|
231
|
+
"flags": "",
|
|
232
|
+
"reason": "\u7981\u6B62\u901A\u8FC7\u811A\u672C\u89E3\u91CA\u5668\u76F4\u63A5\u4FEE\u6539\u6587\u4EF6",
|
|
233
|
+
"stderr": "\u26D4 \u7981\u6B62\u901A\u8FC7\u811A\u672C\u89E3\u91CA\u5668\u76F4\u63A5\u5199\u6587\u4EF6: {{cmd}}\u3002\u8BF7\u4F7F\u7528 Write \u6216 SearchReplace \u5DE5\u5177\u3002\n"
|
|
234
|
+
}
|
|
235
|
+
],
|
|
236
|
+
"sensitive_files": {
|
|
237
|
+
"regex": "(^|\\/)\\.env$|(^|\\/)\\.env\\.production$|(^|\\/)\\.env\\.local$|credentials|secrets",
|
|
238
|
+
"deny_reason": "\u654F\u611F\u6587\u4EF6\u53D7\u4FDD\u62A4"
|
|
239
|
+
},
|
|
240
|
+
"template_hints": [
|
|
241
|
+
{
|
|
242
|
+
"pattern": "plan-v\\d",
|
|
243
|
+
"message": "\u{1F4A1} [ADD PreToolUse] \u5199\u5165 Plan \u2192 \u6A21\u677F: standard-plan-template.md\uFF08\u6807\u51C6\uFF09\u6216 simple-plan-template.md\uFF08\u22643\u6587\u4EF6\uFF09"
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
"pattern": "add-route",
|
|
247
|
+
"message": "\u{1F4A1} [ADD PreToolUse] \u5199\u5165 ADD Route \u2192 \u6A21\u677F: add-route-template.md"
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
"pattern": "handoff",
|
|
251
|
+
"message": "\u{1F4A1} [ADD PreToolUse] \u5199\u5165 Handoff \u2192 \u6A21\u677F: handoff-single-round-template.md\uFF08\u5355\u8F6E\uFF09\u6216 handoff-multi-round-template.md\uFF08\u591A\u8F6E\uFF09"
|
|
252
|
+
}
|
|
253
|
+
],
|
|
254
|
+
"thresholds": {
|
|
255
|
+
"large_file_bytes": 2e3
|
|
256
|
+
},
|
|
257
|
+
"hitl_exemptions": {
|
|
258
|
+
"suffixes": [
|
|
259
|
+
"-handoff",
|
|
260
|
+
"-runtime"
|
|
261
|
+
]
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
var event = {
|
|
265
|
+
"file": {
|
|
266
|
+
"path": "{magicDir}/reports/hook-events.jsonl",
|
|
267
|
+
"rotate_bytes": 262144,
|
|
268
|
+
"total_bytes": 524288,
|
|
269
|
+
"note": "MCP Server \u5B95\u673A\u4E0D\u4E22\u4E8B\u4EF6\uFF0C\u91CD\u542F\u540E\u4ECE\u6587\u4EF6\u6062\u590D\u6D88\u8D39"
|
|
270
|
+
},
|
|
271
|
+
"schema": {
|
|
272
|
+
"fields": [
|
|
273
|
+
"ts",
|
|
274
|
+
"hook",
|
|
275
|
+
"decision",
|
|
276
|
+
"cmd",
|
|
277
|
+
"reason",
|
|
278
|
+
"planKeyword",
|
|
279
|
+
"planStatus"
|
|
280
|
+
],
|
|
281
|
+
"ts_format": "date -u +%Y-%m-%dT%H:%M:%SZ",
|
|
282
|
+
"extra_fields": [
|
|
283
|
+
"anchor_hit",
|
|
284
|
+
"struct_score",
|
|
285
|
+
"override"
|
|
286
|
+
]
|
|
287
|
+
},
|
|
288
|
+
"daily": {
|
|
289
|
+
"warn_threshold": 10
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
var protocol = {
|
|
293
|
+
"exit_codes": {
|
|
294
|
+
"pass": 0,
|
|
295
|
+
"block": 2,
|
|
296
|
+
"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"
|
|
297
|
+
},
|
|
298
|
+
"output": {
|
|
299
|
+
"stdout_json_only": true,
|
|
300
|
+
"stderr_human_text": true,
|
|
301
|
+
"field_separator": "::",
|
|
302
|
+
"magic_dir_resolution": "\u6CE8\u5165\u4F18\u5148 \u2192 \u7269\u7406\u4F4D\u7F6E\u63A8\u5BFC \u2192 failClosed\uFF08\u7981\u6B62\u731C\u6D4B adapter \u540D\uFF09"
|
|
303
|
+
},
|
|
304
|
+
"adapters": {
|
|
305
|
+
"claude": {
|
|
306
|
+
"stdout_form": "plain-text",
|
|
307
|
+
"project_dir_env": "CLAUDE_PROJECT_DIR",
|
|
308
|
+
"magic_dir": ".claude",
|
|
309
|
+
"handlerTypes": [
|
|
310
|
+
"command",
|
|
311
|
+
"mcp_tool"
|
|
312
|
+
]
|
|
313
|
+
},
|
|
314
|
+
"qoder": {
|
|
315
|
+
"stdout_form": "json",
|
|
316
|
+
"project_dir_env": "QODER_PROJECT_DIR",
|
|
317
|
+
"magic_dir": ".qoder",
|
|
318
|
+
"handlerTypes": [
|
|
319
|
+
"command",
|
|
320
|
+
"http"
|
|
321
|
+
]
|
|
322
|
+
},
|
|
323
|
+
"codex": {
|
|
324
|
+
"stdout_form": "systemMessage",
|
|
325
|
+
"project_dir_env": "git-toplevel",
|
|
326
|
+
"magic_dir": ".codex",
|
|
327
|
+
"handlerTypes": [
|
|
328
|
+
"command"
|
|
329
|
+
]
|
|
330
|
+
},
|
|
331
|
+
"vscode": {
|
|
332
|
+
"stdout_form": "plain-text",
|
|
333
|
+
"project_dir_env": "PWD",
|
|
334
|
+
"magic_dir": ".vscode",
|
|
335
|
+
"handlerTypes": [
|
|
336
|
+
"command"
|
|
337
|
+
]
|
|
338
|
+
},
|
|
339
|
+
"trae": {
|
|
340
|
+
"stdout_form": "plain-text",
|
|
341
|
+
"project_dir_env": "PWD",
|
|
342
|
+
"magic_dir": ".trae",
|
|
343
|
+
"handlerTypes": [
|
|
344
|
+
"command"
|
|
345
|
+
]
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
"event_outputs": {
|
|
349
|
+
"qoder": {
|
|
350
|
+
"SessionStart": "additionalContext",
|
|
351
|
+
"UserPromptSubmit": "additionalContext",
|
|
352
|
+
"PreToolUse": "permissionDecision",
|
|
353
|
+
"Stop": "additionalContext",
|
|
354
|
+
"PostToolUse": "feedback",
|
|
355
|
+
"SubagentStart": "additionalContext",
|
|
356
|
+
"SubagentStop": "additionalContext",
|
|
357
|
+
"PostToolUseFailure": "text",
|
|
358
|
+
"PermissionRequest": "text",
|
|
359
|
+
"SessionEnd": "text",
|
|
360
|
+
"PreCompact": "text",
|
|
361
|
+
"Notification": "text"
|
|
362
|
+
},
|
|
363
|
+
"claude": {
|
|
364
|
+
"SessionStart": "additionalContext",
|
|
365
|
+
"UserPromptSubmit": "additionalContext",
|
|
366
|
+
"PreToolUse": "permissionDecision",
|
|
367
|
+
"Stop": "text",
|
|
368
|
+
"PostToolUse": "feedback",
|
|
369
|
+
"SubagentStart": "text",
|
|
370
|
+
"SubagentStop": "text",
|
|
371
|
+
"PostToolUseFailure": "text",
|
|
372
|
+
"PermissionRequest": "text",
|
|
373
|
+
"SessionEnd": "text",
|
|
374
|
+
"PreCompact": "text",
|
|
375
|
+
"Notification": "text"
|
|
376
|
+
},
|
|
377
|
+
"codex": {
|
|
378
|
+
"Stop": "systemMessage",
|
|
379
|
+
"PostToolUse": "text"
|
|
380
|
+
},
|
|
381
|
+
"vscode": {
|
|
382
|
+
"PostToolUse": "text"
|
|
383
|
+
},
|
|
384
|
+
"trae": {
|
|
385
|
+
"PostToolUse": "text"
|
|
386
|
+
}
|
|
387
|
+
},
|
|
388
|
+
"core": {
|
|
389
|
+
"stdout_form": "json",
|
|
390
|
+
"magic_dir": ".add",
|
|
391
|
+
"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"
|
|
392
|
+
},
|
|
393
|
+
"adapter_defaults": {
|
|
394
|
+
"magic_dir_fallback": ".qoder",
|
|
395
|
+
"probe_magic_dirs": [
|
|
396
|
+
".claude",
|
|
397
|
+
".qoder",
|
|
398
|
+
".vscode",
|
|
399
|
+
".add",
|
|
400
|
+
".trae",
|
|
401
|
+
".codex"
|
|
402
|
+
]
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
// node_modules/.pnpm/find-up@8.0.0/node_modules/find-up/index.js
|
|
407
|
+
import path2 from "node:path";
|
|
408
|
+
|
|
409
|
+
// node_modules/.pnpm/locate-path@8.0.0/node_modules/locate-path/index.js
|
|
410
|
+
import process2 from "node:process";
|
|
411
|
+
import path from "node:path";
|
|
412
|
+
import fs, { promises as fsPromises } from "node:fs";
|
|
413
|
+
import { fileURLToPath } from "node:url";
|
|
414
|
+
var typeMappings = {
|
|
415
|
+
directory: "isDirectory",
|
|
416
|
+
file: "isFile"
|
|
417
|
+
};
|
|
418
|
+
function checkType(type) {
|
|
419
|
+
if (type === "both" || Object.hasOwn(typeMappings, type)) {
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
throw new Error(`Invalid type specified: ${type}`);
|
|
423
|
+
}
|
|
424
|
+
var matchType = (type, stat) => type === "both" ? stat.isFile() || stat.isDirectory() : stat[typeMappings[type]]();
|
|
425
|
+
var toPath = (urlOrPath) => urlOrPath instanceof URL ? fileURLToPath(urlOrPath) : urlOrPath;
|
|
426
|
+
function locatePathSync(paths, {
|
|
427
|
+
cwd = process2.cwd(),
|
|
428
|
+
type = "file",
|
|
429
|
+
allowSymlinks = true
|
|
430
|
+
} = {}) {
|
|
431
|
+
checkType(type);
|
|
432
|
+
cwd = toPath(cwd);
|
|
433
|
+
const statFunction = allowSymlinks ? fs.statSync : fs.lstatSync;
|
|
434
|
+
for (const path_ of paths) {
|
|
435
|
+
try {
|
|
436
|
+
const stat = statFunction(path.resolve(cwd, path_), {
|
|
437
|
+
throwIfNoEntry: false
|
|
438
|
+
});
|
|
439
|
+
if (!stat) {
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
if (matchType(type, stat)) {
|
|
443
|
+
return path_;
|
|
444
|
+
}
|
|
445
|
+
} catch {
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// node_modules/.pnpm/unicorn-magic@0.3.0/node_modules/unicorn-magic/node.js
|
|
451
|
+
import { promisify } from "node:util";
|
|
452
|
+
import { execFile as execFileCallback, execFileSync as execFileSyncOriginal } from "node:child_process";
|
|
453
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
454
|
+
var execFileOriginal = promisify(execFileCallback);
|
|
455
|
+
function toPath2(urlOrPath) {
|
|
456
|
+
return urlOrPath instanceof URL ? fileURLToPath2(urlOrPath) : urlOrPath;
|
|
457
|
+
}
|
|
458
|
+
var TEN_MEGABYTES_IN_BYTES = 10 * 1024 * 1024;
|
|
459
|
+
|
|
460
|
+
// node_modules/.pnpm/find-up@8.0.0/node_modules/find-up/index.js
|
|
461
|
+
var findUpStop = /* @__PURE__ */ Symbol("findUpStop");
|
|
462
|
+
function findUpMultipleSync(name, options = {}) {
|
|
463
|
+
let directory = path2.resolve(toPath2(options.cwd) ?? "");
|
|
464
|
+
const { root } = path2.parse(directory);
|
|
465
|
+
const stopAt = path2.resolve(directory, toPath2(options.stopAt) ?? root);
|
|
466
|
+
const limit = options.limit ?? Number.POSITIVE_INFINITY;
|
|
467
|
+
const paths = [name].flat();
|
|
468
|
+
const runMatcher = (locateOptions) => {
|
|
469
|
+
if (typeof name !== "function") {
|
|
470
|
+
return locatePathSync(paths, locateOptions);
|
|
471
|
+
}
|
|
472
|
+
const foundPath = name(locateOptions.cwd);
|
|
473
|
+
if (typeof foundPath === "string") {
|
|
474
|
+
return locatePathSync([foundPath], locateOptions);
|
|
475
|
+
}
|
|
476
|
+
return foundPath;
|
|
477
|
+
};
|
|
478
|
+
const matches = [];
|
|
479
|
+
while (true) {
|
|
480
|
+
const foundPath = runMatcher({ ...options, cwd: directory });
|
|
481
|
+
if (foundPath === findUpStop) {
|
|
482
|
+
break;
|
|
483
|
+
}
|
|
484
|
+
if (foundPath) {
|
|
485
|
+
matches.push(path2.resolve(directory, foundPath));
|
|
486
|
+
}
|
|
487
|
+
if (directory === stopAt || matches.length >= limit) {
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
directory = path2.dirname(directory);
|
|
491
|
+
}
|
|
492
|
+
return matches;
|
|
493
|
+
}
|
|
494
|
+
function findUpSync(name, options = {}) {
|
|
495
|
+
const matches = findUpMultipleSync(name, { ...options, limit: 1 });
|
|
496
|
+
return matches[0];
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// templates/core/governance/common.ts
|
|
500
|
+
var EXIT_PASS = protocol.exit_codes.pass;
|
|
501
|
+
var EXIT_BLOCK = protocol.exit_codes.block;
|
|
502
|
+
var STATE_SEP = protocol.output.field_separator;
|
|
503
|
+
function readHookInput() {
|
|
504
|
+
if (process.stdin.isTTY) return "{}";
|
|
505
|
+
return readFileSync(0, "utf-8");
|
|
506
|
+
}
|
|
507
|
+
function jsonGet(json, field) {
|
|
508
|
+
const re = new RegExp(`"${field}"\\s*:\\s*"([^"]*)"`);
|
|
509
|
+
const m = re.exec(json);
|
|
510
|
+
return m?.[1] ?? "";
|
|
511
|
+
}
|
|
512
|
+
function tryResolveMagicDir() {
|
|
513
|
+
const injected = process.env.MAGIC_DIR;
|
|
514
|
+
if (injected) return injected;
|
|
515
|
+
const startDir = dirname(fileURLToPath3(import.meta.url));
|
|
516
|
+
const hit = findUpSync((dir) => basename(dir).startsWith(".") ? dir : void 0, {
|
|
517
|
+
cwd: startDir,
|
|
518
|
+
type: "directory"
|
|
519
|
+
});
|
|
520
|
+
return hit ? basename(hit) : "";
|
|
521
|
+
}
|
|
522
|
+
function queryPlanStatus() {
|
|
523
|
+
const magicDir = process.env.MAGIC_DIR;
|
|
524
|
+
if (!magicDir) {
|
|
525
|
+
return {
|
|
526
|
+
stdout: '{"availability":"STATUS_UNAVAILABLE","source":"database","reason":"magicDir \u672A\u6CE8\u5165\u4E14\u65E0\u6CD5\u4ECE\u7269\u7406\u4F4D\u7F6E\u63A8\u5BFC"}',
|
|
527
|
+
exitCode: 3
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
const bridge = join(
|
|
531
|
+
process.env.PROJECT_DIR || process.cwd(),
|
|
532
|
+
magicDir,
|
|
533
|
+
"scripts",
|
|
534
|
+
"plan-status-bridge.ts"
|
|
535
|
+
);
|
|
536
|
+
if (!existsSync(bridge)) {
|
|
537
|
+
return {
|
|
538
|
+
stdout: '{"availability":"STATUS_UNAVAILABLE","source":"database","reason":"plan-status bridge missing"}',
|
|
539
|
+
exitCode: 3
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
const r = spawnSync("node", ["--import", "tsx", bridge], {
|
|
543
|
+
encoding: "utf-8",
|
|
544
|
+
timeout: 1e4
|
|
545
|
+
});
|
|
546
|
+
return { stdout: r.stdout ?? "", exitCode: r.status ?? -1 };
|
|
547
|
+
}
|
|
548
|
+
function detectActiveAdd() {
|
|
549
|
+
const r = queryPlanStatus();
|
|
550
|
+
if (r.exitCode !== 0) {
|
|
551
|
+
let reason = "database status unavailable";
|
|
552
|
+
try {
|
|
553
|
+
const parsed = JSON.parse(r.stdout);
|
|
554
|
+
if (parsed.reason) reason = parsed.reason;
|
|
555
|
+
} catch {
|
|
556
|
+
}
|
|
557
|
+
return `__STATUS_UNAVAILABLE__::${reason}::database::none::none`;
|
|
558
|
+
}
|
|
559
|
+
let snapshot;
|
|
560
|
+
try {
|
|
561
|
+
snapshot = JSON.parse(r.stdout);
|
|
562
|
+
} catch {
|
|
563
|
+
return null;
|
|
564
|
+
}
|
|
565
|
+
if (!(snapshot.availability === "READY" && snapshot.isActive === true)) return null;
|
|
566
|
+
const done = snapshot.progress?.doneTasks ?? 0;
|
|
567
|
+
const total = snapshot.progress?.totalTasks ?? 0;
|
|
568
|
+
const approval = snapshot.approvalStatus ?? "none";
|
|
569
|
+
return `${snapshot.planName}::${done}/${total}::${approval}::none::none`;
|
|
570
|
+
}
|
|
571
|
+
function localIsoSeconds() {
|
|
572
|
+
const d = /* @__PURE__ */ new Date();
|
|
573
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
574
|
+
const offsetMin = -d.getTimezoneOffset();
|
|
575
|
+
const sign = offsetMin >= 0 ? "+" : "-";
|
|
576
|
+
const offAbs = Math.abs(offsetMin);
|
|
577
|
+
const off = `${sign}${pad(Math.floor(offAbs / 60))}:${pad(offAbs % 60)}`;
|
|
578
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}${off}`;
|
|
579
|
+
}
|
|
580
|
+
function projectHash() {
|
|
581
|
+
try {
|
|
582
|
+
return createHash("md5").update(`${process.env.PROJECT_DIR || process.cwd()}
|
|
583
|
+
`).digest("hex").slice(0, 8);
|
|
584
|
+
} catch {
|
|
585
|
+
return "default";
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
var DEV_FLAG = `/tmp/add_dev_${projectHash()}`;
|
|
589
|
+
function markDevAction() {
|
|
590
|
+
try {
|
|
591
|
+
writeFileSync(DEV_FLAG, "");
|
|
592
|
+
} catch {
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// templates/core/governance/notify.ts
|
|
597
|
+
import { existsSync as existsSync2, mkdirSync, renameSync, statSync, appendFileSync } from "node:fs";
|
|
598
|
+
import { join as join2 } from "node:path";
|
|
599
|
+
function writeHookEvent(hook, decision, cmd, reason, plan = "unknown", status = "none", extra = "", magicDirOverride) {
|
|
600
|
+
const defaults = protocol.adapter_defaults;
|
|
601
|
+
const fallback = defaults?.magic_dir_fallback ?? ".qoder";
|
|
602
|
+
const dir = join2(magicDirOverride ?? (process.env.MAGIC_DIR || fallback), "reports");
|
|
603
|
+
try {
|
|
604
|
+
mkdirSync(dir, { recursive: true });
|
|
605
|
+
} catch {
|
|
606
|
+
}
|
|
607
|
+
const file = join2(dir, "hook-events.jsonl");
|
|
608
|
+
if (existsSync2(file)) {
|
|
609
|
+
let sz = 0;
|
|
610
|
+
try {
|
|
611
|
+
sz = statSync(file).size;
|
|
612
|
+
} catch {
|
|
613
|
+
sz = 0;
|
|
614
|
+
}
|
|
615
|
+
if (sz > event.file.rotate_bytes) {
|
|
616
|
+
try {
|
|
617
|
+
renameSync(file, `${file}.old`);
|
|
618
|
+
} catch {
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d+Z$/, "Z");
|
|
623
|
+
const extraPart = extra ? `,${extra}` : "";
|
|
624
|
+
const line = `{"ts":"${ts}","hook":"${hook}","decision":"${decision}","cmd":"${cmd}","reason":"${reason}","planKeyword":"${plan}","planStatus":"${status}"${extraPart}}
|
|
625
|
+
`;
|
|
626
|
+
appendFileSync(file, line);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// templates/core/governance/pre-tool-guard.ts
|
|
630
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, appendFileSync as appendFileSync2, statSync as statSync2 } from "node:fs";
|
|
631
|
+
import { join as join3, basename as basename2 } from "node:path";
|
|
632
|
+
function askJson(reason) {
|
|
633
|
+
return `{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"ask","permissionDecisionReason":"${reason}"}}`;
|
|
634
|
+
}
|
|
635
|
+
function allowJson(reason) {
|
|
636
|
+
return `{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","permissionDecisionReason":"${reason}"}}`;
|
|
637
|
+
}
|
|
638
|
+
function denyJson(reason) {
|
|
639
|
+
return `{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"${reason}"}}`;
|
|
640
|
+
}
|
|
641
|
+
var PreToolUseGuard = class {
|
|
642
|
+
projectDir;
|
|
643
|
+
magicDir;
|
|
644
|
+
planKeyword;
|
|
645
|
+
planStatus;
|
|
646
|
+
adapterName;
|
|
647
|
+
constructor(projectDir, magicDir, adapterName = "core") {
|
|
648
|
+
this.projectDir = projectDir;
|
|
649
|
+
this.magicDir = magicDir;
|
|
650
|
+
this.adapterName = adapterName;
|
|
651
|
+
const active = detectActiveAdd();
|
|
652
|
+
if (active !== null) {
|
|
653
|
+
this.planKeyword = active.split("::")[0] ?? "";
|
|
654
|
+
this.planStatus = "active";
|
|
655
|
+
} else {
|
|
656
|
+
this.planKeyword = "no-active-plan";
|
|
657
|
+
this.planStatus = "none";
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
// ─────────────────────────── 扩展点 ───────────────────────────
|
|
661
|
+
/** 检测链加载(真源: [guard.detectors] 基线 / [guard.adapter_detectors] 独立链) */
|
|
662
|
+
detectorChain() {
|
|
663
|
+
if (this.adapterName === "core") {
|
|
664
|
+
return guard.detectors ?? [];
|
|
665
|
+
}
|
|
666
|
+
const all = guard.adapter_detectors ?? [];
|
|
667
|
+
return all.filter((d) => d.adapter === this.adapterName);
|
|
668
|
+
}
|
|
669
|
+
/** §A 阻断输出(core 协议: stderr + askJson + 阻断日志 + 事件 + 2) */
|
|
670
|
+
onBlock(blocked, command) {
|
|
671
|
+
process.stderr.write(blocked.stderr);
|
|
672
|
+
process.stdout.write(askJson(blocked.reason) + "\n");
|
|
673
|
+
this.logBlock("\u68C0\u6D4B\u5668\u94FE", command);
|
|
674
|
+
writeHookEvent("pre-tool-use", "deny", command, blocked.reason, this.planKeyword, this.planStatus);
|
|
675
|
+
return 2;
|
|
676
|
+
}
|
|
677
|
+
/** §A 放行后处理(core: 无操作;claude: bash 原文 mark_dev_action) */
|
|
678
|
+
onSectionAPass(_command) {
|
|
679
|
+
}
|
|
680
|
+
/** §B 无 Plan 放行(core 协议: 提示 + allowJson + 事件 + 0) */
|
|
681
|
+
onNoPlanAllow(toolName2, filePath) {
|
|
682
|
+
process.stderr.write("[ADD \u63D0\u793A] \u6B63\u5728\u5199\u5165 Plan/Spec/Review \u6587\u6863\u4F46\u65E0\u6D3B\u8DC3 ADD Plan\u2014\u2014\u9996\u6B21\u521B\u5EFA\u573A\u666F\u653E\u884C\uFF0C\u5EFA\u8BAE\u5148\u6267\u884C add-paradigm \u751F\u6210 Plan+add-route\n");
|
|
683
|
+
process.stdout.write(allowJson("\u65E0\u6D3B\u8DC3 ADD Plan \u4F46\u4E3A Plan/Spec/Review \u5199\u5165\uFF08\u9996\u6B21\u521B\u5EFA\u573A\u666F\uFF09\uFF0C\u63D0\u793A\u800C\u975E\u62E6\u622A") + "\n");
|
|
684
|
+
writeHookEvent("pre-tool-use", "info", `${toolName2} ${filePath}`, "\u65E0\u6D3B\u8DC3 ADD Plan \u4E0B\u5199\u5165 Plan/Spec/Review\uFF08\u9996\u6B21\u521B\u5EFA\u653E\u884C\uFF09", this.planKeyword, this.planStatus);
|
|
685
|
+
return 0;
|
|
686
|
+
}
|
|
687
|
+
/** §B 敏感文件阻断(core 协议: stderr + denyJson + 2) */
|
|
688
|
+
onSensitiveDeny(filePath) {
|
|
689
|
+
const sensReason = guard.sensitive_files.deny_reason;
|
|
690
|
+
process.stderr.write(`\u26D4 \u654F\u611F\u6587\u4EF6\u53D7\u4FDD\u62A4\uFF0C\u7981\u6B62\u5199\u5165: ${filePath}
|
|
691
|
+
`);
|
|
692
|
+
process.stdout.write(denyJson(sensReason) + "\n");
|
|
693
|
+
return 2;
|
|
694
|
+
}
|
|
695
|
+
/** 大文件适配提示文本(core: payload 限制;qoder: Qoder 40500 错误码) */
|
|
696
|
+
largeFileText(fsize) {
|
|
697
|
+
return `\u26A0\uFE0F [ADD PreToolUse] \u6587\u4EF6\u5DF2\u6709 ${fsize} \u5B57\u8282\uFF0CWrite \u5168\u91CF\u8986\u76D6\u53EF\u80FD\u89E6\u53D1\u5DE5\u5177 payload \u9650\u5236\u3002\u5EFA\u8BAE\u7528 SearchReplace \u5206\u5757\u8FFD\u52A0\u3002
|
|
698
|
+
`;
|
|
699
|
+
}
|
|
700
|
+
/** HITL 未 tongyi 输出(core 协议: stderr 3 行 + JSON deny + 事件 + exit 0;qoder: exit 2 + event ask) */
|
|
701
|
+
onHitlDeny(toolName2, filePath, tongyiMarker) {
|
|
702
|
+
process.stderr.write(`\u26D4 [ADD PreToolUse \xA7C] HITL \u672A tongyi: ${filePath}
|
|
703
|
+
`);
|
|
704
|
+
process.stderr.write(` \u539F\u56E0: \u54E8\u5175\u6587\u4EF6 ${tongyiMarker} \u4E0D\u5B58\u5728
|
|
705
|
+
`);
|
|
706
|
+
process.stderr.write(' \u64CD\u4F5C: \u8BF7\u5148\u8C03\u7528 create_hitl \u521B\u5EFA\u5BA1\u6279\uFF0C\u518D update_hitl({ status: "TONGYI" })\n');
|
|
707
|
+
const reason = `HITL \u672A tongyi: \u54E8\u5175 ${tongyiMarker} \u4E0D\u5B58\u5728\u3002\u8BF7\u5148 create_hitl \u2192 \u4EBA\u5DE5 tongyi \u2192 update_hitl \u540E\u518D\u5199\u5165`;
|
|
708
|
+
process.stdout.write(
|
|
709
|
+
`{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"${reason}","additionalContext":"${reason}"}}
|
|
710
|
+
`
|
|
711
|
+
);
|
|
712
|
+
writeHookEvent("pre-tool-use", "deny", `${toolName2} ${filePath}`, `HITL \u672A tongyi: ${tongyiMarker}`, this.planKeyword, this.planStatus);
|
|
713
|
+
return 0;
|
|
714
|
+
}
|
|
715
|
+
/** 附加 matcher(core: 无;claude: ③ Read 模板提示) */
|
|
716
|
+
onOtherTool(_input, _toolName) {
|
|
717
|
+
return 0;
|
|
718
|
+
}
|
|
719
|
+
// ─────────────────────────── 流程固化 ───────────────────────────
|
|
720
|
+
/** 阻断日志(对齐 bash _log_block:追加到 debug-dump/stdin.log) */
|
|
721
|
+
logBlock(rule, cmd) {
|
|
722
|
+
try {
|
|
723
|
+
const dir = join3(this.projectDir, this.magicDir, "debug-dump");
|
|
724
|
+
mkdirSync2(dir, { recursive: true });
|
|
725
|
+
appendFileSync2(
|
|
726
|
+
join3(dir, "stdin.log"),
|
|
727
|
+
`=== ${localIsoSeconds()} [BLOCKED by \xA7A: ${rule}] ===
|
|
728
|
+
command: ${cmd.slice(0, 300)}
|
|
729
|
+
=== DONE ===
|
|
730
|
+
`
|
|
731
|
+
);
|
|
732
|
+
} catch {
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
/** §A 检测器链判定:返回阻断结果,放行返回 null */
|
|
736
|
+
checkBashCommand(command) {
|
|
737
|
+
for (const d of this.detectorChain()) {
|
|
738
|
+
if (new RegExp(d.regex, d.flags ?? "").test(command)) {
|
|
739
|
+
return { reason: d.reason, stderr: d.stderr };
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
return null;
|
|
743
|
+
}
|
|
744
|
+
/** §A 入口:Bash 工具写入保护(模板方法) */
|
|
745
|
+
runSectionA(input2) {
|
|
746
|
+
const command = jsonGet(input2, "command");
|
|
747
|
+
if (command === "") return 0;
|
|
748
|
+
const blocked = this.checkBashCommand(command);
|
|
749
|
+
if (blocked) {
|
|
750
|
+
return this.onBlock(blocked, command);
|
|
751
|
+
}
|
|
752
|
+
this.onSectionAPass(command);
|
|
753
|
+
return 0;
|
|
754
|
+
}
|
|
755
|
+
/** §B 入口:Write/Edit 文件写入前置守卫(模板方法) */
|
|
756
|
+
runSectionB(input2, toolName2) {
|
|
757
|
+
if (toolName2 !== "Write" && toolName2 !== "Edit" && toolName2 !== "SearchReplace") return 0;
|
|
758
|
+
const filePath = jsonGet(input2, "file_path");
|
|
759
|
+
if (filePath === "") return 0;
|
|
760
|
+
if (new RegExp(`${this.magicDir}/(plans|specs|reviews)/`).test(filePath)) {
|
|
761
|
+
const state = detectActiveAdd();
|
|
762
|
+
if (state === null) {
|
|
763
|
+
if (this.noPlanHint()) {
|
|
764
|
+
process.stderr.write(`[ADD PreToolUse] \u6B63\u5728\u4FEE\u6539 ADD \u6587\u6863\u4F46\u672A\u68C0\u6D4B\u5230\u6D3B\u8DC3 Plan: ${filePath}
|
|
765
|
+
`);
|
|
766
|
+
} else {
|
|
767
|
+
return this.onNoPlanAllow(toolName2, filePath);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
const guardCode = this.guardFilePath(filePath, toolName2);
|
|
772
|
+
if (guardCode !== 0) return guardCode;
|
|
773
|
+
markDevAction();
|
|
774
|
+
return 0;
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* guard 核心(敏感文件 → HITL → 模板提示;codex apply_patch 路径逐文件复用):
|
|
778
|
+
* 敏感 → onSensitiveDeny;HITL → onHitlDeny;模板提示/大文件 → stderr
|
|
779
|
+
* 返回阻断码(非 0 = 阻断,调用方必须透传 exit)——
|
|
780
|
+
* 2026-08-14 Task 9.4 修复: 原返回 void 丢弃 onSensitiveDeny/onHitlDeny 的 exit 2(敏感文件拦截形同虚设)
|
|
781
|
+
*/
|
|
782
|
+
guardFilePath(filePath, toolName2) {
|
|
783
|
+
const sensRegex = this.sensitiveFileRegex();
|
|
784
|
+
if (new RegExp(sensRegex).test(filePath)) {
|
|
785
|
+
return this.onSensitiveDeny(filePath);
|
|
786
|
+
}
|
|
787
|
+
if (new RegExp(`${this.magicDir}/(plans)/`).test(filePath)) {
|
|
788
|
+
const fname = basename2(filePath);
|
|
789
|
+
for (const hint of guard.template_hints ?? []) {
|
|
790
|
+
if (new RegExp(hint.pattern).test(fname)) {
|
|
791
|
+
process.stderr.write(hint.message + "\n");
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
if (toolName2 === "Write" && existsSync3(filePath)) {
|
|
795
|
+
const fsize = statSync2(filePath).size;
|
|
796
|
+
const limit = guard.thresholds.large_file_bytes;
|
|
797
|
+
if (fsize > limit) {
|
|
798
|
+
process.stderr.write(this.largeFileText(fsize));
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
let doHitl = false;
|
|
803
|
+
if (new RegExp(`${this.magicDir}/(plans)/`).test(filePath)) {
|
|
804
|
+
doHitl = !filePath.includes("-handoff");
|
|
805
|
+
} else if (new RegExp(`${this.magicDir}/(reviews)/`).test(filePath)) {
|
|
806
|
+
doHitl = !this.hitlExemptReviews().test(filePath);
|
|
807
|
+
}
|
|
808
|
+
if (doHitl) {
|
|
809
|
+
if (!new RegExp(`${this.magicDir}/(plans|reviews)/`).test(filePath)) {
|
|
810
|
+
doHitl = false;
|
|
811
|
+
} else {
|
|
812
|
+
const relative = filePath.replace(/.*\/\.(qoder|claude|add|vscode|trae|codex)\/(plans|reviews)\//, "");
|
|
813
|
+
const planName = basename2(relative, ".md").replace(/\.hitl$/, "").replace(/-plan-v\d*$/, "").replace(/-add-route-v\d*$/, "").replace(/-review-v\d*$/, "").replace(/-review-implementation$/, "").replace(/-review-runtime$/, "");
|
|
814
|
+
if (planName !== "") {
|
|
815
|
+
const markers = this.hitlMarkers(planName);
|
|
816
|
+
if (!markers.some((m) => existsSync3(join3(this.projectDir, this.magicDir, "hitl", m)))) {
|
|
817
|
+
const tongyiMarker = markers[0];
|
|
818
|
+
return this.onHitlDeny(toolName2, filePath, tongyiMarker);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
return 0;
|
|
824
|
+
}
|
|
825
|
+
// ─────────────────────────── 扩展点(续)───────────────────────────
|
|
826
|
+
/** 敏感文件正则(core: TOML 真源;codex 子类: 锚定版 `(^|\/)(...)$|credentials|secrets`) */
|
|
827
|
+
sensitiveFileRegex() {
|
|
828
|
+
return guard.sensitive_files.regex;
|
|
829
|
+
}
|
|
830
|
+
/** Reviews HITL 豁免(仅 -runtime;review-implementation-* 也走 HITL——2026-08-14 Task 9.4.4③ 上提,回流: I2) */
|
|
831
|
+
hitlExemptReviews() {
|
|
832
|
+
return /-runtime/;
|
|
833
|
+
}
|
|
834
|
+
/** HITL 哨兵([full, base] 双哨兵——2026-08-14 Task 9.4.4② 上提,回流: I2;
|
|
835
|
+
* 与 MCP update_hitl 双命名哨兵(原始 planName + 剥后缀推导名)对齐) */
|
|
836
|
+
hitlMarkers(planName) {
|
|
837
|
+
const base = planName.replace(/-plan-v\d*$/, "").replace(/-add-route-v\d*$/, "").replace(/-review-v\d*$/, "").replace(/-review-implementation$/, "").replace(/-review-runtime$/, "");
|
|
838
|
+
return [`.tongyi-${planName}`, `.tongyi-${base}`];
|
|
839
|
+
}
|
|
840
|
+
/** 无 Plan 分支语义(core: onNoPlanAllow return;codex 子类: 仅 stderr 提示后继续) */
|
|
841
|
+
noPlanHint() {
|
|
842
|
+
return false;
|
|
843
|
+
}
|
|
844
|
+
/** §C 入口:其他工具 matcher(模板方法,默认委托扩展点) */
|
|
845
|
+
runSectionC(input2, toolName2) {
|
|
846
|
+
return this.onOtherTool(input2, toolName2);
|
|
847
|
+
}
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
// templates/adapters/vscode/hooks/pre-tool-use.ts
|
|
851
|
+
var PROJECT_DIR = process.env.PROJECT_DIR || process.cwd();
|
|
852
|
+
var inferred = tryResolveMagicDir();
|
|
853
|
+
if (inferred && !process.env.MAGIC_DIR) process.env.MAGIC_DIR = inferred;
|
|
854
|
+
var MAGIC_DIR = process.env.MAGIC_DIR || "";
|
|
855
|
+
var VscodePreToolUseGuard = class extends PreToolUseGuard {
|
|
856
|
+
constructor(projectDir, magicDir) {
|
|
857
|
+
super(projectDir, magicDir, "vscode");
|
|
858
|
+
}
|
|
859
|
+
/** ② §A 阻断: 纯 stderr({{cmd}} 模板替换)+ 事件 + exit 2(无 askJson / logBlock) */
|
|
860
|
+
onBlock(blocked, command) {
|
|
861
|
+
process.stderr.write(blocked.stderr.replaceAll("{{cmd}}", command));
|
|
862
|
+
writeHookEvent("pre-tool-use", "deny", command, blocked.reason, this.planKeyword, this.planStatus);
|
|
863
|
+
return 2;
|
|
864
|
+
}
|
|
865
|
+
/** ③ §B 无 Plan 放行: 提示 + allow JSON + 事件 + exit 2(bash 原文缺陷照搬) */
|
|
866
|
+
onNoPlanAllow(toolName2, filePath) {
|
|
867
|
+
process.stderr.write("[ADD \u63D0\u793A] \u6B63\u5728\u5199\u5165 Plan/Spec/Review \u6587\u6863\u4F46\u65E0\u6D3B\u8DC3 ADD Plan\u2014\u2014\u9996\u6B21\u521B\u5EFA\u573A\u666F\u653E\u884C\uFF0C\u5EFA\u8BAE\u5148\u6267\u884C add-paradigm \u751F\u6210 Plan+add-route\n");
|
|
868
|
+
process.stdout.write('{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","permissionDecisionReason":"\u65E0\u6D3B\u8DC3 ADD Plan \u4F46\u4E3A Plan/Spec/Review \u5199\u5165\uFF08\u9996\u6B21\u521B\u5EFA\u573A\u666F\uFF09\uFF0C\u63D0\u793A\u800C\u975E\u62E6\u622A"}}\n');
|
|
869
|
+
writeHookEvent("pre-tool-use", "info", filePath, "\u65E0\u6D3B\u8DC3 ADD Plan \u4E0B\u5199\u5165 Plan/Spec/Review\uFF08\u9996\u6B21\u521B\u5EFA\u653E\u884C\uFF09", this.planKeyword, this.planStatus);
|
|
870
|
+
return 2;
|
|
871
|
+
}
|
|
872
|
+
/** ④ §B 敏感文件: 纯 stderr(无 denyJson)+ 事件 + exit 2 */
|
|
873
|
+
onSensitiveDeny(filePath) {
|
|
874
|
+
process.stderr.write(`\u26D4 \u654F\u611F\u6587\u4EF6\u53D7\u4FDD\u62A4\uFF0C\u7981\u6B62\u5199\u5165: ${filePath}
|
|
875
|
+
`);
|
|
876
|
+
writeHookEvent("pre-tool-use", "deny", filePath, "\u654F\u611F\u6587\u4EF6\u53D7\u4FDD\u62A4", this.planKeyword, this.planStatus);
|
|
877
|
+
return 2;
|
|
878
|
+
}
|
|
879
|
+
/** ⑤ §A 放行后处理: bash 原文 mark_dev_action */
|
|
880
|
+
onSectionAPass(_command) {
|
|
881
|
+
markDevAction();
|
|
882
|
+
}
|
|
883
|
+
/** ⑥ ③ Read matcher: 模板路径兜底提示 */
|
|
884
|
+
onOtherTool(input2, toolName2) {
|
|
885
|
+
if (toolName2 === "Read") {
|
|
886
|
+
const filePath = jsonGet(input2, "file_path");
|
|
887
|
+
if (/templates\//.test(filePath)) {
|
|
888
|
+
process.stderr.write("[ADD PreToolUse] \u63D0\u793A: \u6A21\u677F\u6587\u4EF6\u5DF2\u901A\u8FC7 hook \u9884\u8BFB\u5230\u4E0A\u4E0B\u6587\uFF0C\u53EF\u8DF3\u8FC7\u91CD\u590D\u8BFB\u53D6\n");
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
return 0;
|
|
892
|
+
}
|
|
893
|
+
};
|
|
894
|
+
var input = readHookInput();
|
|
895
|
+
var toolName = jsonGet(input, "tool_name");
|
|
896
|
+
if (toolName === "") {
|
|
897
|
+
toolName = input.match(/"tool_name"\s*:\s*"([^"]*)"/)?.[1] ?? "";
|
|
898
|
+
}
|
|
899
|
+
var guard2 = new VscodePreToolUseGuard(PROJECT_DIR, MAGIC_DIR);
|
|
900
|
+
if (toolName === "Bash") {
|
|
901
|
+
process.exit(guard2.runSectionA(input));
|
|
902
|
+
} else if (toolName === "Write" || toolName === "Edit" || toolName === "SearchReplace") {
|
|
903
|
+
process.exit(guard2.runSectionB(input, toolName));
|
|
904
|
+
} else {
|
|
905
|
+
process.exit(guard2.runSectionC(input, toolName));
|
|
906
|
+
}
|