@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.
- package/LICENSE +674 -674
- package/README.md +474 -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/commands/update.mjs +32 -0
- package/src/config/defaults.mjs +289 -260
- package/src/config/import-config.mjs +1 -1
- package/src/config/load-config.mjs +61 -4
- package/src/config/schema.mjs +604 -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 +87 -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 +4 -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 +3371 -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 +17 -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/update/checker.mjs +184 -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
- package/src/version.mjs +3 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function renderBackgroundSummaryPanel(backgroundSummary) {
|
|
2
|
+
if (!backgroundSummary) return []
|
|
3
|
+
return [
|
|
4
|
+
`background=${backgroundSummary.active} active (pending:${backgroundSummary.counts.pending}, running:${backgroundSummary.counts.running})`,
|
|
5
|
+
`background.terminal=completed:${backgroundSummary.counts.completed} interrupted:${backgroundSummary.counts.interrupted} error:${backgroundSummary.counts.error}`
|
|
6
|
+
]
|
|
7
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function renderCapabilityPanel(snapshot) {
|
|
2
|
+
if (!snapshot) return []
|
|
3
|
+
return [
|
|
4
|
+
"capability surface:",
|
|
5
|
+
` mode=${snapshot.mode}`,
|
|
6
|
+
` commands=${snapshot.customCommands} skills=${snapshot.skills} tools=${snapshot.tools}`,
|
|
7
|
+
` mcp=${snapshot.healthyMcp}/${snapshot.mcpServers} healthy agents=${snapshot.agents}`
|
|
8
|
+
]
|
|
9
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { homedir } from "node:os"
|
|
2
|
+
import { resolve } from "node:path"
|
|
2
3
|
import { paint } from "../theme/color.mjs"
|
|
3
4
|
|
|
4
5
|
function stripAnsi(text) {
|
|
@@ -101,11 +102,50 @@ function centerLine(text, width) {
|
|
|
101
102
|
function shortenPath(path) {
|
|
102
103
|
const home = homedir()
|
|
103
104
|
if (!path) return ""
|
|
104
|
-
const
|
|
105
|
+
const normalizedPath = resolve(path).replace(/\\/g, "/")
|
|
106
|
+
const homeNorm = home ? resolve(home).replace(/\\/g, "/") : ""
|
|
107
|
+
const replaced = (home && normalizedPath.startsWith(`${homeNorm}/`)) || normalizedPath === homeNorm
|
|
108
|
+
? `~${normalizedPath.slice(homeNorm.length)}`
|
|
109
|
+
: path
|
|
105
110
|
if (replaced.length <= 72) return replaced
|
|
106
111
|
return `...${replaced.slice(-69)}`
|
|
107
112
|
}
|
|
108
113
|
|
|
114
|
+
function formatMcpStatus(summary) {
|
|
115
|
+
if (!summary) return []
|
|
116
|
+
const lines = [
|
|
117
|
+
`MCP: configured ${summary.configured}, healthy ${summary.healthy}, tools ${summary.tools || 0}`
|
|
118
|
+
]
|
|
119
|
+
if (!summary.configured) {
|
|
120
|
+
lines.push(" quickstart: kkcode mcp init --project --with-skills")
|
|
121
|
+
return lines
|
|
122
|
+
}
|
|
123
|
+
for (const entry of summary.entries || []) {
|
|
124
|
+
const byServer = summary.byServer?.[entry.name]
|
|
125
|
+
const toolCount = byServer !== undefined ? ` (${byServer} tools)` : ""
|
|
126
|
+
lines.push(` ${entry.ok ? "✓" : "×"} ${entry.name}${toolCount}`)
|
|
127
|
+
}
|
|
128
|
+
return lines
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function formatSkillStatus(summary) {
|
|
132
|
+
if (!summary) return []
|
|
133
|
+
const mdTotal = summary.template + summary.skillMd
|
|
134
|
+
const lines = [
|
|
135
|
+
`Skills: ${summary.total} loaded`,
|
|
136
|
+
` markdown: ${mdTotal} (templates: ${summary.template}, dirs: ${summary.skillMd})`,
|
|
137
|
+
` mcp prompts: ${summary.mcpPrompt}`,
|
|
138
|
+
` programmable: ${summary.programmable}`
|
|
139
|
+
]
|
|
140
|
+
if (summary.total === 0) {
|
|
141
|
+
lines.push(" quickstart: kkcode skill init --project")
|
|
142
|
+
}
|
|
143
|
+
if (summary.total > 0) {
|
|
144
|
+
lines.push(" try: /code-review /test-plan")
|
|
145
|
+
}
|
|
146
|
+
return lines
|
|
147
|
+
}
|
|
148
|
+
|
|
109
149
|
function renderTag(theme, label, fg = "#0b0b0b", bg = theme.base.accent) {
|
|
110
150
|
return paint(` ${label} `, fg, { bg, bold: true })
|
|
111
151
|
}
|
|
@@ -179,6 +219,8 @@ export function renderReplDashboard({
|
|
|
179
219
|
providers,
|
|
180
220
|
recentSessions,
|
|
181
221
|
customCommandCount,
|
|
222
|
+
mcpSummary = null,
|
|
223
|
+
skillSummary = null,
|
|
182
224
|
cwd,
|
|
183
225
|
columns = null
|
|
184
226
|
}) {
|
|
@@ -204,7 +246,9 @@ export function renderReplDashboard({
|
|
|
204
246
|
`Mode: ${state.mode}`,
|
|
205
247
|
`Provider: ${state.providerType}`,
|
|
206
248
|
`Model: ${state.model}`,
|
|
207
|
-
`Custom commands: ${customCommandCount}
|
|
249
|
+
`Custom commands: ${customCommandCount}`,
|
|
250
|
+
...formatMcpStatus(mcpSummary),
|
|
251
|
+
...formatSkillStatus(skillSummary)
|
|
208
252
|
]
|
|
209
253
|
},
|
|
210
254
|
{
|
|
@@ -226,6 +270,12 @@ export function renderReplDashboard({
|
|
|
226
270
|
title: "Tips",
|
|
227
271
|
color: theme.semantic.warn,
|
|
228
272
|
items: [
|
|
273
|
+
...[
|
|
274
|
+
"Commands for bootstrap:",
|
|
275
|
+
" kkcode mcp discover",
|
|
276
|
+
" kkcode mcp init --project",
|
|
277
|
+
" kkcode skill init"
|
|
278
|
+
],
|
|
229
279
|
"Use /dash to redraw this panel",
|
|
230
280
|
"Use /clear to clear screen",
|
|
231
281
|
"Use /model <id> to override model",
|
|
@@ -239,7 +289,7 @@ export function renderReplDashboard({
|
|
|
239
289
|
},
|
|
240
290
|
{
|
|
241
291
|
title: "Providers",
|
|
242
|
-
color: theme.modes.
|
|
292
|
+
color: theme.modes.assistant,
|
|
243
293
|
items: [providers.length ? providers.join(" | ") : "(none configured)"]
|
|
244
294
|
},
|
|
245
295
|
{
|
|
@@ -247,7 +297,7 @@ export function renderReplDashboard({
|
|
|
247
297
|
color: theme.modes.longagent,
|
|
248
298
|
items: [
|
|
249
299
|
"/history /resume /commands /reload",
|
|
250
|
-
"/
|
|
300
|
+
"/assistant /plan /agent /longagent"
|
|
251
301
|
]
|
|
252
302
|
}
|
|
253
303
|
]
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
function padRight(text, width) {
|
|
2
|
+
const value = String(text || "")
|
|
3
|
+
return value.length >= width ? value : value + " ".repeat(width - value.length)
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function buildHelpText({ providers = [], userRootPath = "" } = {}) {
|
|
7
|
+
const W = 30
|
|
8
|
+
const row = (cmd, desc) => ` ${padRight(cmd, W)} ${desc}`
|
|
9
|
+
const lines = ["", "Commands:"]
|
|
10
|
+
|
|
11
|
+
lines.push("")
|
|
12
|
+
lines.push(" Session")
|
|
13
|
+
lines.push(row("/new,/n", "start a new session"))
|
|
14
|
+
lines.push(row("/resume [id],/r [id]", "resume a previous session"))
|
|
15
|
+
lines.push(row("/history", "list recent sessions"))
|
|
16
|
+
lines.push(row("/session,/s", "print current session id"))
|
|
17
|
+
lines.push(row("/compact", "summarize conversation to free context"))
|
|
18
|
+
lines.push(row("/undo", "undo last code changes"))
|
|
19
|
+
|
|
20
|
+
lines.push("")
|
|
21
|
+
lines.push(" Mode & Provider")
|
|
22
|
+
lines.push(row("/assistant /plan /agent /code /longagent", "quick mode switch to the public execution lanes"))
|
|
23
|
+
lines.push(row("/mode <name>,/m <name>", "switch mode (assistant|plan|agent|code|coding|longagent)"))
|
|
24
|
+
lines.push(row("/longagent 4stage|hybrid", "switch longagent impl"))
|
|
25
|
+
lines.push(row("/provider <type>,/p <type>", `switch provider (${providers.join("|") || "configured"})`))
|
|
26
|
+
lines.push(row("/provider edit <name>", "edit existing provider config"))
|
|
27
|
+
lines.push(row("/model <id>", "set active model"))
|
|
28
|
+
lines.push(row("", "assistant = personal CLI + Q&A lane · plan = spec only · agent/code = coding · longagent = staged"))
|
|
29
|
+
|
|
30
|
+
lines.push("")
|
|
31
|
+
lines.push(" Profile & Workspace")
|
|
32
|
+
lines.push(row("/profile", "view or edit your user profile"))
|
|
33
|
+
lines.push(row("/like", "show welcome screen / re-run onboarding"))
|
|
34
|
+
lines.push(row("/trust", "trust this workspace"))
|
|
35
|
+
lines.push(row("/untrust", "revoke workspace trust"))
|
|
36
|
+
|
|
37
|
+
lines.push("")
|
|
38
|
+
lines.push(" Tools & Display")
|
|
39
|
+
lines.push(row("/permission [...]", "adjust permission mode/policy"))
|
|
40
|
+
lines.push(row("/paste [text]", "paste clipboard image"))
|
|
41
|
+
lines.push(row("/status", "show current runtime state"))
|
|
42
|
+
lines.push(row("/dash,/home", "redraw dashboard"))
|
|
43
|
+
lines.push(row("/clear,/cls", "clear terminal"))
|
|
44
|
+
lines.push(row("/keys,/k", "show key map"))
|
|
45
|
+
|
|
46
|
+
lines.push("")
|
|
47
|
+
lines.push(" Custom Extensions")
|
|
48
|
+
lines.push(row("/commands", "list custom slash commands"))
|
|
49
|
+
lines.push(row("/create-skill <desc>", "generate a new skill via AI"))
|
|
50
|
+
lines.push(row("/create-agent <desc>", "generate a new sub-agent via AI"))
|
|
51
|
+
lines.push(row("/reload", "reload commands, skills, agents"))
|
|
52
|
+
|
|
53
|
+
lines.push("")
|
|
54
|
+
lines.push(row("/help,/h,/?", "show this help"))
|
|
55
|
+
lines.push(row("/exit,/quit,/q", "quit"))
|
|
56
|
+
|
|
57
|
+
lines.push("")
|
|
58
|
+
lines.push("Configuration:")
|
|
59
|
+
lines.push(` Global config ${userRootPath}/config.yaml`)
|
|
60
|
+
lines.push(" Project config kkcode.config.yaml / .kkcode/config.yaml")
|
|
61
|
+
lines.push(" Custom commands .kkcode/commands/ (project-level slash commands)")
|
|
62
|
+
lines.push(` Custom skills ${userRootPath}/skills/ or .kkcode/skills/`)
|
|
63
|
+
lines.push(` Custom agents ${userRootPath}/agents/ or .kkcode/agents/`)
|
|
64
|
+
lines.push(" Custom tools .kkcode/tools/ (project-level tool definitions)")
|
|
65
|
+
lines.push(" Hooks .kkcode/hooks/ (project-level hook scripts)")
|
|
66
|
+
lines.push(" Plugin packages .kkcode-plugin/ or .kkcode/plugins/<name>/")
|
|
67
|
+
lines.push(" Rules .kkcode/rules/ (project-level prompt rules)")
|
|
68
|
+
lines.push(" Instructions .kkcode/instructions.md or KKCODE.md")
|
|
69
|
+
lines.push(" MCP servers config.* -> mcp.servers")
|
|
70
|
+
lines.push("")
|
|
71
|
+
lines.push("Key config settings:")
|
|
72
|
+
lines.push(" provider.default default provider name")
|
|
73
|
+
lines.push(" provider.<name>.api_key_env env var for API key")
|
|
74
|
+
lines.push(" provider.<name>.default_model default model id")
|
|
75
|
+
lines.push(" agent.default_mode startup mode (assistant|plan|agent|code|longagent)")
|
|
76
|
+
lines.push(" agent.longagent.git.enabled git branch mgmt (true|false|\"ask\")")
|
|
77
|
+
lines.push(" permission.mode tool approvals (auto|manual|yolo)")
|
|
78
|
+
lines.push(" permission.default_policy manual policy (ask|allow|deny)")
|
|
79
|
+
lines.push(" usage.budget.session_usd per-session cost limit")
|
|
80
|
+
lines.push("")
|
|
81
|
+
lines.push("See notice.md in project root for full configuration guide.")
|
|
82
|
+
return lines.join("\n")
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function buildShortcutLegend() {
|
|
86
|
+
return [
|
|
87
|
+
"",
|
|
88
|
+
"Shortcut Map:",
|
|
89
|
+
" /h Help",
|
|
90
|
+
" /n New session",
|
|
91
|
+
" /r Resume latest session",
|
|
92
|
+
" /m Switch mode",
|
|
93
|
+
" /p Switch provider",
|
|
94
|
+
" /k Show this key map",
|
|
95
|
+
" /permission [show|auto|yolo|ask|allow|deny|non-tty <allow_once|deny>|save [project|user]|session-clear]",
|
|
96
|
+
" /dash Redraw dashboard",
|
|
97
|
+
" /clear Clear screen",
|
|
98
|
+
" /assistant /plan /agent /code /longagent Quick lane switch",
|
|
99
|
+
"",
|
|
100
|
+
"TUI keys:",
|
|
101
|
+
" Enter choose slash suggestion / submit prompt",
|
|
102
|
+
" Ctrl+J insert newline (Shift+Enter if terminal supports)",
|
|
103
|
+
" /paste paste image from clipboard (Ctrl+V if terminal supports)",
|
|
104
|
+
" Up/Down navigate suggestion/history",
|
|
105
|
+
" Left/Right/Home/End edit cursor",
|
|
106
|
+
" Ctrl+Up/Down scroll log Ctrl+Home/End oldest/latest",
|
|
107
|
+
" Tab cycle lane (assistant -> agent -> longagent -> plan)",
|
|
108
|
+
" Esc interrupt turn Ctrl+C×2 exit"
|
|
109
|
+
].join("\n")
|
|
110
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function renderOperatorPanel(snapshot) {
|
|
2
|
+
if (!snapshot) return []
|
|
3
|
+
const lines = [
|
|
4
|
+
"operator surface:",
|
|
5
|
+
` recoverable=${snapshot.recoverableCount}`,
|
|
6
|
+
` background.active=${snapshot.activeBackground}`
|
|
7
|
+
]
|
|
8
|
+
for (const action of snapshot.actions || []) {
|
|
9
|
+
lines.push(` next: ${action}`)
|
|
10
|
+
}
|
|
11
|
+
return lines
|
|
12
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
function formatModeLabel(mode) {
|
|
2
|
+
if (mode === "assistant") return "assistant(个人助手)"
|
|
3
|
+
if (mode === "agent") return "agent/code(编码)"
|
|
4
|
+
return String(mode || "assistant")
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function buildRouteFeedback({ route, currentMode, routeSummary = "" } = {}) {
|
|
8
|
+
if (!route) {
|
|
9
|
+
return {
|
|
10
|
+
changedMessage: null,
|
|
11
|
+
forcedMessage: null,
|
|
12
|
+
suggestionMessage: null,
|
|
13
|
+
stayedMessage: null,
|
|
14
|
+
summaryMessage: null
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const routeExplanation = route.explanation || route.reason || ""
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
changedMessage: route.changed
|
|
22
|
+
? `⟳ 自动切换到 ${formatModeLabel(route.mode)} 模式(${routeExplanation})`
|
|
23
|
+
: null,
|
|
24
|
+
forcedMessage: route.forced && route.suggestion
|
|
25
|
+
? `⚠ 这看起来是个简单任务(${routeExplanation}),建议用 ${route.suggestion} 模式。输入 y 继续用 longagent,或 n 切换到 ${route.suggestion}。`
|
|
26
|
+
: null,
|
|
27
|
+
suggestionMessage: route.suggestion === "longagent" && (currentMode === "assistant" || currentMode === "agent")
|
|
28
|
+
? `💡 这看起来是个复杂任务(${routeExplanation}),可以用 /longagent 切换到 longagent 模式获得更好效果。`
|
|
29
|
+
: null,
|
|
30
|
+
stayedMessage: !route.changed && !route.suggestion && !(route.forced && route.suggestion) && route.reason !== "low_confidence"
|
|
31
|
+
? `↳ 保持 ${formatModeLabel(currentMode)} 模式(${routeExplanation})`
|
|
32
|
+
: null,
|
|
33
|
+
summaryMessage: routeSummary ? ` ${routeSummary}` : null
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { renderStatusBar } from "../theme/status-bar.mjs"
|
|
2
|
+
import { renderReplDashboard, renderReplLogo, renderStartupHint } from "./repl-dashboard.mjs"
|
|
3
|
+
import { formatRuntimeStateText } from "./repl-turn-summary.mjs"
|
|
4
|
+
import { renderOperatorPanel } from "./repl-operator-panel.mjs"
|
|
5
|
+
|
|
6
|
+
export function renderReplStatusLine({
|
|
7
|
+
state,
|
|
8
|
+
configState,
|
|
9
|
+
theme,
|
|
10
|
+
tokenMeter,
|
|
11
|
+
cost,
|
|
12
|
+
costSavings = 0,
|
|
13
|
+
contextMeter = null,
|
|
14
|
+
longagentState = null
|
|
15
|
+
}) {
|
|
16
|
+
return renderStatusBar({
|
|
17
|
+
mode: state.mode,
|
|
18
|
+
model: state.model,
|
|
19
|
+
permission: configState.config.permission.mode || configState.config.permission.default_policy,
|
|
20
|
+
tokenMeter,
|
|
21
|
+
aggregation: configState.config.usage.aggregation,
|
|
22
|
+
cost,
|
|
23
|
+
savings: costSavings,
|
|
24
|
+
contextMeter,
|
|
25
|
+
showCost: configState.config.ui.status.show_cost,
|
|
26
|
+
showTokenMeter: configState.config.ui.status.show_token_meter,
|
|
27
|
+
theme,
|
|
28
|
+
layout: configState.config.ui.layout,
|
|
29
|
+
longagentState: state.mode === "longagent" ? longagentState : null,
|
|
30
|
+
memoryLoaded: state.memoryLoaded
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function renderRuntimeDashboardView({
|
|
35
|
+
theme,
|
|
36
|
+
state,
|
|
37
|
+
providers,
|
|
38
|
+
recentSessions,
|
|
39
|
+
mcpSummary,
|
|
40
|
+
skillSummary,
|
|
41
|
+
backgroundSummary,
|
|
42
|
+
runtimeSummary,
|
|
43
|
+
operatorSnapshot = null,
|
|
44
|
+
customCommandCount,
|
|
45
|
+
cwd,
|
|
46
|
+
columns = null
|
|
47
|
+
}) {
|
|
48
|
+
return [
|
|
49
|
+
renderReplDashboard({
|
|
50
|
+
theme,
|
|
51
|
+
state,
|
|
52
|
+
providers,
|
|
53
|
+
recentSessions,
|
|
54
|
+
mcpSummary,
|
|
55
|
+
skillSummary,
|
|
56
|
+
backgroundSummary,
|
|
57
|
+
customCommandCount,
|
|
58
|
+
cwd,
|
|
59
|
+
columns
|
|
60
|
+
}),
|
|
61
|
+
"",
|
|
62
|
+
formatRuntimeStateText(state, mcpSummary, skillSummary, backgroundSummary, runtimeSummary),
|
|
63
|
+
...(operatorSnapshot ? ["", ...renderOperatorPanel(operatorSnapshot)] : [])
|
|
64
|
+
].join("\n")
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function renderStartupScreen({ theme, recentSessions, columns = null }) {
|
|
68
|
+
const logo = renderReplLogo({ theme, columns })
|
|
69
|
+
const hint = renderStartupHint(recentSessions)
|
|
70
|
+
return hint ? `${logo}\n${hint}\n` : logo
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function renderFrameDashboardHeader({ showDashboard, theme, columns = null }) {
|
|
74
|
+
if (!showDashboard) return []
|
|
75
|
+
return renderReplLogo({ theme, columns }).split("\n")
|
|
76
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export function buildTranscriptViewport({
|
|
2
|
+
logs = [],
|
|
3
|
+
width,
|
|
4
|
+
logRows,
|
|
5
|
+
scrollOffset,
|
|
6
|
+
wrapLogLines,
|
|
7
|
+
clipAnsiLine,
|
|
8
|
+
paint,
|
|
9
|
+
theme
|
|
10
|
+
}) {
|
|
11
|
+
const wrappedAllLogs = wrapLogLines(logs, width)
|
|
12
|
+
const maxOffset = Math.max(0, wrappedAllLogs.length - logRows)
|
|
13
|
+
const clampedOffset = Math.max(0, Math.min(maxOffset, scrollOffset))
|
|
14
|
+
const end = Math.max(0, wrappedAllLogs.length - clampedOffset)
|
|
15
|
+
const start = Math.max(0, end - logRows)
|
|
16
|
+
const wrappedLogs = wrappedAllLogs.slice(start, end)
|
|
17
|
+
const scrollMeta = {
|
|
18
|
+
logRows,
|
|
19
|
+
totalRows: wrappedAllLogs.length,
|
|
20
|
+
maxOffset
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const scrollHint = clampedOffset > 0
|
|
24
|
+
? paint(` Ctrl+Up/Down scroll | +${clampedOffset} lines`, theme.semantic.warn)
|
|
25
|
+
: paint(" Ctrl+Up/Down scroll | Ctrl+Home oldest | Ctrl+End latest", theme.base.muted, { dim: true })
|
|
26
|
+
|
|
27
|
+
const totalLog = wrappedAllLogs.length
|
|
28
|
+
const showScrollbar = totalLog > logRows
|
|
29
|
+
let thumbStart = 0
|
|
30
|
+
let thumbEnd = 0
|
|
31
|
+
if (showScrollbar) {
|
|
32
|
+
thumbStart = Math.floor((start / totalLog) * logRows)
|
|
33
|
+
thumbEnd = Math.min(logRows, thumbStart + Math.max(1, Math.round((logRows / totalLog) * logRows)))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const lines = []
|
|
37
|
+
for (let i = 0; i < logRows; i++) {
|
|
38
|
+
const content = wrappedLogs[i] || ""
|
|
39
|
+
if (showScrollbar) {
|
|
40
|
+
const bar = i >= thumbStart && i < thumbEnd
|
|
41
|
+
? paint("┃", theme.semantic.warn)
|
|
42
|
+
: paint("│", theme.base.border, { dim: true })
|
|
43
|
+
lines.push(clipAnsiLine(content, width - 2) + " " + bar)
|
|
44
|
+
} else {
|
|
45
|
+
lines.push(clipAnsiLine(content, width))
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
lines,
|
|
51
|
+
scrollHint,
|
|
52
|
+
scrollMeta,
|
|
53
|
+
scrollOffset: clampedOffset,
|
|
54
|
+
wrappedLogs
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { paint } from "../theme/color.mjs"
|
|
2
|
+
import { renderBackgroundSummaryPanel } from "./repl-background-panel.mjs"
|
|
3
|
+
|
|
4
|
+
export function formatRuntimeStateText(state, mcpSummary = null, skillSummary = null, backgroundSummary = null, runtimeSummary = null) {
|
|
5
|
+
const lines = [
|
|
6
|
+
`session=${state.sessionId}`,
|
|
7
|
+
`mode=${state.mode}`,
|
|
8
|
+
`provider=${state.providerType}`,
|
|
9
|
+
`model=${state.model}`
|
|
10
|
+
]
|
|
11
|
+
if (state.mode === "longagent" && state.longagentImpl) {
|
|
12
|
+
lines.push(`longagent.impl=${state.longagentImpl}`)
|
|
13
|
+
}
|
|
14
|
+
if (mcpSummary) {
|
|
15
|
+
lines.push(`mcp=${mcpSummary.healthy}/${mcpSummary.configured} healthy, ${mcpSummary.tools} tools`)
|
|
16
|
+
if (mcpSummary.configured === 0) {
|
|
17
|
+
lines.push("mcp.quickstart=kkcode mcp init --project")
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
if (skillSummary) {
|
|
21
|
+
const mdCount = skillSummary.template + skillSummary.skillMd
|
|
22
|
+
lines.push(`skills=${skillSummary.total} loaded (md:${mdCount}, mcp:${skillSummary.mcpPrompt}, mjs:${skillSummary.programmable})`)
|
|
23
|
+
if (skillSummary.total === 0) {
|
|
24
|
+
lines.push("skills.quickstart=kkcode skill init --project")
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (backgroundSummary) {
|
|
28
|
+
lines.push(...renderBackgroundSummaryPanel(backgroundSummary))
|
|
29
|
+
}
|
|
30
|
+
if (runtimeSummary) {
|
|
31
|
+
lines.push(`session.messages=${runtimeSummary.messageCount}`)
|
|
32
|
+
lines.push(`session.parts=${runtimeSummary.partCount}`)
|
|
33
|
+
lines.push(`session.recoverable=${runtimeSummary.recoverableCount}`)
|
|
34
|
+
if (runtimeSummary.audit) {
|
|
35
|
+
lines.push(`audit.total=${runtimeSummary.audit.total} error=${runtimeSummary.audit.errorCount}`)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return lines.join("\n")
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function normalizeFileChanges(toolEvents = []) {
|
|
42
|
+
const rows = []
|
|
43
|
+
for (const event of toolEvents || []) {
|
|
44
|
+
if (!event || !["write", "edit"].includes(event.name)) continue
|
|
45
|
+
const changes = Array.isArray(event?.metadata?.fileChanges) ? event.metadata.fileChanges : []
|
|
46
|
+
for (const item of changes) {
|
|
47
|
+
const path = String(item?.path || event.args?.path || "").trim()
|
|
48
|
+
if (!path) continue
|
|
49
|
+
rows.push({
|
|
50
|
+
path,
|
|
51
|
+
addedLines: Number(item?.addedLines || 0),
|
|
52
|
+
removedLines: Number(item?.removedLines || 0),
|
|
53
|
+
stageId: item?.stageId ? String(item.stageId) : "",
|
|
54
|
+
taskId: item?.taskId ? String(item.taskId) : ""
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const grouped = new Map()
|
|
60
|
+
for (const row of rows) {
|
|
61
|
+
const key = `${row.path}::${row.stageId}::${row.taskId}`
|
|
62
|
+
const prev = grouped.get(key) || {
|
|
63
|
+
path: row.path,
|
|
64
|
+
addedLines: 0,
|
|
65
|
+
removedLines: 0,
|
|
66
|
+
stageId: row.stageId,
|
|
67
|
+
taskId: row.taskId
|
|
68
|
+
}
|
|
69
|
+
prev.addedLines += row.addedLines
|
|
70
|
+
prev.removedLines += row.removedLines
|
|
71
|
+
grouped.set(key, prev)
|
|
72
|
+
}
|
|
73
|
+
return [...grouped.values()]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function renderFileChangeLines(fileChanges = [], limit = 20) {
|
|
77
|
+
const lines = []
|
|
78
|
+
const rows = fileChanges.slice(0, limit)
|
|
79
|
+
for (const item of rows) {
|
|
80
|
+
const scope = [item.stageId, item.taskId].filter(Boolean).join("/")
|
|
81
|
+
const suffix = scope ? paint(` (${scope})`, null, { dim: true }) : ""
|
|
82
|
+
const add = item.addedLines > 0
|
|
83
|
+
? paint(`+${item.addedLines}`, "#00ff00", { bold: true })
|
|
84
|
+
: paint("+0", null, { dim: true })
|
|
85
|
+
const del = item.removedLines > 0
|
|
86
|
+
? paint(`-${item.removedLines}`, "#ff4444", { bold: true })
|
|
87
|
+
: paint("-0", null, { dim: true })
|
|
88
|
+
lines.push(` ${paint(item.path, "white")} ${add} ${del}${suffix}`)
|
|
89
|
+
}
|
|
90
|
+
if (fileChanges.length > rows.length) {
|
|
91
|
+
lines.push(paint(` ... +${fileChanges.length - rows.length} more file(s)`, null, { dim: true }))
|
|
92
|
+
}
|
|
93
|
+
return lines
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function normalizeDiagnostics(toolEvents = []) {
|
|
97
|
+
const summaries = []
|
|
98
|
+
for (const event of toolEvents || []) {
|
|
99
|
+
const diagnostics = event?.metadata?.diagnostics
|
|
100
|
+
if (!diagnostics) continue
|
|
101
|
+
const summary = diagnostics.summary || {}
|
|
102
|
+
const currentSummary = diagnostics.after?.summary || diagnostics.current?.summary || {}
|
|
103
|
+
const delta = diagnostics.delta || {}
|
|
104
|
+
summaries.push({
|
|
105
|
+
tool: String(event?.name || ""),
|
|
106
|
+
path: String(event?.args?.path || diagnostics.current?.diagnostics?.[0]?.file || diagnostics.baseline?.diagnostics?.[0]?.file || "").trim(),
|
|
107
|
+
introduced: Number(summary.introduced || delta.added?.length || 0),
|
|
108
|
+
persistent: Number(summary.persistent || delta.persisted?.length || 0),
|
|
109
|
+
resolved: Number(summary.resolved || delta.resolved?.length || 0),
|
|
110
|
+
unchanged: Boolean(summary.unchanged || delta.unchanged),
|
|
111
|
+
errorCount: Number(currentSummary.errorCount || currentSummary.errors || 0),
|
|
112
|
+
warningCount: Number(currentSummary.warningCount || currentSummary.warnings || 0),
|
|
113
|
+
status: String(summary.status || diagnostics.after?.status || diagnostics.current?.status || diagnostics.status || "")
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
return summaries
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function renderDiagnosticsLines(rows = [], limit = 10) {
|
|
120
|
+
const lines = []
|
|
121
|
+
const visible = rows.slice(0, limit)
|
|
122
|
+
for (const item of visible) {
|
|
123
|
+
const label = item.path || item.tool || "diagnostics"
|
|
124
|
+
const summary = item.unchanged
|
|
125
|
+
? "no change"
|
|
126
|
+
: `+${item.introduced} / ${item.persistent} / -${item.resolved}`
|
|
127
|
+
const totals = `${item.errorCount} error(s), ${item.warningCount} warning(s)`
|
|
128
|
+
const status = item.status ? paint(` ${item.status}`, null, { dim: true }) : ""
|
|
129
|
+
lines.push(` ${paint(label, "white")} ${paint(summary, "yellow", { bold: true })} ${paint(totals, null, { dim: true })}${status}`)
|
|
130
|
+
}
|
|
131
|
+
if (rows.length > visible.length) {
|
|
132
|
+
lines.push(paint(` ... +${rows.length - visible.length} more diagnostics result(s)`, null, { dim: true }))
|
|
133
|
+
}
|
|
134
|
+
return lines
|
|
135
|
+
}
|