@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
@@ -0,0 +1,222 @@
1
+ import path from "node:path"
2
+ import { access, readFile, readdir } from "node:fs/promises"
3
+ import { userRootDir } from "../storage/paths.mjs"
4
+
5
+ async function exists(target) {
6
+ try {
7
+ await access(target)
8
+ return true
9
+ } catch {
10
+ return false
11
+ }
12
+ }
13
+
14
+ function isPlainObject(value) {
15
+ return value && typeof value === "object" && !Array.isArray(value)
16
+ }
17
+
18
+ function isWithin(rootDir, targetPath) {
19
+ const root = path.resolve(rootDir)
20
+ const target = path.resolve(targetPath)
21
+ return target === root || target.startsWith(root + path.sep)
22
+ }
23
+
24
+ function asArray(value) {
25
+ if (Array.isArray(value)) return value
26
+ if (value === undefined || value === null || value === "") return []
27
+ return [value]
28
+ }
29
+
30
+ function toStringArray(value) {
31
+ return asArray(value)
32
+ .flatMap((item) => typeof item === "string" ? item.split(",") : [item])
33
+ .map((item) => typeof item === "string" ? item.trim() : "")
34
+ .filter(Boolean)
35
+ }
36
+
37
+ function resolveRelativePath(rootDir, rawPath, label, errors) {
38
+ if (typeof rawPath !== "string" || !rawPath.trim()) {
39
+ errors.push(`${label} must be a non-empty relative path`)
40
+ return null
41
+ }
42
+ const resolved = path.resolve(rootDir, rawPath)
43
+ if (!isWithin(rootDir, resolved)) {
44
+ errors.push(`${label} points outside plugin root: ${rawPath}`)
45
+ return null
46
+ }
47
+ return resolved
48
+ }
49
+
50
+ async function listManifestFiles(dir) {
51
+ if (!(await exists(dir))) return []
52
+ const entries = await readdir(dir, { withFileTypes: true })
53
+ return entries
54
+ .filter((entry) => entry.isDirectory())
55
+ .map((entry) => path.join(dir, entry.name, "plugin.json"))
56
+ }
57
+
58
+ async function readJsonFile(filePath, label, errors) {
59
+ try {
60
+ const raw = await readFile(filePath, "utf8")
61
+ return JSON.parse(raw)
62
+ } catch (error) {
63
+ errors.push(`${label} parse failed: ${error.message}`)
64
+ return null
65
+ }
66
+ }
67
+
68
+ function normalizeComponentDirs(value, rootDir, label, errors) {
69
+ return toStringArray(value)
70
+ .map((item) => resolveRelativePath(rootDir, item, label, errors))
71
+ .filter(Boolean)
72
+ }
73
+
74
+ function normalizeComponentSpec(value, rootDir, label, errors) {
75
+ if (isPlainObject(value)) {
76
+ return {
77
+ enabled: value.enabled !== false,
78
+ dirs: normalizeComponentDirs(value.dirs ?? value.paths ?? value.path ?? value.dir ?? [], rootDir, label, errors)
79
+ }
80
+ }
81
+ return {
82
+ enabled: true,
83
+ dirs: normalizeComponentDirs(value, rootDir, label, errors)
84
+ }
85
+ }
86
+
87
+ async function normalizeMcpServers(manifest, rootDir, errors) {
88
+ const out = {}
89
+ const inlineServers = manifest.mcpServers || manifest.mcp_servers
90
+ if (isPlainObject(inlineServers)) Object.assign(out, inlineServers)
91
+
92
+ for (const entry of asArray(manifest.mcp)) {
93
+ if (typeof entry === "string") {
94
+ const filePath = resolveRelativePath(rootDir, entry, "mcp", errors)
95
+ if (!filePath) continue
96
+ const parsed = await readJsonFile(filePath, `mcp file ${entry}`, errors)
97
+ if (!parsed) continue
98
+ Object.assign(out, parsed.servers || parsed.mcpServers || {})
99
+ continue
100
+ }
101
+ if (!isPlainObject(entry)) {
102
+ errors.push("mcp entries must be strings or objects")
103
+ continue
104
+ }
105
+ if (entry.path) {
106
+ const filePath = resolveRelativePath(rootDir, entry.path, "mcp.path", errors)
107
+ if (!filePath) continue
108
+ const parsed = await readJsonFile(filePath, `mcp file ${entry.path}`, errors)
109
+ if (!parsed) continue
110
+ Object.assign(out, parsed.servers || parsed.mcpServers || {})
111
+ }
112
+ if (isPlainObject(entry.servers)) {
113
+ Object.assign(out, entry.servers)
114
+ }
115
+ }
116
+
117
+ return out
118
+ }
119
+
120
+ function normalizeCapabilities(manifest) {
121
+ const caps = isPlainObject(manifest.capabilities) ? manifest.capabilities : {}
122
+ const allowedAgentPermissions = toStringArray(
123
+ caps.allowedAgentPermissions
124
+ || caps.allowed_agent_permissions
125
+ || manifest.allowedAgentPermissions
126
+ || manifest.allowed_agent_permissions
127
+ || ["default"]
128
+ )
129
+ return {
130
+ allowedAgentPermissions: allowedAgentPermissions.length ? allowedAgentPermissions : ["default"]
131
+ }
132
+ }
133
+
134
+ async function loadManifest(filePath, scope) {
135
+ const errors = []
136
+ const manifest = await readJsonFile(filePath, "plugin manifest", errors)
137
+ if (!manifest) return { plugin: null, errors }
138
+
139
+ const rootDir = path.dirname(filePath)
140
+ const components = isPlainObject(manifest.components) ? manifest.components : {}
141
+ const name = typeof manifest.name === "string" && manifest.name.trim()
142
+ ? manifest.name.trim()
143
+ : path.basename(rootDir)
144
+ const skillSpec = normalizeComponentSpec(manifest.skills ?? components.skills, rootDir, "skills", errors)
145
+ const agentSpec = normalizeComponentSpec(manifest.agents ?? components.agents, rootDir, "agents", errors)
146
+ const hookSpec = normalizeComponentSpec(manifest.hooks ?? components.hooks, rootDir, "hooks", errors)
147
+
148
+ const plugin = {
149
+ name,
150
+ version: typeof manifest.version === "string" ? manifest.version : null,
151
+ manifestVersion: manifest.manifest_version ?? manifest.manifestVersion ?? 1,
152
+ enabled: manifest.enabled !== false && manifest.disabled !== true,
153
+ displayName: typeof manifest.displayName === "string" ? manifest.displayName.trim() : null,
154
+ disabledReason: typeof manifest.disabled_reason === "string" ? manifest.disabled_reason : null,
155
+ scope,
156
+ source: filePath,
157
+ rootDir,
158
+ skillsEnabled: skillSpec.enabled,
159
+ agentsEnabled: agentSpec.enabled,
160
+ hooksEnabled: hookSpec.enabled,
161
+ skills: skillSpec.dirs,
162
+ agents: agentSpec.dirs,
163
+ hooks: hookSpec.dirs,
164
+ mcpServers: await normalizeMcpServers(manifest, rootDir, errors),
165
+ capabilities: normalizeCapabilities(manifest)
166
+ }
167
+
168
+ return { plugin, errors }
169
+ }
170
+
171
+ async function candidateManifestFiles(cwd) {
172
+ const files = [
173
+ path.join(userRootDir(), ".kkcode-plugin", "plugin.json"),
174
+ ...(await listManifestFiles(path.join(userRootDir(), "plugins"))),
175
+ ...(await listManifestFiles(path.join(userRootDir(), "plugin"))),
176
+ path.join(cwd, ".kkcode-plugin", "plugin.json"),
177
+ ...(await listManifestFiles(path.join(cwd, ".kkcode", "plugins"))),
178
+ ...(await listManifestFiles(path.join(cwd, ".kkcode", "plugin")))
179
+ ]
180
+
181
+ const seen = new Set()
182
+ return files.filter((file) => {
183
+ const resolved = path.resolve(file)
184
+ if (seen.has(resolved)) return false
185
+ seen.add(resolved)
186
+ return true
187
+ })
188
+ }
189
+
190
+ export async function discoverLocalPluginManifests(cwd = process.cwd()) {
191
+ const files = await candidateManifestFiles(cwd)
192
+ const plugins = []
193
+ const errors = []
194
+
195
+ for (const file of files) {
196
+ if (!(await exists(file))) continue
197
+ const scope = isWithin(cwd, file) ? "project" : "global"
198
+ const loaded = await loadManifest(file, scope)
199
+ if (loaded.plugin) plugins.push(loaded.plugin)
200
+ errors.push(...loaded.errors.map((message) => `${file}: ${message}`))
201
+ }
202
+
203
+ return { plugins, errors }
204
+ }
205
+
206
+ export function pluginComponentDirs(plugins, key) {
207
+ const enabledFlag = `${key}Enabled`
208
+ return plugins.flatMap((plugin) => (plugin.enabled === false || plugin[enabledFlag] === false ? [] : (plugin[key] || [])).map((dir) => ({
209
+ dir,
210
+ scope: `plugin:${plugin.scope}:${plugin.name}`,
211
+ plugin
212
+ })))
213
+ }
214
+
215
+ export function pluginMcpServers(plugins) {
216
+ const servers = {}
217
+ for (const plugin of plugins) {
218
+ if (plugin.enabled === false) continue
219
+ Object.assign(servers, plugin.mcpServers || {})
220
+ }
221
+ return servers
222
+ }