@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
@@ -33,7 +33,13 @@ export function resolveSubagent({ config, subagentType = null, category = null }
33
33
  mode: "agent"
34
34
  }
35
35
  }
36
- throw new Error(`unknown subagent_type: ${subagentType}`)
36
+ // Unknown subagent type with configured subagents — return structured error fallback
37
+ return {
38
+ name: subagentType,
39
+ mode: "agent",
40
+ fallback: true,
41
+ reason: `unknown subagent_type: ${subagentType}`
42
+ }
37
43
  }
38
44
 
39
45
  if (category) {
@@ -1,8 +1,209 @@
1
1
  import { BackgroundManager } from "./background-manager.mjs"
2
2
  import { resolveSubagent } from "./subagent-router.mjs"
3
+ import { flushNow, forkSession, getSession } from "../session/store.mjs"
4
+ import { extractEditFeedbackFromToolEvents } from "../observability/edit-diagnostics.mjs"
5
+
6
+ const SUPPORTED_EXECUTION_MODES = new Set(["fresh_agent", "fork_context"])
7
+ const SUPPORTED_ISOLATION_MODES = new Set(["default", "worktree"])
8
+
9
+ function extractFileChanges(toolEvents = []) {
10
+ return toolEvents
11
+ .flatMap((event) => Array.isArray(event?.metadata?.fileChanges) ? event.metadata.fileChanges : [])
12
+ .map((item) => ({
13
+ path: String(item?.path || "").trim(),
14
+ addedLines: Math.max(0, Number(item?.addedLines || 0)),
15
+ removedLines: Math.max(0, Number(item?.removedLines || 0)),
16
+ stageId: item?.stageId ? String(item.stageId) : "",
17
+ taskId: item?.taskId ? String(item.taskId) : ""
18
+ }))
19
+ .filter((item) => item.path)
20
+ }
21
+
22
+ function normalizeExecutionMode(raw) {
23
+ const mode = String(raw || "fresh_agent").trim().toLowerCase() || "fresh_agent"
24
+ if (!SUPPORTED_EXECUTION_MODES.has(mode)) {
25
+ return { error: `unsupported task.execution_mode: ${raw}` }
26
+ }
27
+ return { mode }
28
+ }
29
+
30
+ function normalizeIsolation(raw) {
31
+ const mode = String(raw || "default").trim().toLowerCase() || "default"
32
+ if (!SUPPORTED_ISOLATION_MODES.has(mode)) {
33
+ return { error: `unsupported task.isolation: ${raw}` }
34
+ }
35
+ return { mode }
36
+ }
37
+
38
+ function normalizeList(input) {
39
+ if (Array.isArray(input)) {
40
+ return input
41
+ .map((item) => String(item || "").trim())
42
+ .filter(Boolean)
43
+ }
44
+ if (typeof input === "string") {
45
+ const value = input.trim()
46
+ return value ? [value] : []
47
+ }
48
+ return []
49
+ }
50
+
51
+ function normalizeWriteScope(input) {
52
+ return String(input || "").trim().toLowerCase()
53
+ }
54
+
55
+ function isReadOnlyWriteScope(input) {
56
+ const scope = normalizeWriteScope(input)
57
+ if (!scope) return false
58
+ return [
59
+ "read-only",
60
+ "readonly",
61
+ "no-mutation",
62
+ "no-mutations",
63
+ "no mutation",
64
+ "no mutations",
65
+ "no-write",
66
+ "no-writes",
67
+ "no write",
68
+ "no writes"
69
+ ].includes(scope) || scope.includes("read-only") || scope.includes("no mutation") || scope.includes("no write")
70
+ }
71
+
72
+ function validateDelegationArgs(args = {}, executionMode) {
73
+ const explicitPrompt = String(args.prompt || "").trim()
74
+ const objective = String(args.objective || "").trim()
75
+ const writeScope = String(args.write_scope || "").trim()
76
+ const deliverable = String(args.deliverable || "").trim()
77
+ const isContinuation = Boolean(args.session_id)
78
+ const hasStructuredContinuationFields =
79
+ objective
80
+ || String(args.why || "").trim()
81
+ || writeScope
82
+ || deliverable
83
+ || normalizeList(args.starting_points).length
84
+ || normalizeList(args.constraints).length
85
+ || normalizeList(args.planned_files).length
86
+
87
+ if (!explicitPrompt && !objective && !isContinuation) {
88
+ return "task.prompt or task.objective is required when session_id is not provided"
89
+ }
90
+ if (isContinuation && hasStructuredContinuationFields) {
91
+ return "task.session_id cannot be combined with structured brief fields; use a short continuation prompt instead"
92
+ }
93
+ if (isContinuation && !explicitPrompt) {
94
+ return "task.prompt is required when continuing an existing delegated session"
95
+ }
96
+ if (isContinuation && args.execution_mode) {
97
+ return "task.execution_mode only applies when starting a new delegated session"
98
+ }
99
+ if (!explicitPrompt && objective && !writeScope) {
100
+ return "task.write_scope is required when synthesizing a new delegation brief"
101
+ }
102
+ if (!explicitPrompt && objective && !deliverable) {
103
+ return "task.deliverable is required when synthesizing a new delegation brief"
104
+ }
105
+ if (executionMode === "fork_context" && !isReadOnlyWriteScope(writeScope) && !isContinuation) {
106
+ return "task.execution_mode=fork_context is reserved for read-only sidecar work; use fresh_agent for implementation"
107
+ }
108
+ if (args.run_in_background && args.allow_question === true) {
109
+ return "task.run_in_background does not support allow_question=true"
110
+ }
111
+ const isolation = String(args.isolation || "default").trim().toLowerCase() || "default"
112
+ if (isolation === "worktree" && executionMode !== "fresh_agent") {
113
+ return "task.isolation=worktree currently requires execution_mode='fresh_agent'"
114
+ }
115
+ if (isolation === "worktree" && args.run_in_background !== true) {
116
+ return "task.isolation=worktree currently requires run_in_background=true"
117
+ }
118
+ return null
119
+ }
120
+
121
+ function buildDelegationPrompt(args = {}) {
122
+ const explicitPrompt = String(args.prompt || "").trim()
123
+ if (explicitPrompt) return explicitPrompt
124
+
125
+ const objective = String(args.objective || "").trim()
126
+ if (!objective) return ""
127
+ const executionMode = String(args.execution_mode || "fresh_agent").trim().toLowerCase() || "fresh_agent"
128
+ const isolation = String(args.isolation || "default").trim().toLowerCase() || "default"
129
+
130
+ const why = String(args.why || "").trim()
131
+ const writeScope = String(args.write_scope || "").trim()
132
+ const startingPoints = normalizeList(args.starting_points)
133
+ const constraints = normalizeList(args.constraints)
134
+ const deliverable = String(args.deliverable || "").trim()
135
+ const plannedFiles = normalizeList(args.planned_files)
136
+
137
+ const lines = [`Objective: ${objective}`]
138
+ if (why) lines.push(`Why: ${why}`)
139
+ if (writeScope) lines.push(`Write scope: ${writeScope}`)
140
+ if (startingPoints.length) {
141
+ lines.push("Starting points:")
142
+ for (const item of startingPoints) lines.push(`- ${item}`)
143
+ }
144
+ if (constraints.length) {
145
+ lines.push("Constraints:")
146
+ for (const item of constraints) lines.push(`- ${item}`)
147
+ }
148
+ if (plannedFiles.length) {
149
+ lines.push("Planned files:")
150
+ for (const item of plannedFiles) lines.push(`- ${item}`)
151
+ }
152
+ if (deliverable) lines.push(`Deliverable: ${deliverable}`)
153
+
154
+ lines.push("Execution contract:")
155
+ lines.push("- Stay local instead of delegating if a direct read/edit/run action would finish the next step faster.")
156
+ if (executionMode === "fork_context") {
157
+ lines.push("- This is a forked-context sidecar: inherit parent context, keep the brief directive-style, and avoid restating the full parent thread.")
158
+ } else {
159
+ lines.push("- This is a fresh agent: assume zero inherited context and include all required context in the brief.")
160
+ }
161
+ if (isolation === "worktree") {
162
+ lines.push("- Run this delegated slice inside a local detached git worktree. Keep all execution local and self-contained.")
163
+ }
164
+ lines.push("- Never delegate understanding of the problem itself; delegate execution, verification, or bounded research against an already-understood objective.")
165
+ lines.push("- Do not guess unfinished results or treat background work as completed before it settles.")
166
+ lines.push("- Do not fabricate completion or present unfinished work as done.")
167
+ lines.push("- Do not peek at unfinished sibling work and turn guesses into facts.")
168
+ lines.push("- Background delegates must stay non-interactive; if clarification is needed, keep the work in the foreground.")
169
+
170
+ return lines.join("\n")
171
+ }
172
+
173
+ async function ensureDelegatedSession({ executionMode, parentSessionId, subSessionId }) {
174
+ if (executionMode !== "fork_context") return
175
+
176
+ if (!parentSessionId) {
177
+ throw new Error("fork_context requires a parent session")
178
+ }
179
+
180
+ const existing = await getSession(subSessionId)
181
+ if (existing) return
182
+
183
+ const forked = await forkSession({
184
+ sessionId: parentSessionId,
185
+ newSessionId: subSessionId,
186
+ title: `fork:${subSessionId}`
187
+ })
188
+
189
+ if (!forked) {
190
+ throw new Error(`fork_context parent session not found: ${parentSessionId}`)
191
+ }
192
+
193
+ await flushNow()
194
+ }
3
195
 
4
196
  export function createTaskDelegate({ config, parentSessionId, model, providerType, runSubtask }) {
5
197
  return async function delegateTask(args = {}) {
198
+ const executionModeResult = normalizeExecutionMode(args.execution_mode)
199
+ if (executionModeResult.error) return { error: executionModeResult.error }
200
+ const executionMode = executionModeResult.mode
201
+ const isolationResult = normalizeIsolation(args.isolation)
202
+ if (isolationResult.error) return { error: isolationResult.error }
203
+ const validationError = validateDelegationArgs(args, executionMode)
204
+ if (validationError) return { error: validationError }
205
+ const isolation = isolationResult.mode
206
+
6
207
  const subagent = resolveSubagent({
7
208
  config,
8
209
  subagentType: args.subagent_type || null,
@@ -10,16 +211,17 @@ export function createTaskDelegate({ config, parentSessionId, model, providerTyp
10
211
  })
11
212
 
12
213
  const subSessionId = String(args.session_id || `sub_${parentSessionId}_${Date.now()}`)
13
- const prompt = String(args.prompt || "").trim() || (args.session_id ? "Continue from existing sub-session context." : "")
14
-
15
- if (!prompt) {
16
- return { error: "task.prompt is required when session_id is not provided" }
17
- }
214
+ const prompt = buildDelegationPrompt(args)
18
215
 
19
216
  const subModel = subagent.model || model
20
217
  const subProvider = subagent.providerType || providerType
21
218
 
22
219
  const run = async ({ isCancelled, log }) => {
220
+ await ensureDelegatedSession({
221
+ executionMode,
222
+ parentSessionId,
223
+ subSessionId
224
+ })
23
225
  await log(`task started (${subagent.name})`)
24
226
  const out = await runSubtask({
25
227
  prompt,
@@ -31,11 +233,17 @@ export function createTaskDelegate({ config, parentSessionId, model, providerTyp
31
233
  })
32
234
  await log(out.reply)
33
235
  if (isCancelled()) return { cancelled: true }
236
+ const fileChanges = extractFileChanges(out.toolEvents || [])
237
+ const editFeedback = extractEditFeedbackFromToolEvents(out.toolEvents || [])
34
238
  return {
35
239
  session_id: subSessionId,
240
+ parent_session_id: parentSessionId,
36
241
  subagent: subagent.name,
242
+ execution_mode: executionMode,
37
243
  reply: out.reply,
38
- tool_events: out.toolEvents?.length || 0
244
+ tool_events: out.toolEvents?.length || 0,
245
+ file_changes: fileChanges,
246
+ edit_feedback: editFeedback
39
247
  }
40
248
  }
41
249
 
@@ -49,6 +257,8 @@ export function createTaskDelegate({ config, parentSessionId, model, providerTyp
49
257
  cwd: process.cwd(),
50
258
  model: subModel,
51
259
  providerType: subProvider,
260
+ executionMode,
261
+ isolation,
52
262
  subagent: subagent.name,
53
263
  category: args.category || null,
54
264
  subagentType: subagent.name,
@@ -62,7 +272,9 @@ export function createTaskDelegate({ config, parentSessionId, model, providerTyp
62
272
  return {
63
273
  background_task_id: task.id,
64
274
  status: task.status,
65
- session_id: subSessionId
275
+ session_id: subSessionId,
276
+ execution_mode: executionMode,
277
+ isolation
66
278
  }
67
279
  }
68
280
 
@@ -5,7 +5,7 @@ import { evaluatePermission } from "./rules.mjs"
5
5
  import { askPermissionInteractive } from "./prompt.mjs"
6
6
 
7
7
  const sessionAllow = new Map()
8
- let workspaceTrusted = true
8
+ let workspaceTrusted = false
9
9
 
10
10
  function cacheKey(tool, pattern) {
11
11
  return `${tool}::${pattern || "*"}`