@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,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,
@@ -0,0 +1,32 @@
1
+ import { Command } from "commander"
2
+ import { loadConfig } from "../config/load-config.mjs"
3
+ import { PACKAGE_VERSION } from "../version.mjs"
4
+ import { checkForUpdate, installUpdate, updateMessage } from "../update/checker.mjs"
5
+
6
+ export function createUpdateCommand() {
7
+ return new Command("update")
8
+ .description("check for and install kkcode updates")
9
+ .option("--check", "only check for updates", false)
10
+ .option("--install", "install the selected update", false)
11
+ .option("--channel <channel>", "npm dist-tag to follow", null)
12
+ .option("--json", "print structured result", false)
13
+ .action(async (options) => {
14
+ const state = await loadConfig(process.cwd())
15
+ const config = { ...state.config, update: { ...(state.config.update || {}) } }
16
+ if (options.channel) config.update.channel = options.channel
17
+ const result = await checkForUpdate(config, { force: true, currentVersion: PACKAGE_VERSION })
18
+ if (options.json) {
19
+ console.log(JSON.stringify(result, null, 2))
20
+ } else if (result.hasUpdate) {
21
+ console.log(updateMessage(result))
22
+ } else {
23
+ console.log(`kkcode is up to date (${result.currentVersion}) on ${result.channel}`)
24
+ }
25
+
26
+ if (options.check || !options.install) return
27
+ if (!result.hasUpdate) return
28
+ const installed = await installUpdate(config, { channel: result.channel })
29
+ if (!installed.ok) throw new Error(`update install failed: ${installed.error}`)
30
+ console.log(`installed kkcode ${result.latestVersion}; restart your shell or kkcode session if needed`)
31
+ })
32
+ }