@kkelly-offical/kkcode 0.1.7 → 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.
- package/LICENSE +674 -674
- package/README.md +452 -387
- package/package.json +50 -46
- package/src/agent/agent.mjs +228 -220
- package/src/agent/custom-agent-loader.mjs +6 -3
- package/src/agent/generator.mjs +2 -2
- package/src/agent/prompt/assistant.txt +12 -0
- package/src/agent/prompt/bug-hunter.txt +89 -89
- package/src/agent/prompt/frontend-designer.txt +58 -58
- package/src/agent/prompt/guide.txt +1 -1
- package/src/agent/prompt/longagent-blueprint-agent.txt +83 -83
- package/src/agent/prompt/longagent-coding-agent.txt +37 -37
- package/src/agent/prompt/longagent-debugging-agent.txt +46 -46
- package/src/agent/prompt/longagent-preview-agent.txt +63 -63
- package/src/command/custom-commands.mjs +2 -2
- package/src/commands/agent.mjs +1 -1
- package/src/commands/background.mjs +145 -4
- package/src/commands/chat.mjs +117 -76
- package/src/commands/config.mjs +148 -1
- package/src/commands/doctor.mjs +30 -6
- package/src/commands/init.mjs +32 -6
- package/src/commands/longagent.mjs +117 -0
- package/src/commands/mcp.mjs +275 -43
- package/src/commands/permission.mjs +1 -1
- package/src/commands/session.mjs +195 -140
- package/src/commands/skill.mjs +63 -0
- package/src/commands/theme.mjs +1 -1
- package/src/config/defaults.mjs +280 -260
- package/src/config/import-config.mjs +1 -1
- package/src/config/load-config.mjs +61 -4
- package/src/config/schema.mjs +591 -574
- package/src/context.mjs +4 -1
- package/src/core/constants.mjs +97 -91
- package/src/core/types.mjs +1 -1
- package/src/github/api.mjs +78 -78
- package/src/github/auth.mjs +294 -286
- package/src/github/flow.mjs +298 -298
- package/src/github/workspace.mjs +225 -212
- package/src/index.mjs +84 -82
- package/src/knowledge/frontend-aesthetics.txt +38 -38
- package/src/mcp/client-http.mjs +139 -141
- package/src/mcp/client-sse.mjs +297 -288
- package/src/mcp/client-stdio.mjs +534 -533
- package/src/mcp/constants.mjs +2 -2
- package/src/mcp/registry.mjs +498 -479
- package/src/mcp/stdio-framing.mjs +135 -133
- package/src/mcp/tool-result.mjs +24 -24
- package/src/observability/edit-diagnostics.mjs +449 -0
- package/src/observability/index.mjs +42 -42
- package/src/observability/metrics.mjs +165 -137
- package/src/observability/tracer.mjs +137 -137
- package/src/onboarding.mjs +209 -0
- package/src/orchestration/background-manager.mjs +567 -372
- package/src/orchestration/background-worker.mjs +419 -305
- package/src/orchestration/interruption-reason.mjs +21 -0
- package/src/orchestration/longagent-manager.mjs +197 -171
- package/src/orchestration/stage-scheduler.mjs +733 -728
- package/src/orchestration/subagent-router.mjs +7 -1
- package/src/orchestration/task-scheduler.mjs +219 -7
- package/src/permission/engine.mjs +1 -1
- package/src/permission/exec-policy.mjs +370 -370
- package/src/permission/file-edit-policy.mjs +108 -0
- package/src/permission/prompt.mjs +1 -1
- package/src/permission/rules.mjs +116 -7
- package/src/plugin/builtin-hooks/post-edit-format.mjs +2 -1
- package/src/plugin/builtin-hooks/post-edit-typecheck.mjs +104 -40
- package/src/plugin/hook-bus.mjs +19 -5
- package/src/plugin/manifest-loader.mjs +222 -0
- package/src/provider/anthropic.mjs +396 -390
- package/src/provider/ollama.mjs +7 -1
- package/src/provider/openai.mjs +382 -340
- package/src/provider/retry-policy.mjs +74 -68
- package/src/provider/router.mjs +242 -241
- package/src/provider/sse.mjs +104 -104
- package/src/provider/wizard.mjs +556 -0
- package/src/repl/capability-facade.mjs +30 -0
- package/src/repl/command-surface.mjs +23 -0
- package/src/repl/controller-entry.mjs +40 -0
- package/src/repl/core-shell.mjs +208 -0
- package/src/repl/dialog-router.mjs +87 -0
- package/src/repl/input-engine.mjs +76 -0
- package/src/repl/keymap.mjs +7 -0
- package/src/repl/operator-surface.mjs +15 -0
- package/src/repl/permission-flow.mjs +49 -0
- package/src/repl/runtime-facade.mjs +36 -0
- package/src/repl/slash-router.mjs +62 -0
- package/src/repl/state-store.mjs +29 -0
- package/src/repl/turn-controller.mjs +58 -0
- package/src/repl/verification.mjs +23 -0
- package/src/repl.mjs +3368 -2981
- package/src/rules/load-rules.mjs +3 -3
- package/src/runtime.mjs +1 -1
- package/src/session/agent-transaction.mjs +86 -0
- package/src/session/checkpoint.mjs +302 -302
- package/src/session/compaction.mjs +298 -298
- package/src/session/engine.mjs +417 -232
- package/src/session/longagent-4stage.mjs +467 -460
- package/src/session/longagent-hybrid.mjs +1344 -1097
- package/src/session/longagent-plan.mjs +376 -365
- package/src/session/longagent-project-memory.mjs +53 -53
- package/src/session/longagent-scaffold.mjs +291 -291
- package/src/session/longagent-task-bus.mjs +138 -54
- package/src/session/longagent-utils.mjs +828 -472
- package/src/session/longagent.mjs +911 -900
- package/src/session/loop.mjs +1005 -930
- package/src/session/prompt/agent.txt +25 -25
- package/src/session/prompt/anthropic.txt +150 -150
- package/src/session/prompt/beast.txt +1 -1
- package/src/session/prompt/plan.txt +31 -31
- package/src/session/prompt/qwen.txt +46 -46
- package/src/session/recovery.mjs +21 -0
- package/src/session/rollback.mjs +196 -195
- package/src/session/routing-observability.mjs +72 -0
- package/src/session/runtime-state.mjs +47 -0
- package/src/session/store.mjs +523 -519
- package/src/session/system-prompt.mjs +308 -273
- package/src/session/task-validator.mjs +267 -267
- package/src/session/usability-gates.mjs +2 -2
- package/src/skill/builtin/commit.mjs +64 -64
- package/src/skill/builtin/design.mjs +76 -76
- package/src/skill/generator.mjs +18 -2
- package/src/skill/registry.mjs +642 -390
- package/src/storage/audit-store.mjs +18 -11
- package/src/storage/event-log.mjs +7 -1
- package/src/storage/ghost-commit-store.mjs +243 -245
- package/src/storage/paths.mjs +13 -0
- package/src/theme/default-theme.mjs +1 -1
- package/src/theme/markdown.mjs +4 -0
- package/src/theme/schema.mjs +1 -1
- package/src/theme/status-bar.mjs +162 -158
- package/src/tool/audit-wrapper.mjs +18 -2
- package/src/tool/edit-transaction.mjs +23 -0
- package/src/tool/executor.mjs +26 -1
- package/src/tool/file-read-state.mjs +65 -0
- package/src/tool/git-auto.mjs +526 -526
- package/src/tool/git-full-auto.mjs +487 -478
- package/src/tool/mutation-guard.mjs +54 -0
- package/src/tool/prompt/edit.txt +3 -3
- package/src/tool/prompt/multiedit.txt +1 -0
- package/src/tool/prompt/notebookedit.txt +2 -1
- package/src/tool/prompt/patch.txt +25 -24
- package/src/tool/prompt/read.txt +3 -3
- package/src/tool/prompt/sysinfo.txt +29 -0
- package/src/tool/prompt/task.txt +66 -4
- package/src/tool/prompt/write.txt +2 -2
- package/src/tool/question-prompt.mjs +99 -93
- package/src/tool/registry.mjs +1701 -1343
- package/src/tool/task-tool.mjs +14 -6
- package/src/ui/activity-renderer.mjs +667 -664
- package/src/ui/repl-background-panel.mjs +7 -0
- package/src/ui/repl-capability-panel.mjs +9 -0
- package/src/ui/repl-dashboard.mjs +54 -4
- package/src/ui/repl-help.mjs +110 -0
- package/src/ui/repl-operator-panel.mjs +12 -0
- package/src/ui/repl-route-feedback.mjs +35 -0
- package/src/ui/repl-status-view.mjs +76 -0
- package/src/ui/repl-task-panel.mjs +5 -0
- package/src/ui/repl-transcript-panel.mjs +56 -0
- package/src/ui/repl-turn-summary.mjs +135 -0
- package/src/usage/pricing.mjs +122 -121
- package/src/usage/usage-meter.mjs +1 -0
- package/src/util/git.mjs +562 -519
- 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
|
+
}
|