@kkelly-offical/kkcode 0.1.7 → 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 +228 -220
  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 +89 -89
  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 -2981
  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 +298 -298
  96. package/src/session/engine.mjs +417 -232
  97. package/src/session/longagent-4stage.mjs +467 -460
  98. package/src/session/longagent-hybrid.mjs +1344 -1097
  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 -900
  105. package/src/session/loop.mjs +1005 -930
  106. package/src/session/prompt/agent.txt +25 -25
  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 +31 -31
  110. package/src/session/prompt/qwen.txt +46 -46
  111. package/src/session/recovery.mjs +21 -0
  112. package/src/session/rollback.mjs +196 -195
  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 -519
  116. package/src/session/system-prompt.mjs +308 -273
  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 +99 -93
  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,6 +1,122 @@
1
1
  import { Command } from "commander"
2
+ import { access, mkdir, readFile, writeFile } from "node:fs/promises"
3
+ import { dirname, join, resolve } from "node:path"
2
4
  import { buildContext, printContextWarnings } from "../context.mjs"
3
5
  import { McpRegistry } from "../mcp/registry.mjs"
6
+ import { ensureDefaultSkillPack } from "../skill/registry.mjs"
7
+ import { userRootDir } from "../storage/paths.mjs"
8
+
9
+ const DEFAULT_MCP_INIT_CONFIG = {
10
+ servers: {
11
+ context7: {
12
+ enabled: false,
13
+ command: "npx",
14
+ args: ["--yes", "@upstash/context7-mcp"],
15
+ startup_timeout_ms: 60000
16
+ }
17
+ }
18
+ }
19
+
20
+ async function withInitializedMcp(run) {
21
+ const ctx = await buildContext()
22
+ printContextWarnings(ctx)
23
+ try {
24
+ await McpRegistry.initialize(ctx.configState.config)
25
+ return await run(ctx)
26
+ } finally {
27
+ McpRegistry.shutdown()
28
+ }
29
+ }
30
+
31
+ async function exists(target) {
32
+ try {
33
+ await access(target)
34
+ return true
35
+ } catch {
36
+ return false
37
+ }
38
+ }
39
+
40
+ function projectMcpPath(cwd = process.cwd()) {
41
+ return join(cwd, ".kkcode", "mcp.json")
42
+ }
43
+
44
+ function globalMcpPath() {
45
+ return join(userRootDir(), "mcp.json")
46
+ }
47
+
48
+ function renderPathLabel(filePath) {
49
+ const home = process.env.HOME || process.env.USERPROFILE
50
+ if (!home) return filePath
51
+ const homeNorm = resolve(home).replace(/\\/g, "/")
52
+ const fileNorm = resolve(filePath).replace(/\\/g, "/")
53
+ if (fileNorm === homeNorm) return "~"
54
+ if (fileNorm.startsWith(`${homeNorm}/`)) {
55
+ return `~${fileNorm.slice(homeNorm.length)}`
56
+ }
57
+ return filePath
58
+ }
59
+
60
+ function globalScopeLabel() {
61
+ const home = process.env.HOME || process.env.USERPROFILE
62
+ const userRoot = globalMcpPath()
63
+ if (!home) return userRoot
64
+ const homeNorm = resolve(home).replace(/\\/g, "/")
65
+ const rootNorm = resolve(userRoot).replace(/\\/g, "/")
66
+ if (rootNorm === homeNorm) return "~"
67
+ if (rootNorm.startsWith(`${homeNorm}/`)) {
68
+ return `~${rootNorm.slice(homeNorm.length)}`
69
+ }
70
+ return userRoot
71
+ }
72
+
73
+ function legacyMcpPaths(cwd = process.cwd()) {
74
+ return [
75
+ { kind: "local", path: join(cwd, ".mcp.json"), label: ".mcp.json" },
76
+ { kind: "local", path: join(cwd, ".mcp", "config.json"), label: ".mcp/config.json" },
77
+ { kind: "project", path: join(cwd, ".kkcode", "mcp.json"), label: ".kkcode/mcp.json" },
78
+ { kind: "global", path: globalMcpPath(), label: renderPathLabel(globalMcpPath()) }
79
+ ]
80
+ }
81
+
82
+ function stringifyMcpConfig(config) {
83
+ return JSON.stringify(config, null, 2) + "\n"
84
+ }
85
+
86
+ function collectServers(fileConfig) {
87
+ if (!fileConfig || typeof fileConfig !== "object") return []
88
+ const servers = fileConfig.servers || fileConfig.mcpServers || {}
89
+ if (!servers || typeof servers !== "object") return []
90
+ return Object.entries(servers)
91
+ .filter((entry) => {
92
+ const name = entry[0]
93
+ const cfg = entry[1]
94
+ return !!name && cfg !== null && cfg !== undefined
95
+ })
96
+ .map((entry) => entry[0])
97
+ }
98
+
99
+ function parseMcpConfig(text) {
100
+ if (!text || !text.trim()) return { raw: {}, error: "empty" }
101
+ const normalizedText = text.replace(/^\uFEFF/, "").trim()
102
+ const first = normalizedText[0]
103
+ if (first !== "{" && first !== "[") {
104
+ return { raw: null, error: "unsupported format (expected JSON)" }
105
+ }
106
+ try {
107
+ return { raw: JSON.parse(normalizedText), error: null }
108
+ } catch (error) {
109
+ const cleaned = normalizedText
110
+ .replace(/\/\*[\s\S]*?\*\//g, "")
111
+ .replace(/(^|[^:])\/\/.*$/gm, "$1")
112
+ .replace(/,\s*([}\]])/g, "$1")
113
+ try {
114
+ return { raw: JSON.parse(cleaned), error: null }
115
+ } catch (fallbackError) {
116
+ return { raw: null, error: error.message || "invalid JSON", fallbackError: fallbackError?.message || "invalid JSON" }
117
+ }
118
+ }
119
+ }
4
120
 
5
121
  export function createMcpCommand() {
6
122
  const cmd = new Command("mcp").description("manage MCP servers and tools")
@@ -9,20 +125,18 @@ export function createMcpCommand() {
9
125
  .command("list")
10
126
  .description("list configured and healthy MCP servers")
11
127
  .action(async () => {
12
- const ctx = await buildContext()
13
- printContextWarnings(ctx)
14
- await McpRegistry.initialize(ctx.configState.config)
15
- console.log(JSON.stringify(McpRegistry.listServers(), null, 2))
128
+ await withInitializedMcp(async () => {
129
+ console.log(JSON.stringify(McpRegistry.listServers(), null, 2))
130
+ })
16
131
  })
17
132
 
18
133
  cmd
19
134
  .command("tools")
20
- .description("list MCP tools")
135
+ .description("list tools for all MCP servers")
21
136
  .action(async () => {
22
- const ctx = await buildContext()
23
- printContextWarnings(ctx)
24
- await McpRegistry.initialize(ctx.configState.config)
25
- console.log(JSON.stringify(McpRegistry.listTools(), null, 2))
137
+ await withInitializedMcp(async () => {
138
+ console.log(JSON.stringify(McpRegistry.listTools(), null, 2))
139
+ })
26
140
  })
27
141
 
28
142
  cmd
@@ -30,11 +144,10 @@ export function createMcpCommand() {
30
144
  .description("list resources for MCP server")
31
145
  .requiredOption("--server <name>", "server name")
32
146
  .action(async (options) => {
33
- const ctx = await buildContext()
34
- printContextWarnings(ctx)
35
- await McpRegistry.initialize(ctx.configState.config)
36
- const list = await McpRegistry.listResources(options.server)
37
- console.log(JSON.stringify(list, null, 2))
147
+ await withInitializedMcp(async () => {
148
+ const list = await McpRegistry.listResources(options.server)
149
+ console.log(JSON.stringify(list, null, 2))
150
+ })
38
151
  })
39
152
 
40
153
  cmd
@@ -42,11 +155,10 @@ export function createMcpCommand() {
42
155
  .description("list templates for MCP server")
43
156
  .requiredOption("--server <name>", "server name")
44
157
  .action(async (options) => {
45
- const ctx = await buildContext()
46
- printContextWarnings(ctx)
47
- await McpRegistry.initialize(ctx.configState.config)
48
- const list = await McpRegistry.listTemplates(options.server)
49
- console.log(JSON.stringify(list, null, 2))
158
+ await withInitializedMcp(async () => {
159
+ const list = await McpRegistry.listTemplates(options.server)
160
+ console.log(JSON.stringify(list, null, 2))
161
+ })
50
162
  })
51
163
 
52
164
  cmd
@@ -54,36 +166,156 @@ export function createMcpCommand() {
54
166
  .description("test MCP health and tool discovery")
55
167
  .option("--json", "print JSON output", false)
56
168
  .action(async (options) => {
57
- const ctx = await buildContext()
58
- printContextWarnings(ctx)
59
- await McpRegistry.initialize(ctx.configState.config)
60
- const snapshot = McpRegistry.healthSnapshot()
61
- const tools = McpRegistry.listTools()
62
- const healthy = snapshot.filter((item) => item.ok).length
63
- const unhealthy = snapshot.length - healthy
169
+ await withInitializedMcp(async () => {
170
+ const snapshot = McpRegistry.healthSnapshot()
171
+ const tools = McpRegistry.listTools()
172
+ const healthy = snapshot.filter((item) => item.ok).length
173
+ const unhealthy = snapshot.length - healthy
174
+
175
+ if (options.json) {
176
+ console.log(JSON.stringify({
177
+ configured: snapshot.length,
178
+ healthy,
179
+ unhealthy,
180
+ tools: tools.length,
181
+ servers: snapshot
182
+ }, null, 2))
183
+ return
184
+ }
185
+
186
+ console.log(`configured: ${snapshot.length}`)
187
+ console.log(`healthy: ${healthy}`)
188
+ console.log(`unhealthy: ${unhealthy}`)
189
+ console.log(`tools: ${tools.length}`)
190
+ for (const item of snapshot) {
191
+ const status = item.ok ? "ok" : "fail"
192
+ const reason = item.reason || "-"
193
+ const error = item.error ? ` | ${item.error}` : ""
194
+ console.log(`- ${item.name} [${item.transport}] ${status} (${reason})${error}`)
195
+ }
196
+ })
197
+ })
198
+
199
+ cmd
200
+ .command("discover")
201
+ .description("discover MCP config files in current workspace")
202
+ .option("--json", "print JSON output", false)
203
+ .action(async (options) => {
204
+ const cwd = process.cwd()
205
+ const candidates = legacyMcpPaths(cwd)
206
+ const results = []
207
+
208
+ for (const item of candidates) {
209
+ const existsNow = await exists(item.path)
210
+ if (!existsNow) {
211
+ results.push({ ...item, exists: false })
212
+ continue
213
+ }
64
214
 
215
+ const text = await readFile(item.path, "utf8")
216
+ const { raw, error, fallbackError } = parseMcpConfig(text)
217
+ const servers = collectServers(raw)
218
+ results.push({
219
+ ...item,
220
+ exists: true,
221
+ parsed: error === null,
222
+ parseError: error,
223
+ parseFallbackError: fallbackError || null,
224
+ servers,
225
+ serverCount: servers.length
226
+ })
227
+ }
228
+
229
+ const foundCount = results.filter((entry) => entry.exists).length
65
230
  if (options.json) {
66
- console.log(JSON.stringify({
67
- configured: snapshot.length,
68
- healthy,
69
- unhealthy,
70
- tools: tools.length,
71
- servers: snapshot
72
- }, null, 2))
231
+ console.log(JSON.stringify({ total: candidates.length, found: foundCount, configs: results }, null, 2))
232
+ return
233
+ }
234
+
235
+ if (!foundCount) {
236
+ console.log("no MCP config file found")
237
+ console.log("run: kkcode mcp init --project")
73
238
  return
74
239
  }
75
240
 
76
- console.log(`configured: ${snapshot.length}`)
77
- console.log(`healthy: ${healthy}`)
78
- console.log(`unhealthy: ${unhealthy}`)
79
- console.log(`tools: ${tools.length}`)
80
- for (const item of snapshot) {
81
- const status = item.ok ? "ok" : "fail"
82
- const reason = item.reason || "-"
83
- const error = item.error ? ` | ${item.error}` : ""
84
- console.log(`- ${item.name} [${item.transport}] ${status} (${reason})${error}`)
241
+ for (const item of results) {
242
+ if (!item.exists) continue
243
+ if (!item.parsed) {
244
+ const pathHint = item.parseFallbackError ? ` fallback=${item.parseFallbackError}` : ""
245
+ console.log(`- ${item.label}: parse error (${item.parseError})${pathHint}`)
246
+ if (item.parseError?.includes("unsupported format")) {
247
+ console.log(" tip: check file is JSON and contains { servers: ... } or { mcpServers: ... }")
248
+ }
249
+ } else {
250
+ console.log(`- ${item.label}: ${item.serverCount} server(s)`)
251
+ for (const serverName of item.servers) {
252
+ console.log(` - ${serverName}`)
253
+ }
254
+ }
255
+ }
256
+ })
257
+
258
+ cmd
259
+ .command("init")
260
+ .description("initialize MCP config for quick one-click import")
261
+ .option("--global", `write to ${globalScopeLabel()}`)
262
+ .option("--project", "write to .kkcode/mcp.json")
263
+ .option("--all", "write both global and project config")
264
+ .option("--force", "overwrite existing files")
265
+ .option("--with-skills", "also initialize built-in skill packs")
266
+ .action(async (options) => {
267
+ const includeProject = options.all || options.project || (!options.global && !options.project)
268
+ const includeGlobal = options.all || options.global || (!options.global && !options.project)
269
+ const targets = []
270
+
271
+ if (includeProject) targets.push(projectMcpPath(process.cwd()))
272
+ if (includeGlobal) targets.push(globalMcpPath())
273
+
274
+ if (!targets.length) {
275
+ console.log("no target selected for MCP init")
276
+ return
277
+ }
278
+
279
+ const rendered = stringifyMcpConfig(DEFAULT_MCP_INIT_CONFIG)
280
+ for (const target of targets) {
281
+ await mkdir(dirname(target), { recursive: true })
282
+ if (await exists(target) && !options.force) {
283
+ console.log(`skip: exists ${target}`)
284
+ continue
285
+ }
286
+ await writeFile(target, rendered, "utf8")
287
+ console.log(`created: ${target}`)
288
+ }
289
+
290
+ if (options.withSkills) {
291
+ const seedResults = await ensureDefaultSkillPack({
292
+ cwd: process.cwd(),
293
+ force: options.force || false,
294
+ includeProject,
295
+ includeGlobal
296
+ })
297
+ if (seedResults.length) {
298
+ console.log("skill init summary:")
299
+ for (const item of seedResults) {
300
+ const created = item.created.join(", ")
301
+ const skipped = item.skipped.join(", ")
302
+ if (created.length) {
303
+ console.log(`- [${item.scope}] created: ${created}`)
304
+ }
305
+ if (skipped.length) {
306
+ console.log(`- [${item.scope}] already exists: ${skipped}`)
307
+ }
308
+ }
309
+ }
310
+ }
311
+
312
+ console.log("tip: set enabled: true for desired servers after editing")
313
+ if (options.withSkills) {
314
+ console.log("tip: run kkcode skill init to re-seed or adjust scopes")
85
315
  }
316
+ console.log("kkcode mcp discover # verify config is readable")
317
+ console.log("kkcode mcp test # verify health")
86
318
  })
87
319
 
88
320
  return cmd
89
- }
321
+ }
@@ -33,4 +33,4 @@ export function createPermissionCommand() {
33
33
  })
34
34
 
35
35
  return cmd
36
- }
36
+ }