@kkelly-offical/kkcode 0.1.6 → 0.2.1

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 (163) hide show
  1. package/LICENSE +674 -674
  2. package/README.md +452 -387
  3. package/package.json +50 -46
  4. package/src/agent/agent.mjs +19 -2
  5. package/src/agent/custom-agent-loader.mjs +6 -3
  6. package/src/agent/generator.mjs +2 -2
  7. package/src/agent/prompt/assistant.txt +12 -0
  8. package/src/agent/prompt/bug-hunter.txt +90 -0
  9. package/src/agent/prompt/frontend-designer.txt +58 -58
  10. package/src/agent/prompt/guide.txt +1 -1
  11. package/src/agent/prompt/longagent-blueprint-agent.txt +83 -83
  12. package/src/agent/prompt/longagent-coding-agent.txt +37 -37
  13. package/src/agent/prompt/longagent-debugging-agent.txt +46 -46
  14. package/src/agent/prompt/longagent-preview-agent.txt +63 -63
  15. package/src/command/custom-commands.mjs +2 -2
  16. package/src/commands/agent.mjs +1 -1
  17. package/src/commands/background.mjs +145 -4
  18. package/src/commands/chat.mjs +117 -76
  19. package/src/commands/config.mjs +148 -1
  20. package/src/commands/doctor.mjs +30 -6
  21. package/src/commands/init.mjs +32 -6
  22. package/src/commands/longagent.mjs +117 -0
  23. package/src/commands/mcp.mjs +275 -43
  24. package/src/commands/permission.mjs +1 -1
  25. package/src/commands/session.mjs +195 -140
  26. package/src/commands/skill.mjs +63 -0
  27. package/src/commands/theme.mjs +1 -1
  28. package/src/config/defaults.mjs +280 -260
  29. package/src/config/import-config.mjs +1 -1
  30. package/src/config/load-config.mjs +61 -4
  31. package/src/config/schema.mjs +591 -574
  32. package/src/context.mjs +4 -1
  33. package/src/core/constants.mjs +97 -91
  34. package/src/core/types.mjs +1 -1
  35. package/src/github/api.mjs +78 -78
  36. package/src/github/auth.mjs +294 -286
  37. package/src/github/flow.mjs +298 -298
  38. package/src/github/workspace.mjs +225 -212
  39. package/src/index.mjs +84 -82
  40. package/src/knowledge/frontend-aesthetics.txt +38 -38
  41. package/src/mcp/client-http.mjs +139 -141
  42. package/src/mcp/client-sse.mjs +297 -288
  43. package/src/mcp/client-stdio.mjs +534 -533
  44. package/src/mcp/constants.mjs +2 -2
  45. package/src/mcp/registry.mjs +498 -479
  46. package/src/mcp/stdio-framing.mjs +135 -133
  47. package/src/mcp/tool-result.mjs +24 -24
  48. package/src/observability/edit-diagnostics.mjs +449 -0
  49. package/src/observability/index.mjs +42 -42
  50. package/src/observability/metrics.mjs +165 -137
  51. package/src/observability/tracer.mjs +137 -137
  52. package/src/onboarding.mjs +209 -0
  53. package/src/orchestration/background-manager.mjs +567 -372
  54. package/src/orchestration/background-worker.mjs +419 -305
  55. package/src/orchestration/interruption-reason.mjs +21 -0
  56. package/src/orchestration/longagent-manager.mjs +197 -171
  57. package/src/orchestration/stage-scheduler.mjs +733 -728
  58. package/src/orchestration/subagent-router.mjs +7 -1
  59. package/src/orchestration/task-scheduler.mjs +219 -7
  60. package/src/permission/engine.mjs +1 -1
  61. package/src/permission/exec-policy.mjs +370 -370
  62. package/src/permission/file-edit-policy.mjs +108 -0
  63. package/src/permission/prompt.mjs +1 -1
  64. package/src/permission/rules.mjs +116 -7
  65. package/src/plugin/builtin-hooks/post-edit-format.mjs +2 -1
  66. package/src/plugin/builtin-hooks/post-edit-typecheck.mjs +104 -40
  67. package/src/plugin/hook-bus.mjs +19 -5
  68. package/src/plugin/manifest-loader.mjs +222 -0
  69. package/src/provider/anthropic.mjs +396 -390
  70. package/src/provider/ollama.mjs +7 -1
  71. package/src/provider/openai.mjs +382 -340
  72. package/src/provider/retry-policy.mjs +74 -68
  73. package/src/provider/router.mjs +242 -241
  74. package/src/provider/sse.mjs +104 -104
  75. package/src/provider/wizard.mjs +556 -0
  76. package/src/repl/capability-facade.mjs +30 -0
  77. package/src/repl/command-surface.mjs +23 -0
  78. package/src/repl/controller-entry.mjs +40 -0
  79. package/src/repl/core-shell.mjs +208 -0
  80. package/src/repl/dialog-router.mjs +87 -0
  81. package/src/repl/input-engine.mjs +76 -0
  82. package/src/repl/keymap.mjs +7 -0
  83. package/src/repl/operator-surface.mjs +15 -0
  84. package/src/repl/permission-flow.mjs +49 -0
  85. package/src/repl/runtime-facade.mjs +36 -0
  86. package/src/repl/slash-router.mjs +62 -0
  87. package/src/repl/state-store.mjs +29 -0
  88. package/src/repl/turn-controller.mjs +58 -0
  89. package/src/repl/verification.mjs +23 -0
  90. package/src/repl.mjs +3368 -2929
  91. package/src/rules/load-rules.mjs +3 -3
  92. package/src/runtime.mjs +1 -1
  93. package/src/session/agent-transaction.mjs +86 -0
  94. package/src/session/checkpoint.mjs +302 -302
  95. package/src/session/compaction.mjs +36 -14
  96. package/src/session/engine.mjs +417 -227
  97. package/src/session/longagent-4stage.mjs +467 -460
  98. package/src/session/longagent-hybrid.mjs +1344 -1081
  99. package/src/session/longagent-plan.mjs +376 -365
  100. package/src/session/longagent-project-memory.mjs +53 -53
  101. package/src/session/longagent-scaffold.mjs +291 -291
  102. package/src/session/longagent-task-bus.mjs +138 -54
  103. package/src/session/longagent-utils.mjs +828 -472
  104. package/src/session/longagent.mjs +911 -884
  105. package/src/session/loop.mjs +1005 -905
  106. package/src/session/prompt/agent.txt +25 -0
  107. package/src/session/prompt/anthropic.txt +150 -150
  108. package/src/session/prompt/beast.txt +1 -1
  109. package/src/session/prompt/plan.txt +28 -6
  110. package/src/session/prompt/qwen.txt +46 -46
  111. package/src/session/recovery.mjs +21 -0
  112. package/src/session/rollback.mjs +197 -0
  113. package/src/session/routing-observability.mjs +72 -0
  114. package/src/session/runtime-state.mjs +47 -0
  115. package/src/session/store.mjs +523 -510
  116. package/src/session/system-prompt.mjs +56 -8
  117. package/src/session/task-validator.mjs +267 -267
  118. package/src/session/usability-gates.mjs +2 -2
  119. package/src/skill/builtin/commit.mjs +64 -64
  120. package/src/skill/builtin/design.mjs +76 -76
  121. package/src/skill/generator.mjs +18 -2
  122. package/src/skill/registry.mjs +642 -390
  123. package/src/storage/audit-store.mjs +18 -11
  124. package/src/storage/event-log.mjs +7 -1
  125. package/src/storage/ghost-commit-store.mjs +243 -245
  126. package/src/storage/paths.mjs +13 -0
  127. package/src/theme/default-theme.mjs +1 -1
  128. package/src/theme/markdown.mjs +4 -0
  129. package/src/theme/schema.mjs +1 -1
  130. package/src/theme/status-bar.mjs +162 -158
  131. package/src/tool/audit-wrapper.mjs +18 -2
  132. package/src/tool/edit-transaction.mjs +23 -0
  133. package/src/tool/executor.mjs +26 -1
  134. package/src/tool/file-read-state.mjs +65 -0
  135. package/src/tool/git-auto.mjs +526 -526
  136. package/src/tool/git-full-auto.mjs +487 -478
  137. package/src/tool/mutation-guard.mjs +54 -0
  138. package/src/tool/prompt/edit.txt +3 -3
  139. package/src/tool/prompt/multiedit.txt +1 -0
  140. package/src/tool/prompt/notebookedit.txt +2 -1
  141. package/src/tool/prompt/patch.txt +25 -24
  142. package/src/tool/prompt/read.txt +3 -3
  143. package/src/tool/prompt/sysinfo.txt +29 -0
  144. package/src/tool/prompt/task.txt +66 -4
  145. package/src/tool/prompt/write.txt +2 -2
  146. package/src/tool/question-prompt.mjs +17 -4
  147. package/src/tool/registry.mjs +1701 -1343
  148. package/src/tool/task-tool.mjs +14 -6
  149. package/src/ui/activity-renderer.mjs +667 -664
  150. package/src/ui/repl-background-panel.mjs +7 -0
  151. package/src/ui/repl-capability-panel.mjs +9 -0
  152. package/src/ui/repl-dashboard.mjs +54 -4
  153. package/src/ui/repl-help.mjs +110 -0
  154. package/src/ui/repl-operator-panel.mjs +12 -0
  155. package/src/ui/repl-route-feedback.mjs +35 -0
  156. package/src/ui/repl-status-view.mjs +76 -0
  157. package/src/ui/repl-task-panel.mjs +5 -0
  158. package/src/ui/repl-transcript-panel.mjs +56 -0
  159. package/src/ui/repl-turn-summary.mjs +135 -0
  160. package/src/usage/pricing.mjs +122 -121
  161. package/src/usage/usage-meter.mjs +1 -0
  162. package/src/util/git.mjs +562 -519
  163. package/src/util/template.mjs +6 -1
@@ -1,370 +1,370 @@
1
- /**
2
- * 执行策略 (Execution Policy)
3
- *
4
- * 定义 AI Agent 执行命令的安全规则。
5
- * 参考 Codex 的设计理念:
6
- * - AI 可以修改文件(通过 patch)
7
- * - AI 可以查看 Git 状态
8
- * - AI 不能执行某些危险操作(如 git commit, git push)
9
- *
10
- * 安全原则:
11
- * 1. 最小权限原则 - AI 只拥有完成任务必需的最小权限
12
- * 2. 用户保留控制权 - 关键操作(commit/push)必须由用户手动执行
13
- * 3. 透明可审计 - 所有策略决策都有明确的原因
14
- */
15
-
16
- /**
17
- * 决策类型
18
- */
19
- export const Decision = {
20
- ALLOW: "allow", // 允许执行
21
- FORBID: "forbid", // 禁止执行
22
- WARN: "warn", // 警告但允许(记录日志)
23
- CONFIRM: "confirm" // 需要用户确认
24
- }
25
-
26
- /**
27
- * 规则匹配器
28
- */
29
- function createMatcher(pattern) {
30
- if (typeof pattern === "string") {
31
- return (cmd) => cmd.includes(pattern)
32
- }
33
- if (pattern instanceof RegExp) {
34
- return (cmd) => pattern.test(cmd)
35
- }
36
- if (Array.isArray(pattern)) {
37
- // RegExp 数组:任一匹配即命中
38
- if (pattern[0] instanceof RegExp) {
39
- return (cmd) => pattern.some(re => re.test(cmd))
40
- }
41
- // 字符串数组:按顺序包含所有元素(用于匹配 git commit)
42
- return (cmd) => {
43
- const parts = cmd.toLowerCase().split(/\s+/)
44
- let patternIdx = 0
45
- for (const part of parts) {
46
- if (patternIdx < pattern.length && part === pattern[patternIdx].toLowerCase()) {
47
- patternIdx++
48
- }
49
- }
50
- return patternIdx === pattern.length
51
- }
52
- }
53
- return () => false
54
- }
55
-
56
- /**
57
- * 执行策略规则
58
- */
59
- const DEFAULT_RULES = [
60
- // =========================================================================
61
- // 高危操作 - 禁止
62
- // =========================================================================
63
-
64
- {
65
- name: "forbid_git_commit",
66
- pattern: ["git", "commit"],
67
- decision: Decision.FORBID,
68
- reason: "AI cannot directly create git commits. Use git_snapshot to create temporary snapshots instead. Users must manually run 'git commit' to finalize changes.",
69
- category: "git_safety"
70
- },
71
- {
72
- name: "forbid_git_push",
73
- pattern: ["git", "push"],
74
- decision: Decision.FORBID,
75
- reason: "AI cannot push to remote repositories. Users must manually review and push changes.",
76
- category: "git_safety"
77
- },
78
- {
79
- name: "forbid_git_force_push",
80
- pattern: [/git\s+push\s+.*--force/, /git\s+push\s+.*-f\b/],
81
- decision: Decision.FORBID,
82
- reason: "Force push is extremely dangerous and can overwrite remote history. Never allowed for AI.",
83
- category: "git_safety"
84
- },
85
- {
86
- name: "forbid_git_reset_hard",
87
- pattern: ["git", "reset", "--hard"],
88
- decision: Decision.FORBID,
89
- reason: "Hard reset destroys uncommitted changes. Use git_restore with a snapshot ID instead.",
90
- category: "git_safety"
91
- },
92
- {
93
- name: "forbid_git_clean_fd",
94
- pattern: [/git\s+clean\s+.*-f/, /git\s+clean\s+.*-d/],
95
- decision: Decision.FORBID,
96
- reason: "git clean -f/-d deletes untracked files and directories. Dangerous operation.",
97
- category: "git_safety"
98
- },
99
-
100
- // =========================================================================
101
- // 文件系统危险操作 - 禁止
102
- // =========================================================================
103
-
104
- {
105
- name: "forbid_rm_rf",
106
- pattern: [/rm\s+-rf?\s+\//, /rm\s+.*\*\s+.*\/\.\.?/],
107
- decision: Decision.FORBID,
108
- reason: "Dangerous file deletion pattern detected. Cannot delete system directories or use wildcards with relative path traversals.",
109
- category: "fs_safety"
110
- },
111
- {
112
- name: "forbid_dd_disk",
113
- pattern: [/dd\s+.*of=\/dev\//],
114
- decision: Decision.FORBID,
115
- reason: "Direct disk write operations are forbidden.",
116
- category: "fs_safety"
117
- },
118
- {
119
- name: "forbid_mkfs",
120
- pattern: ["mkfs"],
121
- decision: Decision.FORBID,
122
- reason: "Filesystem formatting operations are forbidden.",
123
- category: "fs_safety"
124
- },
125
-
126
- // =========================================================================
127
- // 网络危险操作 - 禁止
128
- // =========================================================================
129
-
130
- {
131
- name: "forbid_curl_pipe_sh",
132
- pattern: [/curl\s+.*\|\s*(ba)?sh/, /wget\s+.*\|\s*(ba)?sh/],
133
- decision: Decision.FORBID,
134
- reason: "Piping curl/wget output directly to shell is dangerous and can execute arbitrary code.",
135
- category: "network_safety"
136
- },
137
-
138
- // =========================================================================
139
- // 权限提升操作 - 需要确认
140
- // =========================================================================
141
-
142
- {
143
- name: "confirm_sudo",
144
- pattern: ["sudo"],
145
- decision: Decision.CONFIRM,
146
- reason: "Command requires elevated privileges. Please confirm this is necessary.",
147
- category: "privilege"
148
- },
149
- {
150
- name: "confirm_chmod_777",
151
- pattern: [/chmod\s+.*777/],
152
- decision: Decision.WARN,
153
- reason: "777 permissions grant full access to everyone. Consider using more restrictive permissions.",
154
- category: "privilege"
155
- },
156
-
157
- // =========================================================================
158
- // Git 信息查看 - 允许
159
- // =========================================================================
160
-
161
- {
162
- name: "allow_git_status",
163
- pattern: ["git", "status"],
164
- decision: Decision.ALLOW,
165
- reason: "Reading git status is safe and necessary.",
166
- category: "git_read"
167
- },
168
- {
169
- name: "allow_git_log",
170
- pattern: ["git", "log"],
171
- decision: Decision.ALLOW,
172
- reason: "Reading git history is safe.",
173
- category: "git_read"
174
- },
175
- {
176
- name: "allow_git_diff",
177
- pattern: ["git", "diff"],
178
- decision: Decision.ALLOW,
179
- reason: "Reading git diff is safe.",
180
- category: "git_read"
181
- },
182
- {
183
- name: "allow_git_show",
184
- pattern: ["git", "show"],
185
- decision: Decision.ALLOW,
186
- reason: "Reading commit details is safe.",
187
- category: "git_read"
188
- }
189
- ]
190
-
191
- /**
192
- * 执行策略评估结果
193
- */
194
- export class PolicyResult {
195
- constructor(decision, rule, reason, category) {
196
- this.decision = decision
197
- this.rule = rule
198
- this.reason = reason
199
- this.category = category
200
- }
201
-
202
- isAllowed() {
203
- return this.decision === Decision.ALLOW
204
- }
205
-
206
- isForbidden() {
207
- return this.decision === Decision.FORBID
208
- }
209
-
210
- needsConfirmation() {
211
- return this.decision === Decision.CONFIRM
212
- }
213
- }
214
-
215
- /**
216
- * 评估命令是否符合执行策略
217
- *
218
- * @param {string} command - 要评估的命令
219
- * @param {Object} options - 选项
220
- * @param {boolean} [options.strict=false] - 严格模式(默认禁止任何未明确允许的操作)
221
- * @param {Array} [options.customRules=[]] - 自定义规则
222
- * @returns {PolicyResult}
223
- */
224
- export function evaluateCommand(command, options = {}) {
225
- const { strict = false, customRules = [] } = options
226
- const rules = [...customRules, ...DEFAULT_RULES]
227
-
228
- const normalizedCmd = String(command || "").trim()
229
-
230
- for (const rule of rules) {
231
- const matcher = createMatcher(rule.pattern)
232
- if (matcher(normalizedCmd)) {
233
- return new PolicyResult(
234
- rule.decision,
235
- rule.name,
236
- rule.reason,
237
- rule.category
238
- )
239
- }
240
- }
241
-
242
- // 严格模式:未匹配任何规则则禁止
243
- if (strict) {
244
- return new PolicyResult(
245
- Decision.FORBID,
246
- "strict_mode",
247
- "Command not in allowlist (strict mode)",
248
- "strict"
249
- )
250
- }
251
-
252
- // 默认允许
253
- return new PolicyResult(
254
- Decision.ALLOW,
255
- "default",
256
- "No policy rules matched, allowing by default",
257
- "default"
258
- )
259
- }
260
-
261
- /**
262
- * 检查 bash 工具调用是否被允许
263
- *
264
- * @param {string} command - 命令
265
- * @param {Object} config - 配置
266
- * @returns {{allowed: boolean, reason?: string, warning?: string}}
267
- */
268
- export function checkBashAllowed(command, config = {}) {
269
- // 全自动化模式检查
270
- const fullAuto = config.git_auto?.full_auto === true
271
- const allowDangerous = config.git_auto?.allow_dangerous_ops === true
272
-
273
- // 检查是否配置了全局禁止 git commit/push
274
- // 全自动化模式下,如果 auto_commit/auto_push 启用,则允许
275
- const autoCommit = fullAuto && config.git_auto?.auto_commit === true
276
- const autoPush = fullAuto && config.git_auto?.auto_push === true
277
-
278
- if (!autoCommit && config.git_auto?.forbid_commit !== false) {
279
- const commitPattern = /^git\s+commit\b/i
280
- if (commitPattern.test(command)) {
281
- return {
282
- allowed: false,
283
- reason: "git commit is forbidden for AI. Use git_snapshot to create temporary snapshots, then manually commit when satisfied. Or enable git_auto.full_auto and git_auto.auto_commit for automatic commits."
284
- }
285
- }
286
- }
287
-
288
- if (!autoPush && config.git_auto?.forbid_push !== false) {
289
- const pushPattern = /^git\s+push\b/i
290
- if (pushPattern.test(command)) {
291
- return {
292
- allowed: false,
293
- reason: "git push is forbidden for AI. Users must manually review and push changes. Or enable git_auto.full_auto and git_auto.auto_push for automatic pushes."
294
- }
295
- }
296
- }
297
-
298
- // 执行完整策略评估
299
- const result = evaluateCommand(command)
300
-
301
- if (result.isForbidden()) {
302
- // 全自动化模式下,仅 git_safety 类别的危险操作可被允许
303
- if (fullAuto && allowDangerous && result.category === "git_safety") {
304
- return {
305
- allowed: true,
306
- warning: `Dangerous git operation allowed in full-auto mode: ${result.reason}`
307
- }
308
- }
309
-
310
- // 其他 forbidden 类别(fs_safety, network_safety 等)始终禁止
311
- return {
312
- allowed: false,
313
- reason: result.reason
314
- }
315
- }
316
-
317
- return { allowed: true }
318
- }
319
-
320
- /**
321
- * 获取当前执行策略模式
322
- *
323
- * @param {Object} config - 配置
324
- * @returns {{mode: string, restrictions: string[]}}
325
- */
326
- export function getPolicyMode(config = {}) {
327
- if (config.git_auto?.full_auto === true) {
328
- const restrictions = []
329
- if (config.git_auto?.auto_commit !== true) restrictions.push("no_auto_commit")
330
- if (config.git_auto?.auto_push !== true) restrictions.push("no_auto_push")
331
- if (config.git_auto?.allow_dangerous_ops !== true) restrictions.push("no_dangerous_ops")
332
-
333
- return {
334
- mode: "full_auto",
335
- restrictions: restrictions.length > 0 ? restrictions : ["none"]
336
- }
337
- }
338
-
339
- return {
340
- mode: "safe",
341
- restrictions: ["no_commit", "no_push", "no_dangerous_ops"]
342
- }
343
- }
344
-
345
- /**
346
- * 检查是否为全自动化模式
347
- */
348
- export function isFullAutoMode(config = {}) {
349
- return config.git_auto?.full_auto === true
350
- }
351
-
352
- /**
353
- * 获取所有禁止的规则(用于文档)
354
- */
355
- export function getForbiddenRules() {
356
- return DEFAULT_RULES
357
- .filter(r => r.decision === Decision.FORBID)
358
- .map(r => ({
359
- name: r.name,
360
- pattern: Array.isArray(r.pattern) ? r.pattern.join(" ") : r.pattern.toString(),
361
- reason: r.reason
362
- }))
363
- }
364
-
365
- /**
366
- * 检查是否为 Git 相关操作
367
- */
368
- export function isGitCommand(command) {
369
- return /^git\s+/i.test(String(command || ""))
370
- }
1
+ /**
2
+ * 执行策略 (Execution Policy)
3
+ *
4
+ * 定义 AI Agent 执行命令的安全规则。
5
+ * 参考 Codex 的设计理念:
6
+ * - AI 可以修改文件(通过 patch)
7
+ * - AI 可以查看 Git 状态
8
+ * - AI 不能执行某些危险操作(如 git commit, git push)
9
+ *
10
+ * 安全原则:
11
+ * 1. 最小权限原则 - AI 只拥有完成任务必需的最小权限
12
+ * 2. 用户保留控制权 - 关键操作(commit/push)必须由用户手动执行
13
+ * 3. 透明可审计 - 所有策略决策都有明确的原因
14
+ */
15
+
16
+ /**
17
+ * 决策类型
18
+ */
19
+ export const Decision = {
20
+ ALLOW: "allow", // 允许执行
21
+ FORBID: "forbid", // 禁止执行
22
+ WARN: "warn", // 警告但允许(记录日志)
23
+ CONFIRM: "confirm" // 需要用户确认
24
+ }
25
+
26
+ /**
27
+ * 规则匹配器
28
+ */
29
+ function createMatcher(pattern) {
30
+ if (typeof pattern === "string") {
31
+ return (cmd) => cmd.includes(pattern)
32
+ }
33
+ if (pattern instanceof RegExp) {
34
+ return (cmd) => pattern.test(cmd)
35
+ }
36
+ if (Array.isArray(pattern)) {
37
+ // RegExp 数组:任一匹配即命中
38
+ if (pattern[0] instanceof RegExp) {
39
+ return (cmd) => pattern.some(re => re.test(cmd))
40
+ }
41
+ // 字符串数组:按顺序包含所有元素(用于匹配 git commit)
42
+ return (cmd) => {
43
+ const parts = cmd.toLowerCase().split(/\s+/)
44
+ let patternIdx = 0
45
+ for (const part of parts) {
46
+ if (patternIdx < pattern.length && part === pattern[patternIdx].toLowerCase()) {
47
+ patternIdx++
48
+ }
49
+ }
50
+ return patternIdx === pattern.length
51
+ }
52
+ }
53
+ return () => false
54
+ }
55
+
56
+ /**
57
+ * 执行策略规则
58
+ */
59
+ const DEFAULT_RULES = [
60
+ // =========================================================================
61
+ // 高危操作 - 禁止
62
+ // =========================================================================
63
+
64
+ {
65
+ name: "forbid_git_commit",
66
+ pattern: ["git", "commit"],
67
+ decision: Decision.FORBID,
68
+ reason: "AI cannot directly create git commits. Use git_snapshot to create temporary snapshots instead. Users must manually run 'git commit' to finalize changes.",
69
+ category: "git_safety"
70
+ },
71
+ {
72
+ name: "forbid_git_push",
73
+ pattern: ["git", "push"],
74
+ decision: Decision.FORBID,
75
+ reason: "AI cannot push to remote repositories. Users must manually review and push changes.",
76
+ category: "git_safety"
77
+ },
78
+ {
79
+ name: "forbid_git_force_push",
80
+ pattern: [/git\s+push\s+.*--force/, /git\s+push\s+.*-f\b/],
81
+ decision: Decision.FORBID,
82
+ reason: "Force push is extremely dangerous and can overwrite remote history. Never allowed for AI.",
83
+ category: "git_safety"
84
+ },
85
+ {
86
+ name: "forbid_git_reset_hard",
87
+ pattern: ["git", "reset", "--hard"],
88
+ decision: Decision.FORBID,
89
+ reason: "Hard reset destroys uncommitted changes. Use git_restore with a snapshot ID instead.",
90
+ category: "git_safety"
91
+ },
92
+ {
93
+ name: "forbid_git_clean_fd",
94
+ pattern: [/git\s+clean\s+.*-f/, /git\s+clean\s+.*-d/],
95
+ decision: Decision.FORBID,
96
+ reason: "git clean -f/-d deletes untracked files and directories. Dangerous operation.",
97
+ category: "git_safety"
98
+ },
99
+
100
+ // =========================================================================
101
+ // 文件系统危险操作 - 禁止
102
+ // =========================================================================
103
+
104
+ {
105
+ name: "forbid_rm_rf",
106
+ pattern: [/rm\s+-rf?\s+\//, /rm\s+.*\*\s+.*\/\.\.?/],
107
+ decision: Decision.FORBID,
108
+ reason: "Dangerous file deletion pattern detected. Cannot delete system directories or use wildcards with relative path traversals.",
109
+ category: "fs_safety"
110
+ },
111
+ {
112
+ name: "forbid_dd_disk",
113
+ pattern: [/dd\s+.*of=\/dev\//],
114
+ decision: Decision.FORBID,
115
+ reason: "Direct disk write operations are forbidden.",
116
+ category: "fs_safety"
117
+ },
118
+ {
119
+ name: "forbid_mkfs",
120
+ pattern: ["mkfs"],
121
+ decision: Decision.FORBID,
122
+ reason: "Filesystem formatting operations are forbidden.",
123
+ category: "fs_safety"
124
+ },
125
+
126
+ // =========================================================================
127
+ // 网络危险操作 - 禁止
128
+ // =========================================================================
129
+
130
+ {
131
+ name: "forbid_curl_pipe_sh",
132
+ pattern: [/curl\s+.*\|\s*(ba)?sh/, /wget\s+.*\|\s*(ba)?sh/],
133
+ decision: Decision.FORBID,
134
+ reason: "Piping curl/wget output directly to shell is dangerous and can execute arbitrary code.",
135
+ category: "network_safety"
136
+ },
137
+
138
+ // =========================================================================
139
+ // 权限提升操作 - 需要确认
140
+ // =========================================================================
141
+
142
+ {
143
+ name: "confirm_sudo",
144
+ pattern: ["sudo"],
145
+ decision: Decision.CONFIRM,
146
+ reason: "Command requires elevated privileges. Please confirm this is necessary.",
147
+ category: "privilege"
148
+ },
149
+ {
150
+ name: "confirm_chmod_777",
151
+ pattern: [/chmod\s+.*777/],
152
+ decision: Decision.WARN,
153
+ reason: "777 permissions grant full access to everyone. Consider using more restrictive permissions.",
154
+ category: "privilege"
155
+ },
156
+
157
+ // =========================================================================
158
+ // Git 信息查看 - 允许
159
+ // =========================================================================
160
+
161
+ {
162
+ name: "allow_git_status",
163
+ pattern: ["git", "status"],
164
+ decision: Decision.ALLOW,
165
+ reason: "Reading git status is safe and necessary.",
166
+ category: "git_read"
167
+ },
168
+ {
169
+ name: "allow_git_log",
170
+ pattern: ["git", "log"],
171
+ decision: Decision.ALLOW,
172
+ reason: "Reading git history is safe.",
173
+ category: "git_read"
174
+ },
175
+ {
176
+ name: "allow_git_diff",
177
+ pattern: ["git", "diff"],
178
+ decision: Decision.ALLOW,
179
+ reason: "Reading git diff is safe.",
180
+ category: "git_read"
181
+ },
182
+ {
183
+ name: "allow_git_show",
184
+ pattern: ["git", "show"],
185
+ decision: Decision.ALLOW,
186
+ reason: "Reading commit details is safe.",
187
+ category: "git_read"
188
+ }
189
+ ]
190
+
191
+ /**
192
+ * 执行策略评估结果
193
+ */
194
+ export class PolicyResult {
195
+ constructor(decision, rule, reason, category) {
196
+ this.decision = decision
197
+ this.rule = rule
198
+ this.reason = reason
199
+ this.category = category
200
+ }
201
+
202
+ isAllowed() {
203
+ return this.decision === Decision.ALLOW
204
+ }
205
+
206
+ isForbidden() {
207
+ return this.decision === Decision.FORBID
208
+ }
209
+
210
+ needsConfirmation() {
211
+ return this.decision === Decision.CONFIRM
212
+ }
213
+ }
214
+
215
+ /**
216
+ * 评估命令是否符合执行策略
217
+ *
218
+ * @param {string} command - 要评估的命令
219
+ * @param {Object} options - 选项
220
+ * @param {boolean} [options.strict=false] - 严格模式(默认禁止任何未明确允许的操作)
221
+ * @param {Array} [options.customRules=[]] - 自定义规则
222
+ * @returns {PolicyResult}
223
+ */
224
+ export function evaluateCommand(command, options = {}) {
225
+ const { strict = false, customRules = [] } = options
226
+ const rules = [...customRules, ...DEFAULT_RULES]
227
+
228
+ const normalizedCmd = String(command || "").trim()
229
+
230
+ for (const rule of rules) {
231
+ const matcher = createMatcher(rule.pattern)
232
+ if (matcher(normalizedCmd)) {
233
+ return new PolicyResult(
234
+ rule.decision,
235
+ rule.name,
236
+ rule.reason,
237
+ rule.category
238
+ )
239
+ }
240
+ }
241
+
242
+ // 严格模式:未匹配任何规则则禁止
243
+ if (strict) {
244
+ return new PolicyResult(
245
+ Decision.FORBID,
246
+ "strict_mode",
247
+ "Command not in allowlist (strict mode)",
248
+ "strict"
249
+ )
250
+ }
251
+
252
+ // 默认允许
253
+ return new PolicyResult(
254
+ Decision.ALLOW,
255
+ "default",
256
+ "No policy rules matched, allowing by default",
257
+ "default"
258
+ )
259
+ }
260
+
261
+ /**
262
+ * 检查 bash 工具调用是否被允许
263
+ *
264
+ * @param {string} command - 命令
265
+ * @param {Object} config - 配置
266
+ * @returns {{allowed: boolean, reason?: string, warning?: string}}
267
+ */
268
+ export function checkBashAllowed(command, config = {}) {
269
+ // 全自动化模式检查
270
+ const fullAuto = config.git_auto?.full_auto === true
271
+ const allowDangerous = config.git_auto?.allow_dangerous_ops === true
272
+
273
+ // 检查是否配置了全局禁止 git commit/push
274
+ // 全自动化模式下,如果 auto_commit/auto_push 启用,则允许
275
+ const autoCommit = fullAuto && config.git_auto?.auto_commit === true
276
+ const autoPush = fullAuto && config.git_auto?.auto_push === true
277
+
278
+ if (!autoCommit && config.git_auto?.forbid_commit !== false) {
279
+ const commitPattern = /^git\s+commit\b/i
280
+ if (commitPattern.test(command)) {
281
+ return {
282
+ allowed: false,
283
+ reason: "git commit is forbidden for AI. Use git_snapshot to create temporary snapshots, then manually commit when satisfied. Or enable git_auto.full_auto and git_auto.auto_commit for automatic commits."
284
+ }
285
+ }
286
+ }
287
+
288
+ if (!autoPush && config.git_auto?.forbid_push !== false) {
289
+ const pushPattern = /^git\s+push\b/i
290
+ if (pushPattern.test(command)) {
291
+ return {
292
+ allowed: false,
293
+ reason: "git push is forbidden for AI. Users must manually review and push changes. Or enable git_auto.full_auto and git_auto.auto_push for automatic pushes."
294
+ }
295
+ }
296
+ }
297
+
298
+ // 执行完整策略评估
299
+ const result = evaluateCommand(command)
300
+
301
+ if (result.isForbidden()) {
302
+ // 全自动化模式下,仅 git_safety 类别的危险操作可被允许
303
+ if (fullAuto && allowDangerous && result.category === "git_safety") {
304
+ return {
305
+ allowed: true,
306
+ warning: `Dangerous git operation allowed in full-auto mode: ${result.reason}`
307
+ }
308
+ }
309
+
310
+ // 其他 forbidden 类别(fs_safety, network_safety 等)始终禁止
311
+ return {
312
+ allowed: false,
313
+ reason: result.reason
314
+ }
315
+ }
316
+
317
+ return { allowed: true }
318
+ }
319
+
320
+ /**
321
+ * 获取当前执行策略模式
322
+ *
323
+ * @param {Object} config - 配置
324
+ * @returns {{mode: string, restrictions: string[]}}
325
+ */
326
+ export function getPolicyMode(config = {}) {
327
+ if (config.git_auto?.full_auto === true) {
328
+ const restrictions = []
329
+ if (config.git_auto?.auto_commit !== true) restrictions.push("no_auto_commit")
330
+ if (config.git_auto?.auto_push !== true) restrictions.push("no_auto_push")
331
+ if (config.git_auto?.allow_dangerous_ops !== true) restrictions.push("no_dangerous_ops")
332
+
333
+ return {
334
+ mode: "full_auto",
335
+ restrictions: restrictions.length > 0 ? restrictions : ["none"]
336
+ }
337
+ }
338
+
339
+ return {
340
+ mode: "safe",
341
+ restrictions: ["no_commit", "no_push", "no_dangerous_ops"]
342
+ }
343
+ }
344
+
345
+ /**
346
+ * 检查是否为全自动化模式
347
+ */
348
+ export function isFullAutoMode(config = {}) {
349
+ return config.git_auto?.full_auto === true
350
+ }
351
+
352
+ /**
353
+ * 获取所有禁止的规则(用于文档)
354
+ */
355
+ export function getForbiddenRules() {
356
+ return DEFAULT_RULES
357
+ .filter(r => r.decision === Decision.FORBID)
358
+ .map(r => ({
359
+ name: r.name,
360
+ pattern: Array.isArray(r.pattern) ? r.pattern.join(" ") : r.pattern.toString(),
361
+ reason: r.reason
362
+ }))
363
+ }
364
+
365
+ /**
366
+ * 检查是否为 Git 相关操作
367
+ */
368
+ export function isGitCommand(command) {
369
+ return /^git\s+/i.test(String(command || ""))
370
+ }