@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
@@ -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
+ }