@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,9 +1,10 @@
1
- import path from "node:path"
2
- import { writeFile } from "node:fs/promises"
3
- import { Command } from "commander"
1
+ import path from "node:path"
2
+ import { writeFile } from "node:fs/promises"
3
+ import { Command } from "commander"
4
4
  import { exportSession, getSession, listSessions, forkSession, fsckSessionStore, gcSessionStore, flushNow } from "../session/store.mjs"
5
5
  import { newSessionId, executeTurn } from "../session/engine.mjs"
6
- import { listRecoverableSessions, getResumeContext, isRecoveryEnabled } from "../session/recovery.mjs"
6
+ import { listRecoverableSessions, getResumeContext, isRecoveryEnabled, summarizeResumeContext } from "../session/recovery.mjs"
7
+ import { summarizeSessionRuntimeState } from "../session/runtime-state.mjs"
7
8
  import { buildContext } from "../context.mjs"
8
9
  import { ToolRegistry } from "../tool/registry.mjs"
9
10
 
@@ -15,8 +16,8 @@ function assertRecoveryEnabled(config, commandName) {
15
16
  }
16
17
 
17
18
  export function createSessionCommand() {
18
- const cmd = new Command("session").description("manage persisted kkcode sessions")
19
-
19
+ const cmd = new Command("session").description("manage persisted kkcode sessions")
20
+
20
21
  cmd
21
22
  .command("fsck")
22
23
  .description("check session index/data consistency")
@@ -62,86 +63,131 @@ export function createSessionCommand() {
62
63
 
63
64
  cmd
64
65
  .command("list")
65
- .description("list sessions")
66
- .option("--cwd-only", "filter by cwd", false)
67
- .option("--roots", "only root sessions", false)
68
- .option("--limit <n>", "max sessions", "30")
69
- .action(async (options) => {
70
- const list = await listSessions({
71
- cwd: options.cwdOnly ? process.cwd() : null,
72
- includeChildren: !options.roots,
73
- limit: Number(options.limit || 30)
74
- })
75
- if (!list.length) {
76
- console.log("no sessions found")
77
- return
78
- }
79
- for (const session of list) {
80
- const parent = session.parentSessionId ? ` parent=${session.parentSessionId}` : ""
81
- console.log(
82
- `${session.id} ${session.mode} ${session.providerType} ${session.model} ${new Date(session.updatedAt).toLocaleString()}${parent}`
83
- )
84
- }
85
- })
86
-
87
- cmd
88
- .command("show")
89
- .description("show one session")
90
- .requiredOption("--id <id>", "session id")
91
- .action(async (options) => {
92
- const data = await getSession(options.id)
93
- if (!data) {
94
- console.error(`session not found: ${options.id}`)
95
- process.exitCode = 1
96
- return
97
- }
98
- console.log(JSON.stringify(data, null, 2))
99
- })
100
-
101
- cmd
102
- .command("export")
103
- .description("export session to json")
104
- .requiredOption("--id <id>", "session id")
105
- .option("--out <file>", "output file")
106
- .action(async (options) => {
107
- const data = await exportSession(options.id)
108
- if (!data) {
109
- console.error(`session not found: ${options.id}`)
110
- process.exitCode = 1
111
- return
112
- }
113
- const out = options.out ? path.resolve(options.out) : path.resolve(`session-${options.id}.json`)
114
- await writeFile(out, JSON.stringify(data, null, 2) + "\n", "utf8")
115
- console.log(`exported: ${out}`)
116
- })
117
-
118
- cmd
119
- .command("fork")
120
- .description("fork session into a new child session")
121
- .requiredOption("--id <id>", "source session id")
122
- .option("--new-id <id>", "new session id")
123
- .option("--title <title>", "child title")
124
- .action(async (options) => {
125
- const newId = options.newId || newSessionId()
126
- const out = await forkSession({
127
- sessionId: options.id,
128
- newSessionId: newId,
129
- title: options.title || null
130
- })
131
- if (!out) {
132
- console.error(`session not found: ${options.id}`)
133
- process.exitCode = 1
134
- return
135
- }
136
- console.log(`forked: ${newId} <- ${options.id}`)
137
- })
138
-
139
- cmd
140
- .command("resume")
141
- .description("resume a session from the last user message")
142
- .requiredOption("--id <id>", "session id to resume")
143
- .option("--mode <mode>", "override mode")
144
- .option("--model <model>", "override model")
66
+ .description("list sessions")
67
+ .option("--cwd-only", "filter by cwd", false)
68
+ .option("--roots", "only root sessions", false)
69
+ .option("--limit <n>", "max sessions", "30")
70
+ .action(async (options) => {
71
+ const list = await listSessions({
72
+ cwd: options.cwdOnly ? process.cwd() : null,
73
+ includeChildren: !options.roots,
74
+ limit: Number(options.limit || 30)
75
+ })
76
+ if (!list.length) {
77
+ console.log("no sessions found")
78
+ return
79
+ }
80
+ for (const session of list) {
81
+ const parent = session.parentSessionId ? ` parent=${session.parentSessionId}` : ""
82
+ console.log(
83
+ `${session.id} ${session.mode} ${session.providerType} ${session.model} ${new Date(session.updatedAt).toLocaleString()}${parent}`
84
+ )
85
+ }
86
+ })
87
+
88
+ cmd
89
+ .command("status")
90
+ .description("show a unified runtime summary for the latest or selected session")
91
+ .option("--id <id>", "session id")
92
+ .option("--json", "print as json", false)
93
+ .action(async (options) => {
94
+ const ctx = await buildContext()
95
+ const summary = await summarizeSessionRuntimeState({
96
+ sessionId: options.id || null,
97
+ cwd: process.cwd(),
98
+ recoveryEnabled: isRecoveryEnabled(ctx.configState.config)
99
+ })
100
+
101
+ if (options.json) {
102
+ console.log(JSON.stringify(summary, null, 2))
103
+ return
104
+ }
105
+ if (!summary.session) {
106
+ console.log("no session state available")
107
+ return
108
+ }
109
+
110
+ console.log(`session=${summary.session.id} mode=${summary.session.mode} provider=${summary.session.providerType} model=${summary.session.model}`)
111
+ console.log(`status=${summary.session.status} messages=${summary.messageCount} parts=${summary.partCount}`)
112
+ if (summary.retryMeta) {
113
+ console.log(`retry=inProgress:${Boolean(summary.retryMeta.inProgress)} step=${summary.retryMeta.step ?? "-"}`)
114
+ }
115
+ if (summary.budgetState) {
116
+ console.log(`budget=exceeded:${Boolean(summary.budgetState.exceeded)} warnings=${(summary.budgetState.warnings || []).length}`)
117
+ }
118
+ console.log(`recoverable=${summary.recoverableCount}`)
119
+ console.log(`background total=${summary.background.total} running=${summary.background.running} pending=${summary.background.pending} interrupted=${summary.background.interrupted} error=${summary.background.error}`)
120
+ console.log(`audit total=${summary.audit.total} error1h=${summary.audit.error1h} error24h=${summary.audit.error24h}`)
121
+ })
122
+
123
+ cmd
124
+ .command("show")
125
+ .description("show one session")
126
+ .requiredOption("--id <id>", "session id")
127
+ .option("--summary", "show a concise session/resume summary")
128
+ .action(async (options) => {
129
+ const data = await getSession(options.id)
130
+ if (!data) {
131
+ console.error(`session not found: ${options.id}`)
132
+ process.exitCode = 1
133
+ return
134
+ }
135
+ if (options.summary) {
136
+ const resumeCtx = await getResumeContext(options.id)
137
+ const summary = summarizeResumeContext(resumeCtx)
138
+ console.log(JSON.stringify({
139
+ session: data.session,
140
+ resume: summary
141
+ }, null, 2))
142
+ return
143
+ }
144
+ console.log(JSON.stringify(data, null, 2))
145
+ })
146
+
147
+ cmd
148
+ .command("export")
149
+ .description("export session to json")
150
+ .requiredOption("--id <id>", "session id")
151
+ .option("--out <file>", "output file")
152
+ .action(async (options) => {
153
+ const data = await exportSession(options.id)
154
+ if (!data) {
155
+ console.error(`session not found: ${options.id}`)
156
+ process.exitCode = 1
157
+ return
158
+ }
159
+ const out = options.out ? path.resolve(options.out) : path.resolve(`session-${options.id}.json`)
160
+ await writeFile(out, JSON.stringify(data, null, 2) + "\n", "utf8")
161
+ console.log(`exported: ${out}`)
162
+ })
163
+
164
+ cmd
165
+ .command("fork")
166
+ .description("fork session into a new child session")
167
+ .requiredOption("--id <id>", "source session id")
168
+ .option("--new-id <id>", "new session id")
169
+ .option("--title <title>", "child title")
170
+ .action(async (options) => {
171
+ const newId = options.newId || newSessionId()
172
+ const out = await forkSession({
173
+ sessionId: options.id,
174
+ newSessionId: newId,
175
+ title: options.title || null
176
+ })
177
+ if (!out) {
178
+ console.error(`session not found: ${options.id}`)
179
+ process.exitCode = 1
180
+ return
181
+ }
182
+ console.log(`forked: ${newId} <- ${options.id}`)
183
+ })
184
+
185
+ cmd
186
+ .command("resume")
187
+ .description("resume a session from the last user message")
188
+ .requiredOption("--id <id>", "session id to resume")
189
+ .option("--mode <mode>", "override mode")
190
+ .option("--model <model>", "override model")
145
191
  .action(async (options) => {
146
192
  const ctx = await buildContext()
147
193
  if (!assertRecoveryEnabled(ctx.configState.config, "session resume")) return
@@ -152,31 +198,31 @@ export function createSessionCommand() {
152
198
  process.exitCode = 1
153
199
  return
154
200
  }
155
- if (!resumeCtx.canResume) {
156
- console.error(`no user message found in session ${options.id}`)
157
- process.exitCode = 1
158
- return
159
- }
201
+ if (!resumeCtx.canResume) {
202
+ console.error(`no user message found in session ${options.id}`)
203
+ process.exitCode = 1
204
+ return
205
+ }
160
206
  await ToolRegistry.initialize({ config: ctx.configState.config, cwd: process.cwd() })
161
207
  const mode = options.mode || resumeCtx.session.mode
162
208
  const model = options.model || resumeCtx.session.model
163
- console.log(`resuming session ${options.id} (${resumeCtx.messageCount} messages)`)
164
- console.log(`last prompt: ${resumeCtx.lastPrompt.slice(0, 100)}${resumeCtx.lastPrompt.length > 100 ? "..." : ""}`)
165
- const result = await executeTurn({
166
- prompt: resumeCtx.lastPrompt,
167
- mode,
168
- model,
169
- sessionId: options.id,
170
- configState: ctx.configState,
171
- providerType: resumeCtx.session.providerType
172
- })
173
- console.log(result.reply)
174
- })
175
-
176
- cmd
209
+ console.log(`resuming session ${options.id} (${resumeCtx.messageCount} messages)`)
210
+ console.log(`last prompt: ${resumeCtx.lastPrompt.slice(0, 100)}${resumeCtx.lastPrompt.length > 100 ? "..." : ""}`)
211
+ const result = await executeTurn({
212
+ prompt: resumeCtx.lastPrompt,
213
+ mode,
214
+ model,
215
+ sessionId: options.id,
216
+ configState: ctx.configState,
217
+ providerType: resumeCtx.session.providerType
218
+ })
219
+ console.log(result.reply)
220
+ })
221
+
222
+ cmd
177
223
  .command("retry")
178
- .description("retry the last failed turn in a session")
179
- .requiredOption("--id <id>", "session id to retry")
224
+ .description("retry the last failed turn in a session")
225
+ .requiredOption("--id <id>", "session id to retry")
180
226
  .action(async (options) => {
181
227
  const ctx = await buildContext()
182
228
  if (!assertRecoveryEnabled(ctx.configState.config, "session retry")) return
@@ -186,34 +232,35 @@ export function createSessionCommand() {
186
232
  console.error(`session not found: ${options.id}`)
187
233
  process.exitCode = 1
188
234
  return
189
- }
190
- if (!resumeCtx.canRetry) {
191
- console.error(`session ${options.id} has no failed turn to retry`)
192
- process.exitCode = 1
193
- return
194
- }
195
- if (!resumeCtx.canResume) {
196
- console.error(`no user message found in session ${options.id}`)
197
- process.exitCode = 1
198
- return
199
- }
235
+ }
236
+ if (!resumeCtx.canRetry) {
237
+ console.error(`session ${options.id} has no failed turn to retry`)
238
+ process.exitCode = 1
239
+ return
240
+ }
241
+ if (!resumeCtx.canResume) {
242
+ console.error(`no user message found in session ${options.id}`)
243
+ process.exitCode = 1
244
+ return
245
+ }
200
246
  await ToolRegistry.initialize({ config: ctx.configState.config, cwd: process.cwd() })
201
247
  console.log(`retrying failed turn in session ${options.id}`)
202
- const result = await executeTurn({
203
- prompt: resumeCtx.lastPrompt,
204
- mode: resumeCtx.session.mode,
205
- model: resumeCtx.session.model,
206
- sessionId: options.id,
207
- configState: ctx.configState,
208
- providerType: resumeCtx.session.providerType
209
- })
210
- console.log(result.reply)
211
- })
212
-
213
- cmd
248
+ const result = await executeTurn({
249
+ prompt: resumeCtx.lastPrompt,
250
+ mode: resumeCtx.session.mode,
251
+ model: resumeCtx.session.model,
252
+ sessionId: options.id,
253
+ configState: ctx.configState,
254
+ providerType: resumeCtx.session.providerType
255
+ })
256
+ console.log(result.reply)
257
+ })
258
+
259
+ cmd
214
260
  .command("recoverable")
215
261
  .description("list sessions that can be resumed or retried")
216
- .action(async () => {
262
+ .option("--json", "print as json", false)
263
+ .action(async (options) => {
217
264
  const ctx = await buildContext()
218
265
  if (!assertRecoveryEnabled(ctx.configState.config, "session recoverable")) return
219
266
 
@@ -224,12 +271,20 @@ export function createSessionCommand() {
224
271
  if (!sessions.length) {
225
272
  console.log("no recoverable sessions")
226
273
  return
227
- }
228
- for (const s of sessions) {
229
- const reason = s.retryMeta?.inProgress ? "in-progress" : s.status === "error" ? "error" : "unknown"
230
- console.log(`${s.id} ${s.mode} ${reason} ${new Date(s.updatedAt).toLocaleString()}`)
231
- }
232
- })
233
-
234
- return cmd
235
- }
274
+ }
275
+ if (options.json) {
276
+ const payload = await Promise.all(sessions.map(async (session) => ({
277
+ session,
278
+ resume: summarizeResumeContext(await getResumeContext(session.id, { enabled: true }))
279
+ })))
280
+ console.log(JSON.stringify(payload, null, 2))
281
+ return
282
+ }
283
+ for (const s of sessions) {
284
+ const summary = summarizeResumeContext(await getResumeContext(s.id, { enabled: true }))
285
+ console.log(`${s.id} ${s.mode} ${summary?.status || "unknown"} ${new Date(s.updatedAt).toLocaleString()} ${summary?.lastPromptPreview || ""}`)
286
+ }
287
+ })
288
+
289
+ return cmd
290
+ }
@@ -0,0 +1,63 @@
1
+ import { Command } from "commander"
2
+ import { ensureDefaultSkillPack } from "../skill/registry.mjs"
3
+ import { userRootDir } from "../storage/paths.mjs"
4
+
5
+ function formatSummary(scopeResults) {
6
+ const lines = []
7
+ for (const item of scopeResults) {
8
+ const created = item.created.join(", ")
9
+ const skipped = item.skipped.join(", ")
10
+ if (created.length) {
11
+ lines.push(`[${item.scope}] created: ${created}`)
12
+ }
13
+ if (skipped.length) {
14
+ lines.push(`[${item.scope}] already exists: ${skipped}`)
15
+ }
16
+ }
17
+ return lines
18
+ }
19
+
20
+ export function createSkillCommand() {
21
+ const cmd = new Command("skill").description("manage kkcode skills")
22
+
23
+ cmd
24
+ .command("init")
25
+ .description("initialize built-in skill packs")
26
+ .option("--project", "initialize project scope .kkcode/skills")
27
+ .option("--global", "initialize global scope (KKCODE_HOME)/skills")
28
+ .option("--all", "initialize both project and global scope")
29
+ .option("--force", "overwrite existing files")
30
+ .option("--json", "print structured output", false)
31
+ .action(async (options) => {
32
+ const cwd = process.cwd()
33
+ const includeProject = options.all || options.project || (!options.global && !options.project)
34
+ const includeGlobal = options.all || options.global || (!options.global && !options.project)
35
+
36
+ const results = await ensureDefaultSkillPack({
37
+ cwd,
38
+ force: options.force || false,
39
+ includeProject,
40
+ includeGlobal
41
+ })
42
+
43
+ if (options.json) {
44
+ console.log(JSON.stringify({ ok: true, cwd, includeProject, includeGlobal, results }, null, 2))
45
+ return
46
+ }
47
+
48
+ console.log("skill init summary:")
49
+ for (const line of formatSummary(results)) {
50
+ console.log(`- ${line}`)
51
+ }
52
+ if (!results.length) {
53
+ console.log("- no target directories selected")
54
+ }
55
+ console.log("tip:")
56
+ const globalHint = userRootDir()
57
+ console.log(" kkcode skill init --project # initialize .kkcode/skills")
58
+ console.log(` kkcode skill init --global # initialize ${globalHint}/skills`)
59
+ console.log(" kkcode skill init # initialize both")
60
+ })
61
+
62
+ return cmd
63
+ }
@@ -71,7 +71,7 @@ export function createThemeCommand() {
71
71
  printContextWarnings(ctx)
72
72
  const theme = ctx.themeState.theme
73
73
  const config = ctx.configState.config
74
- const modes = ["ask", "plan", "agent", "longagent"]
74
+ const modes = ["assistant", "plan", "agent", "longagent"]
75
75
  for (const mode of modes) {
76
76
  const line = renderStatusBar({
77
77
  mode,