kfc-code-cli 0.0.1-alpha.7 → 0.0.1-alpha.8
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/dist/main.mjs +263 -143
- package/package.json +1 -1
package/dist/main.mjs
CHANGED
|
@@ -95084,26 +95084,21 @@ function pinoToLogger(p) {
|
|
|
95084
95084
|
async function createKimiAgent() {
|
|
95085
95085
|
const workDir = process.cwd();
|
|
95086
95086
|
const version = getVersion();
|
|
95087
|
-
const { client, dispose
|
|
95087
|
+
const { client, dispose, maxContextSize, defaultModel, defaultThinking, defaultYolo, defaultPlanMode, availableModels, syncSessionRuntime } = await createDefaultSoulPlusWireClient({
|
|
95088
95088
|
workspaceDir: workDir,
|
|
95089
95089
|
userAgent: `KimiCLI/${version}`,
|
|
95090
95090
|
defaultHeaders: buildKimiDefaultHeaders(version),
|
|
95091
95091
|
logger: pinoToLogger(getLogger()).child({ component: "session" })
|
|
95092
95092
|
});
|
|
95093
95093
|
client.onRequest("hook.request", async () => ({ ok: true }));
|
|
95094
|
-
const dispose = async () => {
|
|
95095
|
-
await disposeClient();
|
|
95096
|
-
};
|
|
95097
95094
|
return {
|
|
95098
95095
|
client,
|
|
95099
|
-
model:
|
|
95096
|
+
model: defaultModel,
|
|
95100
95097
|
defaultThinking,
|
|
95101
95098
|
defaultModes: {
|
|
95102
95099
|
yolo: defaultYolo,
|
|
95103
95100
|
planMode: defaultPlanMode
|
|
95104
95101
|
},
|
|
95105
|
-
theme,
|
|
95106
|
-
defaultEditor,
|
|
95107
95102
|
availableModels,
|
|
95108
95103
|
maxContextSize,
|
|
95109
95104
|
syncSessionRuntime,
|
|
@@ -95254,6 +95249,40 @@ function shellQuote(path) {
|
|
|
95254
95249
|
return `'${path.replace(/'/g, "'\\''")}'`;
|
|
95255
95250
|
}
|
|
95256
95251
|
//#endregion
|
|
95252
|
+
//#region src/tui/components/media/image-thumbnail.ts
|
|
95253
|
+
/**
|
|
95254
|
+
* Transcript-side rendering of a pasted image.
|
|
95255
|
+
*
|
|
95256
|
+
* On terminals that speak the Kitty graphics protocol or iTerm2 inline
|
|
95257
|
+
* image protocol (detected by pi-tui's `getCapabilities()`), we show
|
|
95258
|
+
* the actual image. Everywhere else we fall back to a one-line text
|
|
95259
|
+
* marker matching the placeholder the user sees in the input box —
|
|
95260
|
+
* this keeps the transcript readable on Terminal.app / Linux default
|
|
95261
|
+
* terminals / `script` recordings without extra chrome.
|
|
95262
|
+
*
|
|
95263
|
+
* Height is capped at ~12 rows so a single screenshot can't monopolize
|
|
95264
|
+
* the viewport; pi-tui handles proportional scaling internally.
|
|
95265
|
+
*/
|
|
95266
|
+
const MAX_IMAGE_ROWS = 12;
|
|
95267
|
+
var ImageThumbnail = class extends Container {
|
|
95268
|
+
constructor(attachment, colors) {
|
|
95269
|
+
super();
|
|
95270
|
+
const caps = getCapabilities();
|
|
95271
|
+
if (!(caps.images === "kitty" || caps.images === "iterm2")) {
|
|
95272
|
+
this.addChild(new Text(chalk.dim.cyan(` ${attachment.placeholder}`), 0, 0));
|
|
95273
|
+
return;
|
|
95274
|
+
}
|
|
95275
|
+
const image = new Image(Buffer.from(attachment.bytes).toString("base64"), attachment.mime, { fallbackColor: (s) => chalk.hex(colors.textDim)(s) }, {
|
|
95276
|
+
maxHeightCells: MAX_IMAGE_ROWS,
|
|
95277
|
+
filename: attachment.placeholder
|
|
95278
|
+
}, {
|
|
95279
|
+
widthPx: attachment.width,
|
|
95280
|
+
heightPx: attachment.height
|
|
95281
|
+
});
|
|
95282
|
+
this.addChild(image);
|
|
95283
|
+
}
|
|
95284
|
+
};
|
|
95285
|
+
//#endregion
|
|
95257
95286
|
//#region src/tui/components/messages/assistant-message.ts
|
|
95258
95287
|
const BULLET$1 = "⏺ ";
|
|
95259
95288
|
const INDENT$1 = " ";
|
|
@@ -95293,44 +95322,41 @@ var AssistantMessageComponent = class {
|
|
|
95293
95322
|
}
|
|
95294
95323
|
};
|
|
95295
95324
|
//#endregion
|
|
95296
|
-
//#region src/tui/components/
|
|
95325
|
+
//#region src/tui/components/messages/skill-activation.ts
|
|
95297
95326
|
/**
|
|
95298
|
-
*
|
|
95327
|
+
* Skill activation card.
|
|
95299
95328
|
*
|
|
95300
|
-
*
|
|
95301
|
-
*
|
|
95302
|
-
* the actual image. Everywhere else we fall back to a one-line text
|
|
95303
|
-
* marker matching the placeholder the user sees in the input box —
|
|
95304
|
-
* this keeps the transcript readable on Terminal.app / Linux default
|
|
95305
|
-
* terminals / `script` recordings without extra chrome.
|
|
95329
|
+
* 用户跑 `/skill:foo bar` 时不再把 SKILL.md 正文铺在 user 气泡里 ——
|
|
95330
|
+
* 只显示一张紧凑卡片:
|
|
95306
95331
|
*
|
|
95307
|
-
*
|
|
95308
|
-
*
|
|
95332
|
+
* ▶ Activated skill: foo
|
|
95333
|
+
* bar
|
|
95334
|
+
*
|
|
95335
|
+
* 第二行(args)可省。skill 正文已经经由 `client.prompt` 进了 LLM
|
|
95336
|
+
* 的上下文,用户视角无需再看到。
|
|
95337
|
+
*
|
|
95338
|
+
* Resume 重放仍会通过磁盘上的 user_message WAL 把 skill 正文当 user
|
|
95339
|
+
* 消息渲染(核心层未引入 skill_activated WAL 类型)—— 视为已知限制。
|
|
95309
95340
|
*/
|
|
95310
|
-
const
|
|
95311
|
-
var
|
|
95312
|
-
constructor(
|
|
95341
|
+
const ARGS_PREVIEW_MAX = 200;
|
|
95342
|
+
var SkillActivationComponent = class extends Container {
|
|
95343
|
+
constructor(name, args, colors) {
|
|
95313
95344
|
super();
|
|
95314
|
-
|
|
95315
|
-
|
|
95316
|
-
|
|
95317
|
-
|
|
95345
|
+
this.addChild(new Spacer(1));
|
|
95346
|
+
const head = chalk.hex(colors.primary).bold("▶ Activated skill: ") + chalk.hex(colors.user).bold(name);
|
|
95347
|
+
this.addChild(new Text(head, 0, 0));
|
|
95348
|
+
const trimmed = args?.trim() ?? "";
|
|
95349
|
+
if (trimmed.length > 0) {
|
|
95350
|
+
const preview = trimmed.length > ARGS_PREVIEW_MAX ? trimmed.slice(0, ARGS_PREVIEW_MAX) + "…" : trimmed;
|
|
95351
|
+
this.addChild(new Text(" " + chalk.hex(colors.textDim)(preview), 0, 0));
|
|
95318
95352
|
}
|
|
95319
|
-
const image = new Image(Buffer.from(attachment.bytes).toString("base64"), attachment.mime, { fallbackColor: (s) => chalk.hex(colors.textDim)(s) }, {
|
|
95320
|
-
maxHeightCells: MAX_IMAGE_ROWS,
|
|
95321
|
-
filename: attachment.placeholder
|
|
95322
|
-
}, {
|
|
95323
|
-
widthPx: attachment.width,
|
|
95324
|
-
heightPx: attachment.height
|
|
95325
|
-
});
|
|
95326
|
-
this.addChild(image);
|
|
95327
95353
|
}
|
|
95328
95354
|
};
|
|
95329
95355
|
//#endregion
|
|
95330
95356
|
//#region src/tui/components/messages/thinking.ts
|
|
95331
95357
|
const BULLET = "⏺ ";
|
|
95332
95358
|
const INDENT = " ";
|
|
95333
|
-
const PREVIEW_LINES$1 =
|
|
95359
|
+
const PREVIEW_LINES$1 = 3;
|
|
95334
95360
|
const SPINNER_FRAMES = [
|
|
95335
95361
|
"⠋",
|
|
95336
95362
|
"⠙",
|
|
@@ -95384,7 +95410,7 @@ var ThinkingComponent = class {
|
|
|
95384
95410
|
const visibleLines = contentLines.length > PREVIEW_LINES$1 ? contentLines.slice(contentLines.length - PREVIEW_LINES$1) : contentLines;
|
|
95385
95411
|
return [
|
|
95386
95412
|
"",
|
|
95387
|
-
|
|
95413
|
+
chalk.hex(this.color)(`${SPINNER_FRAMES[this.spinnerFrame] ?? SPINNER_FRAMES[0]} `) + chalk.hex(this.color)("thinking..."),
|
|
95388
95414
|
...visibleLines.map((line) => INDENT + line)
|
|
95389
95415
|
];
|
|
95390
95416
|
}
|
|
@@ -95586,7 +95612,7 @@ function extractApprovedPlan(output) {
|
|
|
95586
95612
|
}
|
|
95587
95613
|
const STREAMING_FIELD_RE$1 = /"(path|file_path|command|pattern|query|url|description|title|name)"\s*:\s*"((?:\\.|[^"\\])*)"/g;
|
|
95588
95614
|
function unescapeJsonString$1(s) {
|
|
95589
|
-
return s.
|
|
95615
|
+
return s.replaceAll(/\\(["\\/bfnrt])/g, (_, ch) => {
|
|
95590
95616
|
switch (ch) {
|
|
95591
95617
|
case "n": return "\n";
|
|
95592
95618
|
case "t": return " ";
|
|
@@ -95648,7 +95674,7 @@ function extractPartialStringField(text, key) {
|
|
|
95648
95674
|
const hex = text.slice(i + 2, i + 6);
|
|
95649
95675
|
const code = Number.parseInt(hex, 16);
|
|
95650
95676
|
if (Number.isNaN(code)) return out;
|
|
95651
|
-
out += String.
|
|
95677
|
+
out += String.fromCodePoint(code);
|
|
95652
95678
|
i += 6;
|
|
95653
95679
|
continue;
|
|
95654
95680
|
}
|
|
@@ -96101,10 +96127,11 @@ var WelcomeComponent = class {
|
|
|
96101
96127
|
"",
|
|
96102
96128
|
...infoLines
|
|
96103
96129
|
];
|
|
96104
|
-
const lines = [
|
|
96105
|
-
|
|
96106
|
-
|
|
96107
|
-
|
|
96130
|
+
const lines = [
|
|
96131
|
+
"",
|
|
96132
|
+
primary("╭" + "─".repeat(width - 2) + "╮"),
|
|
96133
|
+
primary("│") + " ".repeat(width - 2) + primary("│")
|
|
96134
|
+
];
|
|
96108
96135
|
for (const content of contentLines) {
|
|
96109
96136
|
const truncated = truncateToWidth(content, innerWidth, "…");
|
|
96110
96137
|
const vis = visibleWidth(truncated);
|
|
@@ -96250,6 +96277,7 @@ function createTranscriptComponent(state, entry) {
|
|
|
96250
96277
|
}
|
|
96251
96278
|
return msg;
|
|
96252
96279
|
}
|
|
96280
|
+
case "skill_activation": return new SkillActivationComponent(entry.skillName ?? entry.content, entry.skillArgs, state.colors);
|
|
96253
96281
|
case "assistant": return createAssistantEntry(state, entry.content);
|
|
96254
96282
|
case "thinking": return new ThinkingComponent(entry.content, state.colors, true);
|
|
96255
96283
|
case "tool_call":
|
|
@@ -96392,7 +96420,7 @@ function buildImagePart(att) {
|
|
|
96392
96420
|
}
|
|
96393
96421
|
//#endregion
|
|
96394
96422
|
//#region src/tui/theme/pi-tui-theme.ts
|
|
96395
|
-
const HEADING_HASH_PREFIX = /^((?:\
|
|
96423
|
+
const HEADING_HASH_PREFIX = /^((?:\x1B\[[0-9;]*m)*)#{1,6}[ \t]+/;
|
|
96396
96424
|
function createMarkdownTheme(colors) {
|
|
96397
96425
|
const stripHash = (text) => text.replace(HEADING_HASH_PREFIX, "$1");
|
|
96398
96426
|
return {
|
|
@@ -96638,7 +96666,7 @@ function fetchSnapshot(gitRoot) {
|
|
|
96638
96666
|
const seen = /* @__PURE__ */ new Set();
|
|
96639
96667
|
for (const path of tracked) seen.add(path);
|
|
96640
96668
|
for (const path of untracked) seen.add(path);
|
|
96641
|
-
const merged = [...seen].
|
|
96669
|
+
const merged = [...seen].toSorted();
|
|
96642
96670
|
const files = merged.length > MAX_ENTRIES ? merged.slice(0, MAX_ENTRIES) : merged;
|
|
96643
96671
|
return {
|
|
96644
96672
|
files,
|
|
@@ -96794,6 +96822,7 @@ function createAuthCommands(deps = {}) {
|
|
|
96794
96822
|
aliases: [],
|
|
96795
96823
|
description: "Clear OAuth credentials",
|
|
96796
96824
|
mode: "both",
|
|
96825
|
+
priority: 40,
|
|
96797
96826
|
async execute(_args, _ctx) {
|
|
96798
96827
|
if (deps.client === void 0) return ok$2("OAuth is unavailable: no wire client is attached.");
|
|
96799
96828
|
if (!(await deps.client.authStatus(providerName)).providers.some((provider) => provider.provider_name === providerName && provider.has_token)) return ok$2("Not logged in.");
|
|
@@ -96805,6 +96834,7 @@ function createAuthCommands(deps = {}) {
|
|
|
96805
96834
|
aliases: [],
|
|
96806
96835
|
description: "Start OAuth device code login flow",
|
|
96807
96836
|
mode: "both",
|
|
96837
|
+
priority: 40,
|
|
96808
96838
|
async execute(_args, ctx) {
|
|
96809
96839
|
if (deps.client === void 0) return ok$2("OAuth is unavailable: no wire client is attached.");
|
|
96810
96840
|
const disposeDeviceCodeHandler = deps.client.onRequest("auth.device_code", (req) => {
|
|
@@ -96853,6 +96883,7 @@ const shellCommands = [
|
|
|
96853
96883
|
aliases: ["quit", "q"],
|
|
96854
96884
|
description: "Exit the application",
|
|
96855
96885
|
mode: "both",
|
|
96886
|
+
priority: 20,
|
|
96856
96887
|
async execute() {
|
|
96857
96888
|
return { type: "exit" };
|
|
96858
96889
|
}
|
|
@@ -96862,6 +96893,7 @@ const shellCommands = [
|
|
|
96862
96893
|
aliases: ["h", "?"],
|
|
96863
96894
|
description: "Show available commands and shortcuts",
|
|
96864
96895
|
mode: "both",
|
|
96896
|
+
priority: 80,
|
|
96865
96897
|
async execute(_args, _ctx) {
|
|
96866
96898
|
return ok$1("__show_help__");
|
|
96867
96899
|
}
|
|
@@ -96871,6 +96903,7 @@ const shellCommands = [
|
|
|
96871
96903
|
aliases: [],
|
|
96872
96904
|
description: "Show version information",
|
|
96873
96905
|
mode: "both",
|
|
96906
|
+
priority: 20,
|
|
96874
96907
|
async execute(_args, ctx) {
|
|
96875
96908
|
return ok$1(`Kimi Code v${ctx.appState.version}`);
|
|
96876
96909
|
}
|
|
@@ -96880,6 +96913,7 @@ const shellCommands = [
|
|
|
96880
96913
|
aliases: [],
|
|
96881
96914
|
description: "Start a fresh session in the current workspace",
|
|
96882
96915
|
mode: "both",
|
|
96916
|
+
priority: 80,
|
|
96883
96917
|
async execute() {
|
|
96884
96918
|
return {
|
|
96885
96919
|
type: "reload",
|
|
@@ -96892,6 +96926,7 @@ const shellCommands = [
|
|
|
96892
96926
|
aliases: ["resume"],
|
|
96893
96927
|
description: "Browse and resume sessions",
|
|
96894
96928
|
mode: "both",
|
|
96929
|
+
priority: 80,
|
|
96895
96930
|
async execute(_args, _ctx) {
|
|
96896
96931
|
return ok$1("__show_sessions__");
|
|
96897
96932
|
}
|
|
@@ -96901,6 +96936,7 @@ const shellCommands = [
|
|
|
96901
96936
|
aliases: ["rename"],
|
|
96902
96937
|
description: "Set or show session title",
|
|
96903
96938
|
mode: "both",
|
|
96939
|
+
priority: 60,
|
|
96904
96940
|
async execute(args, ctx) {
|
|
96905
96941
|
const trimmed = args.trim();
|
|
96906
96942
|
if (trimmed.length === 0) {
|
|
@@ -96921,6 +96957,7 @@ const shellCommands = [
|
|
|
96921
96957
|
aliases: ["yes"],
|
|
96922
96958
|
description: "Toggle auto-approve mode",
|
|
96923
96959
|
mode: "both",
|
|
96960
|
+
priority: 100,
|
|
96924
96961
|
async execute(args, ctx) {
|
|
96925
96962
|
let enabled;
|
|
96926
96963
|
if (args === "on") enabled = true;
|
|
@@ -96937,6 +96974,7 @@ const shellCommands = [
|
|
|
96937
96974
|
aliases: [],
|
|
96938
96975
|
description: "Toggle plan mode",
|
|
96939
96976
|
mode: "both",
|
|
96977
|
+
priority: 100,
|
|
96940
96978
|
async execute(args, ctx) {
|
|
96941
96979
|
const subcmd = args.trim().toLowerCase();
|
|
96942
96980
|
if (subcmd === "view") {
|
|
@@ -96964,6 +97002,7 @@ const shellCommands = [
|
|
|
96964
97002
|
aliases: [],
|
|
96965
97003
|
description: "Switch LLM model",
|
|
96966
97004
|
mode: "both",
|
|
97005
|
+
priority: 100,
|
|
96967
97006
|
async execute(args, ctx) {
|
|
96968
97007
|
const trimmed = args.trim();
|
|
96969
97008
|
if (trimmed.length === 0) return ok$1("__show_model_picker__");
|
|
@@ -96976,6 +97015,7 @@ const shellCommands = [
|
|
|
96976
97015
|
aliases: ["status"],
|
|
96977
97016
|
description: "Show session tokens + context window + plan quotas",
|
|
96978
97017
|
mode: "both",
|
|
97018
|
+
priority: 60,
|
|
96979
97019
|
async execute() {
|
|
96980
97020
|
return ok$1("__show_usage__");
|
|
96981
97021
|
}
|
|
@@ -96995,6 +97035,7 @@ const soulCommands = [{
|
|
|
96995
97035
|
aliases: [],
|
|
96996
97036
|
description: "Compact the conversation context",
|
|
96997
97037
|
mode: "both",
|
|
97038
|
+
priority: 80,
|
|
96998
97039
|
async execute(args, ctx) {
|
|
96999
97040
|
const customInstruction = args.trim() || void 0;
|
|
97000
97041
|
await ctx.client.compact(ctx.appState.sessionId, customInstruction);
|
|
@@ -97077,11 +97118,17 @@ var SlashCommandRegistry = class {
|
|
|
97077
97118
|
}
|
|
97078
97119
|
return [...bestScores.values()].toSorted((a, b) => b.score - a.score || a.def.name.localeCompare(b.def.name)).map(({ def }) => def);
|
|
97079
97120
|
}
|
|
97080
|
-
/**
|
|
97121
|
+
/**
|
|
97122
|
+
* List all registered commands, optionally filtered by mode.
|
|
97123
|
+
* Built-in commands come first, sorted alphabetically; skill commands
|
|
97124
|
+
* follow in their original registration order.
|
|
97125
|
+
*/
|
|
97081
97126
|
listAll(mode) {
|
|
97082
97127
|
const all = [...this.commands.values()];
|
|
97083
|
-
|
|
97084
|
-
|
|
97128
|
+
const filtered = mode === void 0 ? all : all.filter((def) => def.mode === mode || def.mode === "both");
|
|
97129
|
+
const builtins = filtered.filter((def) => !def.name.startsWith(SKILL_COMMAND_PREFIX)).toSorted((a, b) => (b.priority ?? 0) - (a.priority ?? 0) || a.name.localeCompare(b.name));
|
|
97130
|
+
const skills = filtered.filter((def) => def.name.startsWith(SKILL_COMMAND_PREFIX));
|
|
97131
|
+
return [...builtins, ...skills];
|
|
97085
97132
|
}
|
|
97086
97133
|
/** Get the number of registered commands. */
|
|
97087
97134
|
get size() {
|
|
@@ -97652,9 +97699,7 @@ var TodoPanelComponent = class {
|
|
|
97652
97699
|
render(width) {
|
|
97653
97700
|
if (this.todos.length === 0) return [];
|
|
97654
97701
|
const c = this.colors;
|
|
97655
|
-
const lines = [];
|
|
97656
|
-
lines.push(chalk.hex(c.border)("─".repeat(width)));
|
|
97657
|
-
lines.push(chalk.hex(c.primary).bold(" Todo"));
|
|
97702
|
+
const lines = [chalk.hex(c.border)("─".repeat(width)), chalk.hex(c.primary).bold(" Todo")];
|
|
97658
97703
|
for (const todo of this.todos) lines.push(renderRow(todo, c));
|
|
97659
97704
|
return lines.map((line) => truncateToWidth(line, width));
|
|
97660
97705
|
}
|
|
@@ -98024,7 +98069,7 @@ function enqueueMessage(state, text) {
|
|
|
98024
98069
|
}
|
|
98025
98070
|
function recallLastQueued(state) {
|
|
98026
98071
|
if (state.queuedMessages.length === 0) return void 0;
|
|
98027
|
-
const last = state.queuedMessages
|
|
98072
|
+
const last = state.queuedMessages.at(-1);
|
|
98028
98073
|
state.queuedMessages = state.queuedMessages.slice(0, -1);
|
|
98029
98074
|
return last.text;
|
|
98030
98075
|
}
|
|
@@ -98090,6 +98135,66 @@ function sendMessageInternal(state, addEntry, input, options) {
|
|
|
98090
98135
|
});
|
|
98091
98136
|
});
|
|
98092
98137
|
}
|
|
98138
|
+
/**
|
|
98139
|
+
* Activate a skill: render a compact activation card (no skill body in
|
|
98140
|
+
* the transcript) and forward the prepared prompt — `skill.content +
|
|
98141
|
+
* "\n\nUser request:\n" + args` — to core via `client.prompt`. Keeps
|
|
98142
|
+
* the skill body out of the user-visible transcript while still feeding
|
|
98143
|
+
* it into the LLM exactly as the legacy path did.
|
|
98144
|
+
*/
|
|
98145
|
+
function sendSkillActivation(state, addEntry, skillName, skillArgs, fullPrompt) {
|
|
98146
|
+
addEntry({
|
|
98147
|
+
id: nextTranscriptId(),
|
|
98148
|
+
kind: "skill_activation",
|
|
98149
|
+
turnId: void 0,
|
|
98150
|
+
renderMode: "plain",
|
|
98151
|
+
content: `Activated skill: ${skillName}`,
|
|
98152
|
+
skillName,
|
|
98153
|
+
skillArgs
|
|
98154
|
+
});
|
|
98155
|
+
state.currentTurnId = void 0;
|
|
98156
|
+
state.assistantDraft = "";
|
|
98157
|
+
state.assistantStreamActive = false;
|
|
98158
|
+
state.thinkingDraft = "";
|
|
98159
|
+
disposeActiveThinkingComponent(state);
|
|
98160
|
+
state.activeToolCalls.clear();
|
|
98161
|
+
state.streamingToolCallArguments.clear();
|
|
98162
|
+
state.livePane = {
|
|
98163
|
+
...state.livePane,
|
|
98164
|
+
mode: "waiting",
|
|
98165
|
+
thinkingText: "",
|
|
98166
|
+
assistantText: "",
|
|
98167
|
+
pendingToolCall: null,
|
|
98168
|
+
pendingApproval: null,
|
|
98169
|
+
pendingQuestion: null
|
|
98170
|
+
};
|
|
98171
|
+
state.appState.isStreaming = true;
|
|
98172
|
+
state.appState.streamingPhase = "waiting";
|
|
98173
|
+
state.appState.streamingStartTime = Date.now();
|
|
98174
|
+
state.footer.setState(state.appState);
|
|
98175
|
+
state.ui.requestRender();
|
|
98176
|
+
state.client.prompt(state.appState.sessionId, { input: fullPrompt }).catch((error) => {
|
|
98177
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
98178
|
+
state.appState.isStreaming = false;
|
|
98179
|
+
state.appState.streamingPhase = "idle";
|
|
98180
|
+
state.livePane = {
|
|
98181
|
+
mode: "idle",
|
|
98182
|
+
thinkingText: "",
|
|
98183
|
+
assistantText: "",
|
|
98184
|
+
pendingToolCall: null,
|
|
98185
|
+
pendingApproval: null,
|
|
98186
|
+
pendingQuestion: null
|
|
98187
|
+
};
|
|
98188
|
+
state.footer.setState(state.appState);
|
|
98189
|
+
addEntry({
|
|
98190
|
+
id: nextTranscriptId(),
|
|
98191
|
+
kind: "status",
|
|
98192
|
+
renderMode: "plain",
|
|
98193
|
+
content: `Skill "${skillName}" failed: ${message}`,
|
|
98194
|
+
color: state.colors.error
|
|
98195
|
+
});
|
|
98196
|
+
});
|
|
98197
|
+
}
|
|
98093
98198
|
/** Send from user input: enqueue if busy, otherwise send immediately. */
|
|
98094
98199
|
function sendMessage(state, addEntry, input, options) {
|
|
98095
98200
|
if (state.appState.isStreaming || state.appState.isCompacting) {
|
|
@@ -98789,36 +98894,71 @@ function endCompaction(state, tokensBefore, tokensAfter) {
|
|
|
98789
98894
|
state.ui.requestRender();
|
|
98790
98895
|
}
|
|
98791
98896
|
//#endregion
|
|
98897
|
+
//#region src/tui/commands/skill-commands.ts
|
|
98898
|
+
const SKILL_ACTIVATION_SENTINEL_PREFIX = "__activate_skill__:";
|
|
98899
|
+
async function fetchSkills(client, sessionId) {
|
|
98900
|
+
const skills = (await client.listSkills(sessionId))?.skills ?? [];
|
|
98901
|
+
const out = [];
|
|
98902
|
+
for (const s of skills) {
|
|
98903
|
+
if (typeof s?.name !== "string" || s.name.length === 0) continue;
|
|
98904
|
+
if (typeof s.content !== "string") continue;
|
|
98905
|
+
out.push({
|
|
98906
|
+
name: s.name,
|
|
98907
|
+
content: s.content,
|
|
98908
|
+
...typeof s.description === "string" ? { description: s.description } : {}
|
|
98909
|
+
});
|
|
98910
|
+
}
|
|
98911
|
+
return out;
|
|
98912
|
+
}
|
|
98913
|
+
function buildSkillCommand(skill) {
|
|
98914
|
+
return {
|
|
98915
|
+
name: `${SKILL_COMMAND_PREFIX}${skill.name}`,
|
|
98916
|
+
aliases: [],
|
|
98917
|
+
description: skill.description ?? "",
|
|
98918
|
+
mode: "both",
|
|
98919
|
+
async execute(args) {
|
|
98920
|
+
const trimmed = args.trim();
|
|
98921
|
+
const prompt = trimmed.length > 0 ? `${skill.content}\n\nUser request:\n${trimmed}` : skill.content;
|
|
98922
|
+
const payload = {
|
|
98923
|
+
name: skill.name,
|
|
98924
|
+
args: trimmed,
|
|
98925
|
+
prompt
|
|
98926
|
+
};
|
|
98927
|
+
return {
|
|
98928
|
+
type: "ok",
|
|
98929
|
+
message: `${SKILL_ACTIVATION_SENTINEL_PREFIX}${JSON.stringify(payload)}`
|
|
98930
|
+
};
|
|
98931
|
+
}
|
|
98932
|
+
};
|
|
98933
|
+
}
|
|
98934
|
+
//#endregion
|
|
98792
98935
|
//#region src/tui/commands/skill-dispatch.ts
|
|
98793
98936
|
/**
|
|
98794
|
-
*
|
|
98937
|
+
* 探测 `/name args` 是否对应某个 skill。命中返回 activate(含拼好的
|
|
98938
|
+
* prompt);未命中或查询失败返回 unknown,message 由调用方推到 transcript。
|
|
98795
98939
|
*/
|
|
98796
98940
|
async function tryDispatchSkill(client, sessionId, name, args) {
|
|
98797
98941
|
let skills;
|
|
98798
98942
|
try {
|
|
98799
98943
|
skills = (await client.listSkills(sessionId))?.skills ?? [];
|
|
98800
|
-
} catch (
|
|
98944
|
+
} catch (error) {
|
|
98801
98945
|
return {
|
|
98802
|
-
|
|
98803
|
-
message: `Unknown command: /${name} (skill lookup failed: ${
|
|
98946
|
+
kind: "unknown",
|
|
98947
|
+
message: `Unknown command: /${name} (skill lookup failed: ${error instanceof Error ? error.message : String(error)})`
|
|
98804
98948
|
};
|
|
98805
98949
|
}
|
|
98806
|
-
|
|
98807
|
-
|
|
98950
|
+
const skill = skills.find((s) => s.name === name);
|
|
98951
|
+
if (skill === void 0 || typeof skill.content !== "string") return {
|
|
98952
|
+
kind: "unknown",
|
|
98808
98953
|
message: `Unknown command: /${name}`
|
|
98809
98954
|
};
|
|
98810
|
-
|
|
98811
|
-
|
|
98812
|
-
|
|
98813
|
-
|
|
98814
|
-
|
|
98815
|
-
}
|
|
98816
|
-
}
|
|
98817
|
-
return {
|
|
98818
|
-
matched: true,
|
|
98819
|
-
message: `Skill "${name}" failed: ${err instanceof Error ? err.message : String(err)}`
|
|
98820
|
-
};
|
|
98821
|
-
}
|
|
98955
|
+
const trimmed = args.trim();
|
|
98956
|
+
return {
|
|
98957
|
+
kind: "activate",
|
|
98958
|
+
name,
|
|
98959
|
+
args: trimmed,
|
|
98960
|
+
prompt: trimmed.length > 0 ? `${skill.content}\n\nUser request:\n${trimmed}` : skill.content
|
|
98961
|
+
};
|
|
98822
98962
|
}
|
|
98823
98963
|
//#endregion
|
|
98824
98964
|
//#region src/tui/commands/dispatch.ts
|
|
@@ -98827,7 +98967,12 @@ async function dispatchSlashCommand(input, state, buildCtx, addEntry) {
|
|
|
98827
98967
|
if (!parsed) return false;
|
|
98828
98968
|
const def = state.registry.find(parsed.name);
|
|
98829
98969
|
if (!def) {
|
|
98970
|
+
const ctx = buildCtx();
|
|
98830
98971
|
const result = await tryDispatchSkill(state.client, state.appState.sessionId, parsed.name, parsed.args);
|
|
98972
|
+
if (result.kind === "activate") {
|
|
98973
|
+
ctx.activateSkill(result.name, result.args, result.prompt);
|
|
98974
|
+
return true;
|
|
98975
|
+
}
|
|
98831
98976
|
addEntry({
|
|
98832
98977
|
id: `slash-${Date.now()}`,
|
|
98833
98978
|
kind: "status",
|
|
@@ -98895,6 +99040,24 @@ async function dispatchSlashCommand(input, state, buildCtx, addEntry) {
|
|
|
98895
99040
|
ctx.sendAsMessage(msg);
|
|
98896
99041
|
return true;
|
|
98897
99042
|
}
|
|
99043
|
+
if (result.message.startsWith("__activate_skill__:")) {
|
|
99044
|
+
const raw = result.message.slice(19);
|
|
99045
|
+
let payload;
|
|
99046
|
+
try {
|
|
99047
|
+
payload = JSON.parse(raw);
|
|
99048
|
+
} catch (error) {
|
|
99049
|
+
addEntry({
|
|
99050
|
+
id: `slash-err-${Date.now()}`,
|
|
99051
|
+
kind: "status",
|
|
99052
|
+
renderMode: "plain",
|
|
99053
|
+
content: `Skill activation failed: malformed payload (${error instanceof Error ? error.message : String(error)})`,
|
|
99054
|
+
color: state.colors.error
|
|
99055
|
+
});
|
|
99056
|
+
return true;
|
|
99057
|
+
}
|
|
99058
|
+
ctx.activateSkill(payload.name, payload.args, payload.prompt);
|
|
99059
|
+
return true;
|
|
99060
|
+
}
|
|
98898
99061
|
addEntry({
|
|
98899
99062
|
id: `slash-${Date.now()}`,
|
|
98900
99063
|
kind: "status",
|
|
@@ -98986,7 +99149,7 @@ function handleContentDelta(ectx, data) {
|
|
|
98986
99149
|
//#region src/tui/handlers/tool.ts
|
|
98987
99150
|
const STREAMING_FIELD_RE = /"(path|file_path|command|pattern|query|url|description|title|name)"\s*:\s*"((?:\\.|[^"\\])*)"/g;
|
|
98988
99151
|
function unescapeJsonString(s) {
|
|
98989
|
-
return s.
|
|
99152
|
+
return s.replaceAll(/\\(["\\/bfnrt])/g, (_, ch) => {
|
|
98990
99153
|
switch (ch) {
|
|
98991
99154
|
case "n": return "\n";
|
|
98992
99155
|
case "t": return " ";
|
|
@@ -99269,7 +99432,7 @@ var ChoicePickerComponent = class extends Container {
|
|
|
99269
99432
|
super();
|
|
99270
99433
|
this.opts = opts;
|
|
99271
99434
|
const currentIdx = opts.options.findIndex((o) => o.value === opts.currentValue);
|
|
99272
|
-
this.selectedIndex = currentIdx
|
|
99435
|
+
this.selectedIndex = Math.max(currentIdx, 0);
|
|
99273
99436
|
}
|
|
99274
99437
|
handleInput(data) {
|
|
99275
99438
|
if (matchesKey(data, Key.escape)) {
|
|
@@ -99292,12 +99455,13 @@ var ChoicePickerComponent = class extends Container {
|
|
|
99292
99455
|
}
|
|
99293
99456
|
render(width) {
|
|
99294
99457
|
const { colors } = this.opts;
|
|
99295
|
-
const lines = [];
|
|
99296
|
-
lines.push(chalk.hex(colors.primary)("─".repeat(width)));
|
|
99297
|
-
lines.push(chalk.hex(colors.primary).bold(` ${this.opts.title}`));
|
|
99298
99458
|
const hint = this.opts.hint ?? "↑↓ navigate · Enter select · Esc cancel";
|
|
99299
|
-
lines
|
|
99300
|
-
|
|
99459
|
+
const lines = [
|
|
99460
|
+
chalk.hex(colors.primary)("─".repeat(width)),
|
|
99461
|
+
chalk.hex(colors.primary).bold(` ${this.opts.title}`),
|
|
99462
|
+
chalk.hex(colors.textMuted)(` ${hint}`),
|
|
99463
|
+
""
|
|
99464
|
+
];
|
|
99301
99465
|
for (let i = 0; i < this.opts.options.length; i++) {
|
|
99302
99466
|
const opt = this.opts.options[i];
|
|
99303
99467
|
const isSelected = i === this.selectedIndex;
|
|
@@ -99455,31 +99619,28 @@ var HelpPanelComponent = class extends Container {
|
|
|
99455
99619
|
const muted = chalk.hex(c.textMuted);
|
|
99456
99620
|
const kbdColor = chalk.hex(c.warning);
|
|
99457
99621
|
const slashColor = chalk.hex(c.primary);
|
|
99458
|
-
const lines = [];
|
|
99459
|
-
lines.push(accent("─".repeat(width)));
|
|
99460
|
-
lines.push(accent.bold(" help ") + muted("· Esc / Enter / q to close · ↑↓ scroll"));
|
|
99461
|
-
lines.push("");
|
|
99462
|
-
lines.push(` ${dim("Sure, Kimi is ready to help! Just send a message to get started.")}`);
|
|
99463
|
-
lines.push("");
|
|
99464
|
-
lines.push(` ${chalk.bold("Keyboard shortcuts")}`);
|
|
99465
99622
|
const shortcuts = this.opts.shortcuts ?? DEFAULT_KEYBOARD_SHORTCUTS;
|
|
99466
99623
|
const kbdWidth = Math.max(8, ...shortcuts.map((s) => s.keys.length));
|
|
99467
|
-
|
|
99468
|
-
lines.push("");
|
|
99469
|
-
lines.push(` ${chalk.bold("Slash commands")}`);
|
|
99470
|
-
const sortedCmds = [...this.opts.commands].sort((a, b) => a.name.localeCompare(b.name));
|
|
99624
|
+
const sortedCmds = [...this.opts.commands].toSorted((a, b) => a.name.localeCompare(b.name));
|
|
99471
99625
|
const cmdLabels = sortedCmds.map((c) => {
|
|
99472
99626
|
const aliases = c.aliases.length > 0 ? ` (${c.aliases.map((a) => "/" + a).join(", ")})` : "";
|
|
99473
99627
|
return `/${c.name}${aliases}`;
|
|
99474
99628
|
});
|
|
99475
99629
|
const cmdWidth = Math.max(12, ...cmdLabels.map((l) => l.length));
|
|
99476
|
-
|
|
99477
|
-
|
|
99478
|
-
|
|
99479
|
-
|
|
99480
|
-
|
|
99481
|
-
|
|
99482
|
-
|
|
99630
|
+
const lines = [
|
|
99631
|
+
accent("─".repeat(width)),
|
|
99632
|
+
accent.bold(" help ") + muted("· Esc / Enter / q to close · ↑↓ scroll"),
|
|
99633
|
+
"",
|
|
99634
|
+
` ${dim("Sure, Kimi is ready to help! Just send a message to get started.")}`,
|
|
99635
|
+
"",
|
|
99636
|
+
` ${chalk.bold("Keyboard shortcuts")}`,
|
|
99637
|
+
...shortcuts.map((s) => ` ${kbdColor(s.keys.padEnd(kbdWidth))} ${dim(s.description)}`),
|
|
99638
|
+
"",
|
|
99639
|
+
` ${chalk.bold("Slash commands")}`,
|
|
99640
|
+
...sortedCmds.map((cmd, i) => ` ${slashColor(cmdLabels[i].padEnd(cmdWidth))} ${dim(cmd.description)}`),
|
|
99641
|
+
"",
|
|
99642
|
+
accent("─".repeat(width))
|
|
99643
|
+
];
|
|
99483
99644
|
const content = lines.slice(1, lines.length - 1);
|
|
99484
99645
|
const maxVisible = Math.max(5, this.opts.maxVisible ?? 24);
|
|
99485
99646
|
if (content.length > maxVisible) {
|
|
@@ -99490,7 +99651,7 @@ var HelpPanelComponent = class extends Container {
|
|
|
99490
99651
|
lines[0],
|
|
99491
99652
|
...slice,
|
|
99492
99653
|
scrollInfo,
|
|
99493
|
-
lines
|
|
99654
|
+
lines.at(-1)
|
|
99494
99655
|
].map((line) => truncateToWidth(line, width));
|
|
99495
99656
|
}
|
|
99496
99657
|
this.scrollTop = 0;
|
|
@@ -99664,8 +99825,7 @@ var SessionPickerComponent = class extends Container {
|
|
|
99664
99825
|
}
|
|
99665
99826
|
render(width) {
|
|
99666
99827
|
const colors = this.colors;
|
|
99667
|
-
const lines = [];
|
|
99668
|
-
lines.push(chalk.hex(colors.primary)("─".repeat(width)));
|
|
99828
|
+
const lines = [chalk.hex(colors.primary)("─".repeat(width))];
|
|
99669
99829
|
if (this.loading) {
|
|
99670
99830
|
lines.push(chalk.hex(colors.primary).bold("Sessions"));
|
|
99671
99831
|
lines.push(chalk.hex(colors.textMuted)("Loading sessions..."));
|
|
@@ -101209,10 +101369,7 @@ function registerReverseRPCHandlers(state, client, callbacks) {
|
|
|
101209
101369
|
showPanel: (payload) => showQuestionDialog(state, payload),
|
|
101210
101370
|
hidePanel: () => hideQuestionDialog(state)
|
|
101211
101371
|
});
|
|
101212
|
-
|
|
101213
|
-
disposers.push(client.onRequest("approval.request", createApprovalRequestHandler(state)));
|
|
101214
|
-
disposers.push(client.onRequest("question.ask", createQuestionAskHandler(state)));
|
|
101215
|
-
return disposers;
|
|
101372
|
+
return [client.onRequest("approval.request", createApprovalRequestHandler(state)), client.onRequest("question.ask", createQuestionAskHandler(state))];
|
|
101216
101373
|
}
|
|
101217
101374
|
//#endregion
|
|
101218
101375
|
//#region src/utils/clipboard/clipboard-native.ts
|
|
@@ -101554,44 +101711,6 @@ function readUInt32BE(b, off) {
|
|
|
101554
101711
|
return b[off] * 16777216 + (b[off + 1] << 16) + (b[off + 2] << 8) + b[off + 3] >>> 0;
|
|
101555
101712
|
}
|
|
101556
101713
|
//#endregion
|
|
101557
|
-
//#region src/tui/commands/skill-commands.ts
|
|
101558
|
-
async function fetchSkills(client, sessionId) {
|
|
101559
|
-
const skills = (await client.listSkills(sessionId))?.skills ?? [];
|
|
101560
|
-
const out = [];
|
|
101561
|
-
for (const s of skills) {
|
|
101562
|
-
if (typeof s?.name !== "string" || s.name.length === 0) continue;
|
|
101563
|
-
if (typeof s.content !== "string") continue;
|
|
101564
|
-
out.push({
|
|
101565
|
-
name: s.name,
|
|
101566
|
-
content: s.content,
|
|
101567
|
-
...typeof s.description === "string" ? { description: s.description } : {}
|
|
101568
|
-
});
|
|
101569
|
-
}
|
|
101570
|
-
return out;
|
|
101571
|
-
}
|
|
101572
|
-
function buildSkillCommand(skill) {
|
|
101573
|
-
return {
|
|
101574
|
-
name: `${SKILL_COMMAND_PREFIX}${skill.name}`,
|
|
101575
|
-
aliases: [],
|
|
101576
|
-
description: skill.description ?? "",
|
|
101577
|
-
mode: "both",
|
|
101578
|
-
async execute(args, ctx) {
|
|
101579
|
-
const trimmed = args.trim();
|
|
101580
|
-
const prompt = trimmed.length > 0 ? `${skill.content}\n\nUser request:\n${trimmed}` : skill.content;
|
|
101581
|
-
try {
|
|
101582
|
-
await ctx.client.prompt(ctx.appState.sessionId, { input: prompt });
|
|
101583
|
-
} catch (err) {
|
|
101584
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
101585
|
-
return {
|
|
101586
|
-
type: "ok",
|
|
101587
|
-
message: `Skill "${skill.name}" failed: ${msg}`
|
|
101588
|
-
};
|
|
101589
|
-
}
|
|
101590
|
-
return { type: "ok" };
|
|
101591
|
-
}
|
|
101592
|
-
};
|
|
101593
|
-
}
|
|
101594
|
-
//#endregion
|
|
101595
101714
|
//#region src/tui/components/editor/file-mention-provider.ts
|
|
101596
101715
|
/**
|
|
101597
101716
|
* `@file` autocomplete provider for the input box.
|
|
@@ -101701,7 +101820,7 @@ function rankForEmptyQuery(files, snapshot) {
|
|
|
101701
101820
|
const result = [];
|
|
101702
101821
|
const cap = MAX_SUGGESTIONS_WHEN_EMPTY;
|
|
101703
101822
|
const inFiles = new Set(files);
|
|
101704
|
-
const byRecency = [...snapshot.recencyOrder.entries()].filter(([path]) => inFiles.has(path)).
|
|
101823
|
+
const byRecency = [...snapshot.recencyOrder.entries()].filter(([path]) => inFiles.has(path)).toSorted((a, b) => a[1] - b[1]);
|
|
101705
101824
|
for (const [path] of byRecency) {
|
|
101706
101825
|
if (result.length >= cap) break;
|
|
101707
101826
|
if (picked.has(path)) continue;
|
|
@@ -101709,7 +101828,7 @@ function rankForEmptyQuery(files, snapshot) {
|
|
|
101709
101828
|
result.push(path);
|
|
101710
101829
|
}
|
|
101711
101830
|
if (result.length < cap) {
|
|
101712
|
-
const byMtime = files.filter((p) => !picked.has(p) && snapshot.mtimeByPath.has(p)).
|
|
101831
|
+
const byMtime = files.filter((p) => !picked.has(p) && snapshot.mtimeByPath.has(p)).toSorted((a, b) => (snapshot.mtimeByPath.get(b) ?? 0) - (snapshot.mtimeByPath.get(a) ?? 0));
|
|
101713
101832
|
for (const path of byMtime) {
|
|
101714
101833
|
if (result.length >= cap) break;
|
|
101715
101834
|
picked.add(path);
|
|
@@ -101717,7 +101836,7 @@ function rankForEmptyQuery(files, snapshot) {
|
|
|
101717
101836
|
}
|
|
101718
101837
|
}
|
|
101719
101838
|
if (result.length < cap) {
|
|
101720
|
-
const rest = files.filter((p) => !picked.has(p)).
|
|
101839
|
+
const rest = files.filter((p) => !picked.has(p)).toSorted((a, b) => basename(a).localeCompare(basename(b)) || a.localeCompare(b));
|
|
101721
101840
|
for (const path of rest) {
|
|
101722
101841
|
if (result.length >= cap) break;
|
|
101723
101842
|
result.push(path);
|
|
@@ -102238,6 +102357,7 @@ var KimiTUI = class {
|
|
|
102238
102357
|
showUsage(this.state);
|
|
102239
102358
|
},
|
|
102240
102359
|
sendAsMessage: (text) => sendMessage(this.state, (e) => this.addEntry(e), text),
|
|
102360
|
+
activateSkill: (name, args, fullPrompt) => sendSkillActivation(this.state, (e) => this.addEntry(e), name, args, fullPrompt),
|
|
102241
102361
|
performReload: (action) => performReload(this.state, action, this.buildInputHooks())
|
|
102242
102362
|
};
|
|
102243
102363
|
}
|
|
@@ -102285,9 +102405,9 @@ async function runShell(opts, version) {
|
|
|
102285
102405
|
isReplaying: false,
|
|
102286
102406
|
streamingPhase: "idle",
|
|
102287
102407
|
streamingStartTime: 0,
|
|
102288
|
-
theme:
|
|
102408
|
+
theme: "dark",
|
|
102289
102409
|
version,
|
|
102290
|
-
editorCommand:
|
|
102410
|
+
editorCommand: null,
|
|
102291
102411
|
availableModels: ctx.availableModels,
|
|
102292
102412
|
sessionTitle: null
|
|
102293
102413
|
};
|
|
@@ -102421,7 +102541,7 @@ async function promptForInstallConfirmation(options) {
|
|
|
102421
102541
|
const output = options.output ?? process.stdout;
|
|
102422
102542
|
const choices = createInstallPromptChoices(options.target);
|
|
102423
102543
|
let selectedIndex = getDefaultInstallPromptSelection(choices);
|
|
102424
|
-
return
|
|
102544
|
+
return new Promise((resolve) => {
|
|
102425
102545
|
let lineCount = 0;
|
|
102426
102546
|
const hadRawMode = "isRaw" in input ? input.isRaw === true : false;
|
|
102427
102547
|
const canSetRawMode = typeof input.setRawMode === "function";
|