@kkelly-offical/kkcode 0.1.2

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 (196) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +445 -0
  3. package/package.json +46 -0
  4. package/src/agent/agent.mjs +170 -0
  5. package/src/agent/custom-agent-loader.mjs +158 -0
  6. package/src/agent/generator.mjs +115 -0
  7. package/src/agent/prompt/architect.txt +36 -0
  8. package/src/agent/prompt/build-fixer.txt +71 -0
  9. package/src/agent/prompt/build.txt +101 -0
  10. package/src/agent/prompt/compaction.txt +12 -0
  11. package/src/agent/prompt/explore.txt +29 -0
  12. package/src/agent/prompt/guide.txt +40 -0
  13. package/src/agent/prompt/longagent.txt +178 -0
  14. package/src/agent/prompt/plan.txt +50 -0
  15. package/src/agent/prompt/researcher.txt +23 -0
  16. package/src/agent/prompt/reviewer.txt +44 -0
  17. package/src/agent/prompt/security-reviewer.txt +62 -0
  18. package/src/agent/prompt/tdd-guide.txt +84 -0
  19. package/src/agent/prompt/title.txt +8 -0
  20. package/src/command/custom-commands.mjs +57 -0
  21. package/src/commands/agent.mjs +71 -0
  22. package/src/commands/audit.mjs +77 -0
  23. package/src/commands/background.mjs +86 -0
  24. package/src/commands/chat.mjs +114 -0
  25. package/src/commands/command.mjs +41 -0
  26. package/src/commands/config.mjs +44 -0
  27. package/src/commands/doctor.mjs +148 -0
  28. package/src/commands/hook.mjs +29 -0
  29. package/src/commands/init.mjs +141 -0
  30. package/src/commands/longagent.mjs +100 -0
  31. package/src/commands/mcp.mjs +89 -0
  32. package/src/commands/permission.mjs +36 -0
  33. package/src/commands/prompt.mjs +42 -0
  34. package/src/commands/review.mjs +266 -0
  35. package/src/commands/rule.mjs +34 -0
  36. package/src/commands/session.mjs +235 -0
  37. package/src/commands/theme.mjs +98 -0
  38. package/src/commands/usage.mjs +91 -0
  39. package/src/config/defaults.mjs +195 -0
  40. package/src/config/import-config.mjs +76 -0
  41. package/src/config/load-config.mjs +76 -0
  42. package/src/config/schema.mjs +509 -0
  43. package/src/context.mjs +40 -0
  44. package/src/core/constants.mjs +46 -0
  45. package/src/core/errors.mjs +57 -0
  46. package/src/core/events.mjs +29 -0
  47. package/src/core/types.mjs +57 -0
  48. package/src/github/api.mjs +78 -0
  49. package/src/github/auth.mjs +286 -0
  50. package/src/github/flow.mjs +298 -0
  51. package/src/github/workspace.mjs +212 -0
  52. package/src/index.mjs +82 -0
  53. package/src/knowledge/api-design.txt +9 -0
  54. package/src/knowledge/cpp.txt +10 -0
  55. package/src/knowledge/docker.txt +10 -0
  56. package/src/knowledge/dotnet.txt +9 -0
  57. package/src/knowledge/electron.txt +10 -0
  58. package/src/knowledge/flutter.txt +10 -0
  59. package/src/knowledge/go.txt +9 -0
  60. package/src/knowledge/graphql.txt +10 -0
  61. package/src/knowledge/java.txt +9 -0
  62. package/src/knowledge/kotlin.txt +10 -0
  63. package/src/knowledge/loader.mjs +125 -0
  64. package/src/knowledge/next.txt +8 -0
  65. package/src/knowledge/node.txt +8 -0
  66. package/src/knowledge/nuxt.txt +9 -0
  67. package/src/knowledge/php.txt +10 -0
  68. package/src/knowledge/python.txt +10 -0
  69. package/src/knowledge/react-native.txt +10 -0
  70. package/src/knowledge/react.txt +9 -0
  71. package/src/knowledge/ruby.txt +11 -0
  72. package/src/knowledge/rust.txt +9 -0
  73. package/src/knowledge/svelte.txt +9 -0
  74. package/src/knowledge/swift.txt +10 -0
  75. package/src/knowledge/tailwind.txt +10 -0
  76. package/src/knowledge/testing.txt +8 -0
  77. package/src/knowledge/typescript.txt +8 -0
  78. package/src/knowledge/vue.txt +9 -0
  79. package/src/mcp/client-http.mjs +157 -0
  80. package/src/mcp/client-sse.mjs +286 -0
  81. package/src/mcp/client-stdio.mjs +451 -0
  82. package/src/mcp/registry.mjs +394 -0
  83. package/src/mcp/stdio-framing.mjs +127 -0
  84. package/src/orchestration/background-manager.mjs +358 -0
  85. package/src/orchestration/background-worker.mjs +245 -0
  86. package/src/orchestration/longagent-manager.mjs +116 -0
  87. package/src/orchestration/stage-scheduler.mjs +489 -0
  88. package/src/orchestration/subagent-router.mjs +62 -0
  89. package/src/orchestration/task-scheduler.mjs +74 -0
  90. package/src/permission/engine.mjs +92 -0
  91. package/src/permission/exec-policy.mjs +372 -0
  92. package/src/permission/prompt.mjs +39 -0
  93. package/src/permission/rules.mjs +120 -0
  94. package/src/permission/workspace-trust.mjs +44 -0
  95. package/src/plugin/builtin-hooks/console-warn.mjs +41 -0
  96. package/src/plugin/builtin-hooks/extract-patterns.mjs +75 -0
  97. package/src/plugin/builtin-hooks/post-edit-format.mjs +57 -0
  98. package/src/plugin/builtin-hooks/post-edit-typecheck.mjs +61 -0
  99. package/src/plugin/builtin-hooks/strategic-compaction.mjs +38 -0
  100. package/src/plugin/hook-bus.mjs +154 -0
  101. package/src/provider/anthropic.mjs +389 -0
  102. package/src/provider/ollama.mjs +236 -0
  103. package/src/provider/openai-compatible.mjs +1 -0
  104. package/src/provider/openai.mjs +339 -0
  105. package/src/provider/retry-policy.mjs +68 -0
  106. package/src/provider/router.mjs +228 -0
  107. package/src/provider/sse.mjs +91 -0
  108. package/src/repl.mjs +2929 -0
  109. package/src/review/diff-parser.mjs +36 -0
  110. package/src/review/rejection-queue.mjs +62 -0
  111. package/src/review/review-store.mjs +21 -0
  112. package/src/review/risk-score.mjs +61 -0
  113. package/src/rules/load-rules.mjs +64 -0
  114. package/src/runtime.mjs +1 -0
  115. package/src/session/checkpoint.mjs +239 -0
  116. package/src/session/compaction.mjs +276 -0
  117. package/src/session/engine.mjs +225 -0
  118. package/src/session/instinct-manager.mjs +172 -0
  119. package/src/session/instruction-loader.mjs +25 -0
  120. package/src/session/longagent-plan.mjs +329 -0
  121. package/src/session/longagent-scaffold.mjs +100 -0
  122. package/src/session/longagent.mjs +1462 -0
  123. package/src/session/loop.mjs +905 -0
  124. package/src/session/memory-loader.mjs +75 -0
  125. package/src/session/project-context.mjs +367 -0
  126. package/src/session/prompt/anthropic.txt +151 -0
  127. package/src/session/prompt/beast.txt +37 -0
  128. package/src/session/prompt/max-steps.txt +6 -0
  129. package/src/session/prompt/plan.txt +9 -0
  130. package/src/session/prompt/qwen.txt +46 -0
  131. package/src/session/prompt-loader.mjs +18 -0
  132. package/src/session/recovery.mjs +52 -0
  133. package/src/session/store.mjs +503 -0
  134. package/src/session/system-prompt.mjs +260 -0
  135. package/src/session/task-validator.mjs +266 -0
  136. package/src/session/usability-gates.mjs +379 -0
  137. package/src/skill/builtin/backend-patterns.mjs +123 -0
  138. package/src/skill/builtin/commit.mjs +64 -0
  139. package/src/skill/builtin/debug.mjs +45 -0
  140. package/src/skill/builtin/frontend-patterns.mjs +120 -0
  141. package/src/skill/builtin/frontend.mjs +188 -0
  142. package/src/skill/builtin/init.mjs +220 -0
  143. package/src/skill/builtin/review.mjs +49 -0
  144. package/src/skill/builtin/security-checklist.mjs +80 -0
  145. package/src/skill/builtin/tdd.mjs +54 -0
  146. package/src/skill/generator.mjs +113 -0
  147. package/src/skill/registry.mjs +336 -0
  148. package/src/storage/audit-store.mjs +83 -0
  149. package/src/storage/event-log.mjs +82 -0
  150. package/src/storage/ghost-commit-store.mjs +235 -0
  151. package/src/storage/json-store.mjs +53 -0
  152. package/src/storage/paths.mjs +148 -0
  153. package/src/theme/color.mjs +64 -0
  154. package/src/theme/default-theme.mjs +29 -0
  155. package/src/theme/load-theme.mjs +71 -0
  156. package/src/theme/markdown.mjs +135 -0
  157. package/src/theme/schema.mjs +45 -0
  158. package/src/theme/status-bar.mjs +158 -0
  159. package/src/tool/audit-wrapper.mjs +38 -0
  160. package/src/tool/edit-transaction.mjs +126 -0
  161. package/src/tool/executor.mjs +109 -0
  162. package/src/tool/file-lock-manager.mjs +85 -0
  163. package/src/tool/git-auto.mjs +545 -0
  164. package/src/tool/git-full-auto.mjs +478 -0
  165. package/src/tool/image-util.mjs +276 -0
  166. package/src/tool/prompt/background_cancel.txt +1 -0
  167. package/src/tool/prompt/background_output.txt +1 -0
  168. package/src/tool/prompt/bash.txt +71 -0
  169. package/src/tool/prompt/codesearch.txt +18 -0
  170. package/src/tool/prompt/edit.txt +27 -0
  171. package/src/tool/prompt/enter_plan.txt +74 -0
  172. package/src/tool/prompt/exit_plan.txt +62 -0
  173. package/src/tool/prompt/glob.txt +33 -0
  174. package/src/tool/prompt/grep.txt +43 -0
  175. package/src/tool/prompt/list.txt +8 -0
  176. package/src/tool/prompt/multiedit.txt +20 -0
  177. package/src/tool/prompt/notebookedit.txt +21 -0
  178. package/src/tool/prompt/patch.txt +24 -0
  179. package/src/tool/prompt/question.txt +44 -0
  180. package/src/tool/prompt/read.txt +40 -0
  181. package/src/tool/prompt/task.txt +83 -0
  182. package/src/tool/prompt/todowrite.txt +117 -0
  183. package/src/tool/prompt/webfetch.txt +38 -0
  184. package/src/tool/prompt/websearch.txt +43 -0
  185. package/src/tool/prompt/write.txt +38 -0
  186. package/src/tool/prompt-loader.mjs +18 -0
  187. package/src/tool/question-prompt.mjs +86 -0
  188. package/src/tool/registry.mjs +1309 -0
  189. package/src/tool/task-tool.mjs +28 -0
  190. package/src/ui/activity-renderer.mjs +410 -0
  191. package/src/ui/repl-dashboard.mjs +357 -0
  192. package/src/usage/pricing.mjs +121 -0
  193. package/src/usage/usage-meter.mjs +113 -0
  194. package/src/util/git.mjs +496 -0
  195. package/src/util/template.mjs +10 -0
  196. package/src/util/yaml.mjs +100 -0
@@ -0,0 +1,379 @@
1
+ import path from "node:path"
2
+ import { access, readFile, writeFile, mkdir } from "node:fs/promises"
3
+ import { homedir } from "node:os"
4
+ import { spawn } from "node:child_process"
5
+ import { readReviewState } from "../review/review-store.mjs"
6
+ import { fsckSessionStore, getSession } from "./store.mjs"
7
+ import { EventBus } from "../core/events.mjs"
8
+ import { EVENT_TYPES } from "../core/constants.mjs"
9
+
10
+ const DEFAULT_GATE_TIMEOUT_MS = 15 * 60 * 1000
11
+ const GATE_PREFS_FILE = path.join(homedir(), ".kkcode", "gate-preferences.json")
12
+
13
+ // --- Gate result cache (5-min TTL, only caches passing results) ---
14
+ const gateCache = new Map()
15
+ const GATE_CACHE_TTL_MS = 5 * 60 * 1000
16
+
17
+ function getCachedGate(key) {
18
+ const entry = gateCache.get(key)
19
+ if (!entry) return null
20
+ if (Date.now() - entry.ts > GATE_CACHE_TTL_MS) { gateCache.delete(key); return null }
21
+ return entry.result
22
+ }
23
+
24
+ function setCachedGate(key, result) {
25
+ if (result.status === "pass" || result.status === "not_applicable") {
26
+ gateCache.set(key, { result, ts: Date.now() })
27
+ }
28
+ }
29
+
30
+ export function clearGateCache() { gateCache.clear() }
31
+
32
+ // --- Gate preference persistence ---
33
+ let cachedPrefs = null
34
+
35
+ async function loadGatePreferences() {
36
+ if (cachedPrefs) return cachedPrefs
37
+ try {
38
+ const raw = await readFile(GATE_PREFS_FILE, "utf8")
39
+ cachedPrefs = JSON.parse(raw)
40
+ return cachedPrefs
41
+ } catch {
42
+ return null
43
+ }
44
+ }
45
+
46
+ export async function saveGatePreferences(prefs) {
47
+ cachedPrefs = prefs
48
+ await mkdir(path.dirname(GATE_PREFS_FILE), { recursive: true })
49
+ await writeFile(GATE_PREFS_FILE, JSON.stringify(prefs, null, 2), "utf8")
50
+ }
51
+
52
+ export async function hasGatePreferences() {
53
+ const prefs = await loadGatePreferences()
54
+ return prefs !== null
55
+ }
56
+
57
+ export async function getGatePreferences() {
58
+ return loadGatePreferences()
59
+ }
60
+
61
+ export function buildGatePromptText() {
62
+ return [
63
+ "[SYSTEM] LongAgent 质量门控配置",
64
+ "",
65
+ "LongAgent 完成后会运行以下质量检查门控,通过后才标记为完成:",
66
+ " 1. build — 运行 npm run build 检查构建是否通过",
67
+ " 2. test — 运行测试套件确保无回归",
68
+ " 3. review — 检查代码审查状态",
69
+ " 4. health — 检查会话存储健康状态",
70
+ " 5. budget — 检查 token 预算是否超限",
71
+ "",
72
+ "请选择要启用的门控(用逗号分隔,或输入 all/none):",
73
+ "例如: build,test 或 all 或 none",
74
+ "",
75
+ "提示:门控可以在配置文件中随时修改 (agent.longagent.usability_gates)"
76
+ ].join("\n")
77
+ }
78
+
79
+ export function parseGateSelection(answer) {
80
+ const text = String(answer || "").toLowerCase().trim()
81
+ const gates = ["build", "test", "review", "health", "budget"]
82
+ if (text === "all" || text === "全部" || text === "所有") {
83
+ return Object.fromEntries(gates.map(g => [g, true]))
84
+ }
85
+ if (text === "none" || text === "无" || text === "不需要" || text === "跳过") {
86
+ return Object.fromEntries(gates.map(g => [g, false]))
87
+ }
88
+ const selected = new Set(
89
+ text.split(/[,,\s]+/).map(s => s.trim()).filter(Boolean)
90
+ )
91
+ return Object.fromEntries(gates.map(g => [g, selected.has(g)]))
92
+ }
93
+
94
+ function isEnabled(config, gateName) {
95
+ return config?.agent?.longagent?.usability_gates?.[gateName]?.enabled !== false
96
+ }
97
+
98
+ async function fileExists(file) {
99
+ try {
100
+ await access(file)
101
+ return true
102
+ } catch {
103
+ return false
104
+ }
105
+ }
106
+
107
+ async function readPackageScripts(cwd) {
108
+ const pkgPath = path.join(cwd, "package.json")
109
+ const raw = await readFile(pkgPath, "utf8").catch(() => null)
110
+ if (!raw) return null
111
+ try {
112
+ const parsed = JSON.parse(raw)
113
+ return parsed?.scripts && typeof parsed.scripts === "object" ? parsed.scripts : {}
114
+ } catch {
115
+ return null
116
+ }
117
+ }
118
+
119
+ function npmBin() {
120
+ return process.platform === "win32" ? "npm.cmd" : "npm"
121
+ }
122
+
123
+ function outputSnippet(result) {
124
+ const lines = `${result.stdout || ""}\n${result.stderr || ""}`
125
+ .split(/\r?\n/)
126
+ .map((line) => line.trim())
127
+ .filter(Boolean)
128
+ return lines.slice(-12).join(" | ")
129
+ }
130
+
131
+ async function runCommand({ command, args, cwd, timeoutMs = DEFAULT_GATE_TIMEOUT_MS }) {
132
+ return new Promise((resolve) => {
133
+ let done = false
134
+ let stdout = ""
135
+ let stderr = ""
136
+ let timedOut = false
137
+
138
+ const child = spawn(command, args, {
139
+ cwd,
140
+ windowsHide: true,
141
+ stdio: ["ignore", "pipe", "pipe"]
142
+ })
143
+
144
+ const timer = setTimeout(() => {
145
+ timedOut = true
146
+ child.kill()
147
+ }, timeoutMs)
148
+
149
+ child.stdout.on("data", (buf) => {
150
+ stdout += String(buf)
151
+ })
152
+ child.stderr.on("data", (buf) => {
153
+ stderr += String(buf)
154
+ })
155
+
156
+ child.on("error", (error) => {
157
+ if (done) return
158
+ done = true
159
+ clearTimeout(timer)
160
+ resolve({
161
+ ok: false,
162
+ code: null,
163
+ stdout,
164
+ stderr: `${stderr}\n${error.message}`.trim(),
165
+ timedOut: false
166
+ })
167
+ })
168
+
169
+ child.on("close", (code) => {
170
+ if (done) return
171
+ done = true
172
+ clearTimeout(timer)
173
+ resolve({
174
+ ok: !timedOut && code === 0,
175
+ code,
176
+ stdout,
177
+ stderr,
178
+ timedOut
179
+ })
180
+ })
181
+ })
182
+ }
183
+
184
+ async function checkBuildGate({ cwd, config }) {
185
+ const cached = getCachedGate("build"); if (cached) return cached
186
+ if (!isEnabled(config, "build")) {
187
+ return { enabled: false, status: "disabled", reason: "build gate disabled" }
188
+ }
189
+ const scripts = await readPackageScripts(cwd)
190
+ if (!scripts) {
191
+ return { enabled: true, status: "not_applicable", reason: "package.json not found" }
192
+ }
193
+ if (!scripts.build) {
194
+ return { enabled: true, status: "not_applicable", reason: "build script not found" }
195
+ }
196
+ const result = await runCommand({
197
+ command: npmBin(),
198
+ args: ["run", "build", "--silent"],
199
+ cwd
200
+ })
201
+ if (result.ok) {
202
+ const r = { enabled: true, status: "pass", reason: "build succeeded" }
203
+ setCachedGate("build", r); return r
204
+ }
205
+ return {
206
+ enabled: true,
207
+ status: "fail",
208
+ reason: result.timedOut ? "build timed out" : `build failed with code ${result.code}`,
209
+ output: outputSnippet(result)
210
+ }
211
+ }
212
+
213
+ async function checkTestGate({ cwd, config }) {
214
+ const cached = getCachedGate("test"); if (cached) return cached
215
+ if (!isEnabled(config, "test")) {
216
+ return { enabled: false, status: "disabled", reason: "test gate disabled" }
217
+ }
218
+ const scripts = await readPackageScripts(cwd)
219
+ const hasTestDir = await fileExists(path.join(cwd, "test"))
220
+ const hasNodeTestDir = await fileExists(path.join(cwd, "tests"))
221
+
222
+ if (!scripts && !hasTestDir && !hasNodeTestDir) {
223
+ return { enabled: true, status: "not_applicable", reason: "no package.json or test directory" }
224
+ }
225
+
226
+ let result
227
+ if (scripts?.test) {
228
+ result = await runCommand({
229
+ command: npmBin(),
230
+ args: ["run", "test", "--silent"],
231
+ cwd
232
+ })
233
+ } else if (hasTestDir || hasNodeTestDir) {
234
+ result = await runCommand({
235
+ command: process.execPath,
236
+ args: ["--test"],
237
+ cwd
238
+ })
239
+ } else {
240
+ return { enabled: true, status: "not_applicable", reason: "test script not found" }
241
+ }
242
+
243
+ if (result.ok) {
244
+ const r = { enabled: true, status: "pass", reason: "tests succeeded" }
245
+ setCachedGate("test", r); return r
246
+ }
247
+ return {
248
+ enabled: true,
249
+ status: "fail",
250
+ reason: result.timedOut ? "tests timed out" : `tests failed with code ${result.code}`,
251
+ output: outputSnippet(result)
252
+ }
253
+ }
254
+
255
+ async function checkReviewGate({ cwd, config, sessionId }) {
256
+ const cached = getCachedGate("review"); if (cached) return cached
257
+ if (!isEnabled(config, "review")) {
258
+ return { enabled: false, status: "disabled", reason: "review gate disabled" }
259
+ }
260
+ const state = await readReviewState(cwd)
261
+ if (!state.files.length) {
262
+ return { enabled: true, status: "not_applicable", reason: "no review file state" }
263
+ }
264
+ if (state.sessionId && sessionId && state.sessionId !== sessionId) {
265
+ return {
266
+ enabled: true,
267
+ status: "not_applicable",
268
+ reason: `review state belongs to other session (${state.sessionId})`
269
+ }
270
+ }
271
+ const pending = state.files.filter((file) => file.status !== "approved")
272
+ if (pending.length > 0) {
273
+ return {
274
+ enabled: true,
275
+ status: "fail",
276
+ reason: `${pending.length} review item(s) not approved`,
277
+ output: pending.slice(0, 5).map((item) => item.path).join(", ")
278
+ }
279
+ }
280
+ const r = { enabled: true, status: "pass", reason: "all review items approved" }
281
+ setCachedGate("review", r); return r
282
+ }
283
+
284
+ async function checkHealthGate({ config }) {
285
+ const cached = getCachedGate("health"); if (cached) return cached
286
+ if (!isEnabled(config, "health")) {
287
+ return { enabled: false, status: "disabled", reason: "health gate disabled" }
288
+ }
289
+ const report = await fsckSessionStore()
290
+ if (report.ok) {
291
+ const r = { enabled: true, status: "pass", reason: "session fsck passed" }
292
+ setCachedGate("health", r); return r
293
+ }
294
+ return {
295
+ enabled: true,
296
+ status: "fail",
297
+ reason: "session fsck failed",
298
+ output: report.suggestions.join(" | ")
299
+ }
300
+ }
301
+
302
+ async function checkBudgetGate({ config, sessionId }) {
303
+ const cached = getCachedGate("budget"); if (cached) return cached
304
+ if (!isEnabled(config, "budget")) {
305
+ return { enabled: false, status: "disabled", reason: "budget gate disabled" }
306
+ }
307
+ const sessionData = await getSession(sessionId)
308
+ const budgetState = sessionData?.session?.budgetState || null
309
+ if (!budgetState) {
310
+ return { enabled: true, status: "pass", reason: "no budget restriction state" }
311
+ }
312
+ const strategy = config?.usage?.budget?.strategy || "warn"
313
+ if (budgetState.exceeded && strategy === "block") {
314
+ return {
315
+ enabled: true,
316
+ status: "fail",
317
+ reason: "budget exceeded with strategy=block",
318
+ output: (budgetState.warnings || []).join(" | ")
319
+ }
320
+ }
321
+ if ((budgetState.warnings || []).length > 0) {
322
+ return {
323
+ enabled: true,
324
+ status: "warn",
325
+ reason: "budget warning",
326
+ output: budgetState.warnings.join(" | ")
327
+ }
328
+ }
329
+ const r = { enabled: true, status: "pass", reason: "budget gate passed" }
330
+ setCachedGate("budget", r); return r
331
+ }
332
+
333
+ function isPassingStatus(status) {
334
+ return status === "pass" || status === "not_applicable"
335
+ }
336
+
337
+ export async function runUsabilityGates({
338
+ sessionId,
339
+ config,
340
+ cwd = process.cwd(),
341
+ iteration = 0
342
+ }) {
343
+ const [build, test, review, health, budget] = await Promise.all([
344
+ checkBuildGate({ cwd, config }),
345
+ checkTestGate({ cwd, config }),
346
+ checkReviewGate({ cwd, config, sessionId }),
347
+ checkHealthGate({ config }),
348
+ checkBudgetGate({ config, sessionId })
349
+ ])
350
+ const checks = { build, test, review, health, budget }
351
+
352
+ for (const [gate, result] of Object.entries(checks)) {
353
+ await EventBus.emit({
354
+ type: EVENT_TYPES.LONGAGENT_GATE_CHECKED,
355
+ sessionId,
356
+ payload: {
357
+ gate,
358
+ status: result.status,
359
+ reason: result.reason,
360
+ iteration
361
+ }
362
+ })
363
+ }
364
+
365
+ const failures = Object.entries(checks)
366
+ .filter(([, result]) => result.enabled !== false && !isPassingStatus(result.status))
367
+ .map(([gate, result]) => ({
368
+ gate,
369
+ status: result.status,
370
+ reason: result.reason,
371
+ output: result.output || ""
372
+ }))
373
+
374
+ return {
375
+ allPass: failures.length === 0,
376
+ gates: checks,
377
+ failures
378
+ }
379
+ }
@@ -0,0 +1,123 @@
1
+ export const name = "backend-patterns"
2
+ export const description = "Backend development patterns reference: API design, repository pattern, middleware, error handling, authentication"
3
+
4
+ export async function run(ctx) {
5
+ const topic = (ctx.args || "").trim().toLowerCase()
6
+
7
+ const sections = {
8
+ api: `## API Design Patterns
9
+
10
+ ### RESTful Conventions
11
+ - GET /resources — list (with pagination: ?page=1&limit=20)
12
+ - GET /resources/:id — get single
13
+ - POST /resources — create (return 201 + Location header)
14
+ - PUT /resources/:id — full replace
15
+ - PATCH /resources/:id — partial update
16
+ - DELETE /resources/:id — remove (return 204)
17
+
18
+ ### Response Format
19
+ \`\`\`json
20
+ {
21
+ "data": { ... },
22
+ "meta": { "page": 1, "total": 42, "limit": 20 },
23
+ "error": null
24
+ }
25
+ \`\`\`
26
+
27
+ ### Error Responses
28
+ \`\`\`json
29
+ {
30
+ "error": {
31
+ "code": "VALIDATION_ERROR",
32
+ "message": "Email is required",
33
+ "details": [{ "field": "email", "rule": "required" }]
34
+ }
35
+ }
36
+ \`\`\`
37
+ Status codes: 400 validation, 401 unauthenticated, 403 unauthorized, 404 not found, 409 conflict, 422 unprocessable, 429 rate limited, 500 server error`,
38
+
39
+ repository: `## Repository Pattern
40
+
41
+ Separate data access from business logic:
42
+ \`\`\`
43
+ Controller → Service → Repository → Database
44
+ \`\`\`
45
+
46
+ - **Repository**: handles queries, CRUD, caching. Returns domain objects.
47
+ - **Service**: business rules, validation, orchestration. Calls repositories.
48
+ - **Controller**: HTTP concerns only. Parses request, calls service, formats response.
49
+
50
+ Benefits: testable (mock repository in service tests), swappable storage, clear boundaries.`,
51
+
52
+ middleware: `## Middleware Patterns
53
+
54
+ Execution order matters. Typical chain:
55
+ 1. **CORS** — set access headers
56
+ 2. **Request ID** — assign unique ID for tracing
57
+ 3. **Logger** — log method, path, duration, status
58
+ 4. **Rate limiter** — throttle by IP or API key
59
+ 5. **Auth** — verify JWT/session, attach user to request
60
+ 6. **Validation** — validate request body/params against schema
61
+ 7. **Handler** — actual business logic
62
+ 8. **Error handler** — catch errors, format response (always LAST)`,
63
+
64
+ auth: `## Authentication Patterns
65
+
66
+ ### JWT (Stateless)
67
+ - Access token (short-lived: 15min) + Refresh token (long-lived: 7d)
68
+ - Store refresh in httpOnly cookie, access in memory (NOT localStorage)
69
+ - Rotate refresh tokens on use (one-time use)
70
+
71
+ ### Session (Stateful)
72
+ - Server-side session store (Redis for multi-server)
73
+ - Session ID in httpOnly, Secure, SameSite=Strict cookie
74
+ - Regenerate session ID after login (prevent fixation)
75
+
76
+ ### OAuth 2.0
77
+ - Always validate \`state\` parameter (CSRF protection)
78
+ - Use PKCE for public clients (SPAs, mobile)
79
+ - Exchange code for tokens server-side (never client-side)`,
80
+
81
+ error: `## Error Handling
82
+
83
+ ### Layered Error Strategy
84
+ - **Repository**: throw typed errors (NotFoundError, ConflictError)
85
+ - **Service**: catch repo errors, add business context, re-throw
86
+ - **Controller/Middleware**: catch all, map to HTTP status, log, respond
87
+
88
+ ### Custom Error Classes
89
+ \`\`\`javascript
90
+ class AppError extends Error {
91
+ constructor(message, code, statusCode = 500) {
92
+ super(message)
93
+ this.code = code
94
+ this.statusCode = statusCode
95
+ }
96
+ }
97
+ class NotFoundError extends AppError {
98
+ constructor(resource, id) {
99
+ super(\`\${resource} \${id} not found\`, "NOT_FOUND", 404)
100
+ }
101
+ }
102
+ \`\`\`
103
+
104
+ ### Never expose internals
105
+ - Log full stack trace server-side
106
+ - Return sanitized message to client
107
+ - Never leak database errors, file paths, or config values`
108
+ }
109
+
110
+ if (topic && sections[topic]) {
111
+ return sections[topic]
112
+ }
113
+
114
+ // Return overview with all sections
115
+ const overview = Object.values(sections).join("\n\n---\n\n")
116
+ return `# Backend Development Patterns
117
+
118
+ Use \`/backend-patterns <topic>\` for a specific section: api, repository, middleware, auth, error
119
+
120
+ ---
121
+
122
+ ${overview}`
123
+ }
@@ -0,0 +1,64 @@
1
+ export const name = "commit"
2
+ export const description = "Stage changes and create a git commit with a descriptive message using AI-powered git automation"
3
+
4
+ export async function run(args, context = {}) {
5
+ const hasGitAuto = context.config?.git_auto?.enabled !== false
6
+
7
+ return `Review the current git status and create a well-structured commit.
8
+
9
+ ${hasGitAuto ? `🚀 Git Auto Mode Enabled
10
+ The AI can now use ghost commits (temporary snapshots) to safely manage changes before you finalize them.
11
+ ` : `⚙️ Standard Mode
12
+ Consider enabling git_auto in your config for enhanced safety features.
13
+ `}
14
+
15
+ Steps:
16
+ 1. Run \`git_info\` to understand the repository context.
17
+ 2. Run \`git_status\` to see all changed, staged, and untracked files.
18
+ 3. Review the changes to understand what needs to be committed.
19
+
20
+ ${hasGitAuto ? `4. **IMPORTANT**: Before making any edits, create a ghost commit snapshot:
21
+ \`git_snapshot\` - Creates a temporary snapshot you can restore later
22
+
23
+ 5. After reviewing, if you need to make changes, the AI will:
24
+ - First create an automatic snapshot (if git_auto.auto_snapshot is enabled)
25
+ - Apply changes using \`edit\`, \`write\`, or \`git_apply_patch\`
26
+
27
+ 6. If you're not satisfied with the changes:
28
+ - Use \`git_list_snapshots\` to see available snapshots
29
+ - Use \`git_restore\` with the snapshot_id to revert
30
+
31
+ 7. When satisfied with the changes, guide the user to manually run:
32
+ \`bash: git add <files> && git commit -m "<message>"\`
33
+ Note: AI is forbidden from running git commit directly for security.`
34
+
35
+ : `4. Stage the relevant files with manual git commands (AI cannot run git commit):
36
+ - AI can suggest: \`bash: git add <files>\`
37
+ - But user must manually run: \`git commit -m "<message>"\`
38
+
39
+ 5. Note: AI is forbidden from executing git commit/push for security reasons.`}
40
+
41
+ Commit Message Format (Conventional Commits):
42
+ - feat: new feature or capability
43
+ - fix: bug fix
44
+ - refactor: code restructuring without behavior change
45
+ - docs: documentation changes
46
+ - style: formatting, whitespace, semicolons
47
+ - test: adding or updating tests
48
+ - chore: build process, dependencies, tooling
49
+
50
+ Format: <type>(<optional scope>): <short description>
51
+ Example: feat(auth): add JWT token refresh logic
52
+
53
+ Important:
54
+ - Keep the commit focused on a single logical change.
55
+ - If there are unrelated changes, create separate commits.
56
+ - The commit message subject should be under 72 characters.
57
+ - Use imperative mood: "add feature" not "added feature".
58
+
59
+ ${hasGitAuto ? `Safety Features:
60
+ - Ghost commits are stored for 7 days then auto-cleaned
61
+ - Maximum 50 snapshots per repository
62
+ - Snapshots don't interfere with your normal git workflow
63
+ - Use \`git_cleanup\` to manually clean up expired snapshots` : ""}`
64
+ }
@@ -0,0 +1,45 @@
1
+ export const name = "debug"
2
+ export const description = "Diagnose and fix a bug or error (usage: /debug <error description or message>)"
3
+
4
+ export async function run(ctx) {
5
+ const issue = (ctx.args || "").trim()
6
+
7
+ if (!issue) {
8
+ return `Please describe the bug or paste the error message.
9
+
10
+ Usage:
11
+ /debug TypeError: Cannot read properties of undefined
12
+ /debug the login page shows a blank screen after submit
13
+ /debug test suite fails on CI but passes locally`
14
+ }
15
+
16
+ return `Debug this issue: ${issue}
17
+
18
+ Follow this systematic approach:
19
+
20
+ 1. **Reproduce**
21
+ - Identify the minimal steps or command to trigger the issue.
22
+ - If an error message was given, search the codebase for the source: \`grep -r "<key phrase>" src/\`
23
+ - If a test fails, run it in isolation to confirm.
24
+
25
+ 2. **Locate root cause**
26
+ - Trace the execution path from the error location backward.
27
+ - Read the relevant source files to understand the logic.
28
+ - Check recent changes: \`git log --oneline -10\` and \`git diff HEAD~3\` for potential regressions.
29
+ - Add diagnostic logging or assertions if the cause isn't obvious.
30
+
31
+ 3. **Fix**
32
+ - Apply the minimal change that addresses the root cause.
33
+ - Do NOT refactor surrounding code or fix unrelated issues.
34
+ - Preserve existing behavior for all other code paths.
35
+
36
+ 4. **Verify**
37
+ - Run the reproduction steps again to confirm the fix.
38
+ - Run related tests if they exist.
39
+ - Check for regressions in adjacent functionality.
40
+
41
+ 5. **Output**
42
+ - State the root cause in one sentence.
43
+ - Show the exact change made (file, line, before/after).
44
+ - List verification steps performed.`
45
+ }