@kkelly-offical/kkcode 0.1.7 → 0.2.3-preview.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 (166) hide show
  1. package/LICENSE +674 -674
  2. package/README.md +474 -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/commands/update.mjs +32 -0
  29. package/src/config/defaults.mjs +289 -260
  30. package/src/config/import-config.mjs +1 -1
  31. package/src/config/load-config.mjs +61 -4
  32. package/src/config/schema.mjs +604 -574
  33. package/src/context.mjs +4 -1
  34. package/src/core/constants.mjs +97 -91
  35. package/src/core/types.mjs +1 -1
  36. package/src/github/api.mjs +78 -78
  37. package/src/github/auth.mjs +294 -286
  38. package/src/github/flow.mjs +298 -298
  39. package/src/github/workspace.mjs +225 -212
  40. package/src/index.mjs +87 -82
  41. package/src/knowledge/frontend-aesthetics.txt +38 -38
  42. package/src/mcp/client-http.mjs +139 -141
  43. package/src/mcp/client-sse.mjs +297 -288
  44. package/src/mcp/client-stdio.mjs +534 -533
  45. package/src/mcp/constants.mjs +4 -2
  46. package/src/mcp/registry.mjs +498 -479
  47. package/src/mcp/stdio-framing.mjs +135 -133
  48. package/src/mcp/tool-result.mjs +24 -24
  49. package/src/observability/edit-diagnostics.mjs +449 -0
  50. package/src/observability/index.mjs +42 -42
  51. package/src/observability/metrics.mjs +165 -137
  52. package/src/observability/tracer.mjs +137 -137
  53. package/src/onboarding.mjs +209 -0
  54. package/src/orchestration/background-manager.mjs +567 -372
  55. package/src/orchestration/background-worker.mjs +419 -305
  56. package/src/orchestration/interruption-reason.mjs +21 -0
  57. package/src/orchestration/longagent-manager.mjs +197 -171
  58. package/src/orchestration/stage-scheduler.mjs +733 -728
  59. package/src/orchestration/subagent-router.mjs +7 -1
  60. package/src/orchestration/task-scheduler.mjs +219 -7
  61. package/src/permission/engine.mjs +1 -1
  62. package/src/permission/exec-policy.mjs +370 -370
  63. package/src/permission/file-edit-policy.mjs +108 -0
  64. package/src/permission/prompt.mjs +1 -1
  65. package/src/permission/rules.mjs +116 -7
  66. package/src/plugin/builtin-hooks/post-edit-format.mjs +2 -1
  67. package/src/plugin/builtin-hooks/post-edit-typecheck.mjs +104 -40
  68. package/src/plugin/hook-bus.mjs +19 -5
  69. package/src/plugin/manifest-loader.mjs +222 -0
  70. package/src/provider/anthropic.mjs +396 -390
  71. package/src/provider/ollama.mjs +7 -1
  72. package/src/provider/openai.mjs +382 -340
  73. package/src/provider/retry-policy.mjs +74 -68
  74. package/src/provider/router.mjs +242 -241
  75. package/src/provider/sse.mjs +104 -104
  76. package/src/provider/wizard.mjs +556 -0
  77. package/src/repl/capability-facade.mjs +30 -0
  78. package/src/repl/command-surface.mjs +23 -0
  79. package/src/repl/controller-entry.mjs +40 -0
  80. package/src/repl/core-shell.mjs +208 -0
  81. package/src/repl/dialog-router.mjs +87 -0
  82. package/src/repl/input-engine.mjs +76 -0
  83. package/src/repl/keymap.mjs +7 -0
  84. package/src/repl/operator-surface.mjs +15 -0
  85. package/src/repl/permission-flow.mjs +49 -0
  86. package/src/repl/runtime-facade.mjs +36 -0
  87. package/src/repl/slash-router.mjs +62 -0
  88. package/src/repl/state-store.mjs +29 -0
  89. package/src/repl/turn-controller.mjs +58 -0
  90. package/src/repl/verification.mjs +23 -0
  91. package/src/repl.mjs +3371 -2981
  92. package/src/rules/load-rules.mjs +3 -3
  93. package/src/runtime.mjs +1 -1
  94. package/src/session/agent-transaction.mjs +86 -0
  95. package/src/session/checkpoint.mjs +302 -302
  96. package/src/session/compaction.mjs +298 -298
  97. package/src/session/engine.mjs +417 -232
  98. package/src/session/longagent-4stage.mjs +467 -460
  99. package/src/session/longagent-hybrid.mjs +1344 -1097
  100. package/src/session/longagent-plan.mjs +376 -365
  101. package/src/session/longagent-project-memory.mjs +53 -53
  102. package/src/session/longagent-scaffold.mjs +291 -291
  103. package/src/session/longagent-task-bus.mjs +138 -54
  104. package/src/session/longagent-utils.mjs +828 -472
  105. package/src/session/longagent.mjs +911 -900
  106. package/src/session/loop.mjs +1005 -930
  107. package/src/session/prompt/agent.txt +25 -25
  108. package/src/session/prompt/anthropic.txt +150 -150
  109. package/src/session/prompt/beast.txt +1 -1
  110. package/src/session/prompt/plan.txt +31 -31
  111. package/src/session/prompt/qwen.txt +46 -46
  112. package/src/session/recovery.mjs +21 -0
  113. package/src/session/rollback.mjs +196 -195
  114. package/src/session/routing-observability.mjs +72 -0
  115. package/src/session/runtime-state.mjs +47 -0
  116. package/src/session/store.mjs +523 -519
  117. package/src/session/system-prompt.mjs +308 -273
  118. package/src/session/task-validator.mjs +267 -267
  119. package/src/session/usability-gates.mjs +2 -2
  120. package/src/skill/builtin/commit.mjs +64 -64
  121. package/src/skill/builtin/design.mjs +76 -76
  122. package/src/skill/generator.mjs +18 -2
  123. package/src/skill/registry.mjs +642 -390
  124. package/src/storage/audit-store.mjs +18 -11
  125. package/src/storage/event-log.mjs +7 -1
  126. package/src/storage/ghost-commit-store.mjs +243 -245
  127. package/src/storage/paths.mjs +17 -0
  128. package/src/theme/default-theme.mjs +1 -1
  129. package/src/theme/markdown.mjs +4 -0
  130. package/src/theme/schema.mjs +1 -1
  131. package/src/theme/status-bar.mjs +162 -158
  132. package/src/tool/audit-wrapper.mjs +18 -2
  133. package/src/tool/edit-transaction.mjs +23 -0
  134. package/src/tool/executor.mjs +26 -1
  135. package/src/tool/file-read-state.mjs +65 -0
  136. package/src/tool/git-auto.mjs +526 -526
  137. package/src/tool/git-full-auto.mjs +487 -478
  138. package/src/tool/mutation-guard.mjs +54 -0
  139. package/src/tool/prompt/edit.txt +3 -3
  140. package/src/tool/prompt/multiedit.txt +1 -0
  141. package/src/tool/prompt/notebookedit.txt +2 -1
  142. package/src/tool/prompt/patch.txt +25 -24
  143. package/src/tool/prompt/read.txt +3 -3
  144. package/src/tool/prompt/sysinfo.txt +29 -0
  145. package/src/tool/prompt/task.txt +66 -4
  146. package/src/tool/prompt/write.txt +2 -2
  147. package/src/tool/question-prompt.mjs +99 -93
  148. package/src/tool/registry.mjs +1701 -1343
  149. package/src/tool/task-tool.mjs +14 -6
  150. package/src/ui/activity-renderer.mjs +667 -664
  151. package/src/ui/repl-background-panel.mjs +7 -0
  152. package/src/ui/repl-capability-panel.mjs +9 -0
  153. package/src/ui/repl-dashboard.mjs +54 -4
  154. package/src/ui/repl-help.mjs +110 -0
  155. package/src/ui/repl-operator-panel.mjs +12 -0
  156. package/src/ui/repl-route-feedback.mjs +35 -0
  157. package/src/ui/repl-status-view.mjs +76 -0
  158. package/src/ui/repl-task-panel.mjs +5 -0
  159. package/src/ui/repl-transcript-panel.mjs +56 -0
  160. package/src/ui/repl-turn-summary.mjs +135 -0
  161. package/src/update/checker.mjs +184 -0
  162. package/src/usage/pricing.mjs +122 -121
  163. package/src/usage/usage-meter.mjs +1 -0
  164. package/src/util/git.mjs +562 -519
  165. package/src/util/template.mjs +6 -1
  166. package/src/version.mjs +3 -0
@@ -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
+ }