kfc-code-cli 0.0.1-alpha.7 → 0.0.1-alpha.9

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 (2) hide show
  1. package/dist/main.mjs +469 -184
  2. 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: disposeClient, maxContextSize, defaultModel: modelAlias, defaultThinking, defaultYolo, defaultPlanMode, theme, defaultEditor, availableModels, syncSessionRuntime } = await createDefaultSoulPlusWireClient({
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: modelAlias,
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/media/image-thumbnail.ts
95325
+ //#region src/tui/components/messages/skill-activation.ts
95297
95326
  /**
95298
- * Transcript-side rendering of a pasted image.
95327
+ * Skill activation card.
95299
95328
  *
95300
- * On terminals that speak the Kitty graphics protocol or iTerm2 inline
95301
- * image protocol (detected by pi-tui's `getCapabilities()`), we show
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
- * Height is capped at ~12 rows so a single screenshot can't monopolize
95308
- * the viewport; pi-tui handles proportional scaling internally.
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 MAX_IMAGE_ROWS = 12;
95311
- var ImageThumbnail = class extends Container {
95312
- constructor(attachment, colors) {
95341
+ const ARGS_PREVIEW_MAX = 200;
95342
+ var SkillActivationComponent = class extends Container {
95343
+ constructor(name, args, colors) {
95313
95344
  super();
95314
- const caps = getCapabilities();
95315
- if (!(caps.images === "kitty" || caps.images === "iterm2")) {
95316
- this.addChild(new Text(chalk.dim.cyan(` ${attachment.placeholder}`), 0, 0));
95317
- return;
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 = 5;
95359
+ const PREVIEW_LINES = 3;
95334
95360
  const SPINNER_FRAMES = [
95335
95361
  "⠋",
95336
95362
  "⠙",
@@ -95381,10 +95407,10 @@ var ThinkingComponent = class {
95381
95407
  const textComponent = new Text(chalk.hex(this.color).italic(this.text), 0, 0);
95382
95408
  const contentLines = this.text.length > 0 ? textComponent.render(contentWidth) : [""];
95383
95409
  if (this.mode === "live") {
95384
- const visibleLines = contentLines.length > PREVIEW_LINES$1 ? contentLines.slice(contentLines.length - PREVIEW_LINES$1) : contentLines;
95410
+ const visibleLines = contentLines.length > PREVIEW_LINES ? contentLines.slice(contentLines.length - PREVIEW_LINES) : contentLines;
95385
95411
  return [
95386
95412
  "",
95387
- INDENT + chalk.hex(this.color)(SPINNER_FRAMES[this.spinnerFrame] ?? SPINNER_FRAMES[0]) + chalk.hex(this.color)(" thinking..."),
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
  }
@@ -95393,9 +95419,9 @@ var ThinkingComponent = class {
95393
95419
  const p = i === 0 && this.showMarker ? chalk.hex(this.color)(BULLET) : INDENT;
95394
95420
  rendered.push(p + contentLines[i]);
95395
95421
  }
95396
- if (this.expanded || contentLines.length <= PREVIEW_LINES$1) return rendered;
95397
- const truncated = rendered.slice(0, 1 + PREVIEW_LINES$1);
95398
- const remaining = contentLines.length - PREVIEW_LINES$1;
95422
+ if (this.expanded || contentLines.length <= PREVIEW_LINES) return rendered;
95423
+ const truncated = rendered.slice(0, 1 + PREVIEW_LINES);
95424
+ const remaining = contentLines.length - PREVIEW_LINES;
95399
95425
  truncated.push(INDENT + chalk.dim(`... (${String(remaining)} more lines, ctrl+o to expand)`));
95400
95426
  return truncated;
95401
95427
  }
@@ -95524,13 +95550,174 @@ var PlanBoxComponent = class {
95524
95550
  }
95525
95551
  };
95526
95552
  //#endregion
95553
+ //#region src/tui/components/messages/tool-renderers/types.ts
95554
+ function strArg(args, ...keys) {
95555
+ for (const key of keys) {
95556
+ const v = args[key];
95557
+ if (typeof v === "string" && v.length > 0) return v;
95558
+ }
95559
+ return "";
95560
+ }
95561
+ //#endregion
95562
+ //#region src/tui/components/messages/tool-renderers/chip.ts
95563
+ function countNonEmptyLines(text) {
95564
+ if (text.length === 0) return 0;
95565
+ let n = 0;
95566
+ for (const line of text.split("\n")) if (line.length > 0) n++;
95567
+ return n;
95568
+ }
95569
+ function pluralize(n, singular, plural) {
95570
+ return `${String(n)} ${n === 1 ? singular : plural ?? `${singular}s`}`;
95571
+ }
95572
+ function formatBytes(bytes) {
95573
+ if (bytes < 1024) return `${String(bytes)} B`;
95574
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
95575
+ return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
95576
+ }
95577
+ const editChip = (toolCall) => {
95578
+ const oldStr = strArg(toolCall.args, "old_string");
95579
+ const newStr = strArg(toolCall.args, "new_string");
95580
+ if (oldStr.length === 0 && newStr.length === 0) return "";
95581
+ const diff = computeDiffLines(oldStr, newStr);
95582
+ const added = diff.filter((l) => l.kind === "add").length;
95583
+ const removed = diff.filter((l) => l.kind === "delete").length;
95584
+ const parts = [];
95585
+ if (added > 0) parts.push(`+${String(added)}`);
95586
+ if (removed > 0) parts.push(`-${String(removed)}`);
95587
+ return parts.join(" ");
95588
+ };
95589
+ const writeChip = (toolCall) => {
95590
+ const content = strArg(toolCall.args, "content");
95591
+ const normalized = content.endsWith("\n") ? content.slice(0, -1) : content;
95592
+ return pluralize(normalized.length > 0 ? normalized.split("\n").length : 0, "line");
95593
+ };
95594
+ const readChip = (_toolCall, result) => pluralize(countNonEmptyLines(result.output), "line");
95595
+ const grepChip = (_toolCall, result) => {
95596
+ const matches = countNonEmptyLines(result.output);
95597
+ if (matches === 0) return "no matches";
95598
+ return pluralize(matches, "match", "matches");
95599
+ };
95600
+ const globChip = (_toolCall, result) => {
95601
+ const files = countNonEmptyLines(result.output);
95602
+ if (files === 0) return "no files";
95603
+ return pluralize(files, "file");
95604
+ };
95605
+ const fetchChip = (_toolCall, result) => formatBytes(Buffer.byteLength(result.output, "utf8"));
95606
+ const webSearchChip = (_toolCall, result) => {
95607
+ const lines = result.output.split("\n").filter((l) => l.trim().length > 0);
95608
+ let count = 0;
95609
+ for (const line of lines) if (/^\s*(\d+\.|[-*])\s+/.test(line)) count++;
95610
+ if (count === 0) return lines.length === 0 ? "no results" : "web result";
95611
+ return pluralize(count, "result");
95612
+ };
95613
+ const REGISTRY = {
95614
+ Edit: editChip,
95615
+ Write: writeChip,
95616
+ Read: readChip,
95617
+ Grep: grepChip,
95618
+ Glob: globChip,
95619
+ FetchURL: fetchChip,
95620
+ WebSearch: webSearchChip
95621
+ };
95622
+ function pickChip(toolName) {
95623
+ return REGISTRY[toolName];
95624
+ }
95625
+ //#endregion
95626
+ //#region src/tui/components/messages/tool-renderers/truncated.ts
95627
+ const renderTruncated = (_toolCall, result, ctx) => {
95628
+ if (!result.output) return [];
95629
+ const tint = result.is_error ? chalk.hex(ctx.colors.error) : chalk.dim;
95630
+ const lines = result.output.split("\n");
95631
+ if (ctx.expanded) return [new Text(tint(result.output), 2, 0)];
95632
+ const shown = lines.slice(0, 3);
95633
+ const remaining = lines.length - shown.length;
95634
+ const out = [new Text(tint(shown.join("\n")), 2, 0)];
95635
+ if (remaining > 0) out.push(new Text(chalk.dim(`... (${String(remaining)} more lines, ctrl+o to expand)`), 2, 0));
95636
+ return out;
95637
+ };
95638
+ //#endregion
95639
+ //#region src/tui/components/messages/tool-renderers/summary.ts
95640
+ const GLANCE_SAMPLES = 3;
95641
+ function withGlance(glance) {
95642
+ return (toolCall, result, ctx) => {
95643
+ if (result.is_error) return renderTruncated(toolCall, result, ctx);
95644
+ const out = [];
95645
+ if (glance !== null) {
95646
+ const line = glance(toolCall, result);
95647
+ if (line.length > 0) out.push(new Text(` ${chalk.dim(line)}`, 0, 0));
95648
+ }
95649
+ if (ctx.expanded && result.output.length > 0) out.push(new Text(chalk.dim(result.output), 4, 0));
95650
+ return out;
95651
+ };
95652
+ }
95653
+ function nonEmptyLines(text) {
95654
+ if (text.length === 0) return [];
95655
+ return text.split("\n").filter((line) => line.length > 0);
95656
+ }
95657
+ function pathFromGrepLine(line) {
95658
+ const idx = line.indexOf(":");
95659
+ if (idx <= 0) return line;
95660
+ const second = line.indexOf(":", idx + 1);
95661
+ if (second <= 0) return line;
95662
+ return line.slice(0, second);
95663
+ }
95664
+ const grepGlance = (_toolCall, result) => {
95665
+ const lines = nonEmptyLines(result.output);
95666
+ if (lines.length === 0) return "";
95667
+ const samples = lines.slice(0, GLANCE_SAMPLES).map(pathFromGrepLine);
95668
+ const remaining = lines.length - samples.length;
95669
+ const tail = remaining > 0 ? `, +${String(remaining)} more` : "";
95670
+ return `${samples.join(", ")}${tail}`;
95671
+ };
95672
+ const globGlance = (_toolCall, result) => {
95673
+ const lines = nonEmptyLines(result.output);
95674
+ if (lines.length === 0) return "";
95675
+ const samples = lines.slice(0, GLANCE_SAMPLES);
95676
+ const remaining = lines.length - samples.length;
95677
+ const tail = remaining > 0 ? `, +${String(remaining)} more` : "";
95678
+ return `${samples.join(", ")}${tail}`;
95679
+ };
95680
+ const readSummary = withGlance(null);
95681
+ const fetchSummary = withGlance(null);
95682
+ const webSearchSummary = withGlance(null);
95683
+ const thinkSummary = withGlance(null);
95684
+ const editSummary = withGlance(null);
95685
+ const writeSummary = withGlance(null);
95686
+ const grepSummary = withGlance(grepGlance);
95687
+ const globSummary = withGlance(globGlance);
95688
+ //#endregion
95689
+ //#region src/tui/components/messages/tool-renderers/registry.ts
95690
+ /**
95691
+ * Tool result renderer registry.
95692
+ *
95693
+ * Each tool name maps to a `ResultRenderer` that turns the tool's
95694
+ * `ToolResultBlockData` into renderable Components. Tools without an
95695
+ * explicit entry fall through to `renderTruncated` (the original
95696
+ * 3-line + ctrl+o behavior).
95697
+ *
95698
+ * Keep this dispatch flat — tool names live next to the renderer they
95699
+ * choose, so adding a new tool means appending one case.
95700
+ */
95701
+ function pickResultRenderer(toolName) {
95702
+ switch (toolName) {
95703
+ case "Read": return readSummary;
95704
+ case "Grep": return grepSummary;
95705
+ case "Glob": return globSummary;
95706
+ case "FetchURL": return fetchSummary;
95707
+ case "WebSearch": return webSearchSummary;
95708
+ case "Think": return thinkSummary;
95709
+ case "Edit": return editSummary;
95710
+ case "Write": return writeSummary;
95711
+ default: return renderTruncated;
95712
+ }
95713
+ }
95714
+ //#endregion
95527
95715
  //#region src/tui/components/messages/tool-call.ts
95528
95716
  /**
95529
95717
  * Renders a tool call entry in the transcript.
95530
95718
  * Supports expand/collapse via Ctrl+O.
95531
95719
  */
95532
95720
  const MAX_ARG_LENGTH = 60;
95533
- const PREVIEW_LINES = 3;
95534
95721
  const CALL_PREVIEW_LINES = 10;
95535
95722
  const BLINK_INTERVAL$1 = 500;
95536
95723
  const MAX_SUB_TOOL_CALLS_SHOWN = 4;
@@ -95586,7 +95773,7 @@ function extractApprovedPlan(output) {
95586
95773
  }
95587
95774
  const STREAMING_FIELD_RE$1 = /"(path|file_path|command|pattern|query|url|description|title|name)"\s*:\s*"((?:\\.|[^"\\])*)"/g;
95588
95775
  function unescapeJsonString$1(s) {
95589
- return s.replace(/\\(["\\/bfnrt])/g, (_, ch) => {
95776
+ return s.replaceAll(/\\(["\\/bfnrt])/g, (_, ch) => {
95590
95777
  switch (ch) {
95591
95778
  case "n": return "\n";
95592
95779
  case "t": return " ";
@@ -95648,7 +95835,7 @@ function extractPartialStringField(text, key) {
95648
95835
  const hex = text.slice(i + 2, i + 6);
95649
95836
  const code = Number.parseInt(hex, 16);
95650
95837
  if (Number.isNaN(code)) return out;
95651
- out += String.fromCharCode(code);
95838
+ out += String.fromCodePoint(code);
95652
95839
  i += 6;
95653
95840
  continue;
95654
95841
  }
@@ -95680,14 +95867,10 @@ function parseArgsPreview(value) {
95680
95867
  }
95681
95868
  function extractKeyArgument(toolName, args) {
95682
95869
  const candidates = {
95683
- Shell: ["command"],
95684
95870
  Bash: ["command"],
95685
- ReadFile: ["path", "file_path"],
95686
95871
  Read: ["path", "file_path"],
95687
95872
  Write: ["path", "file_path"],
95688
- WriteFile: ["path", "file_path"],
95689
95873
  Edit: ["path", "file_path"],
95690
- EditFile: ["path", "file_path"],
95691
95874
  Grep: ["pattern"],
95692
95875
  Glob: ["pattern"],
95693
95876
  FetchURL: ["url"],
@@ -95864,7 +96047,15 @@ var ToolCallComponent = class extends Container {
95864
96047
  const keyArg = extractKeyArgument(toolCall.name, toolCall.args);
95865
96048
  const toolRef = chalk.hex(colors.primary).bold(toolCall.name);
95866
96049
  const argStr = keyArg ? chalk.dim(` (${keyArg})`) : "";
95867
- return `${bullet}${verb} ${toolRef}${argStr}`;
96050
+ const chipStr = isFinished && result ? this.buildHeaderChip(result) : "";
96051
+ return `${bullet}${verb} ${toolRef}${argStr}${chipStr}`;
96052
+ }
96053
+ buildHeaderChip(result) {
96054
+ const provider = pickChip(this.toolCall.name);
96055
+ if (provider === void 0) return "";
96056
+ const text = provider(this.toolCall, result);
96057
+ if (text.length === 0) return "";
96058
+ return (result.is_error ? chalk.hex(this.colors.error) : chalk.dim)(` · ${text}`);
95868
96059
  }
95869
96060
  rebuildContent() {
95870
96061
  while (this.children.length > this.callPreviewEndIndex) this.children.pop();
@@ -95919,7 +96110,7 @@ var ToolCallComponent = class extends Container {
95919
96110
  this.buildStreamingPreview(this.toolCall.streamingArguments);
95920
96111
  return;
95921
96112
  }
95922
- if (name === "Write" || name === "WriteFile") {
96113
+ if (name === "Write") {
95923
96114
  const content = str(this.toolCall.args["content"]);
95924
96115
  if (content.length === 0) return;
95925
96116
  const allLines = highlightLines(content, langFromPath(str(this.toolCall.args["file_path"] ?? this.toolCall.args["path"])));
@@ -95930,7 +96121,7 @@ var ToolCallComponent = class extends Container {
95930
96121
  this.addChild(new Text(lineNum + line, 2, 0));
95931
96122
  }
95932
96123
  if (remaining > 0) this.addChild(new Text(chalk.dim(`... (${String(remaining)} more lines, ${String(allLines.length)} total)`), 2, 0));
95933
- } else if (name === "Edit" || name === "EditFile") {
96124
+ } else if (name === "Edit") {
95934
96125
  const oldStr = str(this.toolCall.args["old_string"]);
95935
96126
  const newStr = str(this.toolCall.args["new_string"]);
95936
96127
  if (oldStr.length === 0 && newStr.length === 0) return;
@@ -95954,14 +96145,14 @@ var ToolCallComponent = class extends Container {
95954
96145
  */
95955
96146
  buildStreamingPreview(streamText) {
95956
96147
  const name = this.toolCall.name;
95957
- if (name === "Write" || name === "WriteFile") {
96148
+ if (name === "Write") {
95958
96149
  const content = extractPartialStringField(streamText, "content");
95959
96150
  if (content === void 0 || content.length === 0) return;
95960
96151
  const filePath = extractPartialStringField(streamText, "file_path") ?? extractPartialStringField(streamText, "path") ?? "";
95961
96152
  this.renderStreamingCode(content, filePath);
95962
96153
  return;
95963
96154
  }
95964
- if (name === "Edit" || name === "EditFile" || name === "MultiEdit") {
96155
+ if (name === "Edit") {
95965
96156
  const oldStr = extractPartialStringField(streamText, "old_string") ?? "";
95966
96157
  const newStr = extractPartialStringField(streamText, "new_string") ?? "";
95967
96158
  if (oldStr.length === 0 && newStr.length === 0) return;
@@ -95972,7 +96163,7 @@ var ToolCallComponent = class extends Container {
95972
96163
  if (remaining > 0) this.addChild(new Text(chalk.dim(`... (${String(remaining)} more lines)`), 2, 0));
95973
96164
  return;
95974
96165
  }
95975
- if (name === "Bash" || name === "Shell") {
96166
+ if (name === "Bash") {
95976
96167
  const cmd = extractPartialStringField(streamText, "command");
95977
96168
  if (cmd === void 0 || cmd.length === 0) return;
95978
96169
  const lines = cmd.split("\n").slice(0, CALL_PREVIEW_LINES);
@@ -96014,15 +96205,11 @@ var ToolCallComponent = class extends Container {
96014
96205
  }
96015
96206
  if (this.toolCall.name === "SetTodoList" && !result.is_error) return;
96016
96207
  if (this.toolCall.name === "AskUserQuestion" && !result.is_error && this.renderAskUserQuestionResult(result.output)) return;
96017
- const tint = result.is_error ? chalk.hex(this.colors.error) : chalk.dim;
96018
- const lines = result.output.split("\n");
96019
- if (this.expanded) this.addChild(new Text(tint(result.output), 2, 0));
96020
- else {
96021
- const shown = lines.slice(0, PREVIEW_LINES);
96022
- const remaining = lines.length - shown.length;
96023
- this.addChild(new Text(tint(shown.join("\n")), 2, 0));
96024
- if (remaining > 0) this.addChild(new Text(chalk.dim(`... (${String(remaining)} more lines, ctrl+o to expand)`), 2, 0));
96025
- }
96208
+ const components = pickResultRenderer(this.toolCall.name)(this.toolCall, result, {
96209
+ expanded: this.expanded,
96210
+ colors: this.colors
96211
+ });
96212
+ for (const component of components) this.addChild(component);
96026
96213
  }
96027
96214
  /**
96028
96215
  * Render AskUserQuestion's JSON payload as a friendly Q/A list.
@@ -96101,10 +96288,11 @@ var WelcomeComponent = class {
96101
96288
  "",
96102
96289
  ...infoLines
96103
96290
  ];
96104
- const lines = [];
96105
- lines.push("");
96106
- lines.push(primary("╭" + "─".repeat(width - 2) + "╮"));
96107
- lines.push(primary("│") + " ".repeat(width - 2) + primary("│"));
96291
+ const lines = [
96292
+ "",
96293
+ primary("╭" + "─".repeat(width - 2) + "╮"),
96294
+ primary("│") + " ".repeat(width - 2) + primary("│")
96295
+ ];
96108
96296
  for (const content of contentLines) {
96109
96297
  const truncated = truncateToWidth(content, innerWidth, "…");
96110
96298
  const vis = visibleWidth(truncated);
@@ -96250,6 +96438,7 @@ function createTranscriptComponent(state, entry) {
96250
96438
  }
96251
96439
  return msg;
96252
96440
  }
96441
+ case "skill_activation": return new SkillActivationComponent(entry.skillName ?? entry.content, entry.skillArgs, state.colors);
96253
96442
  case "assistant": return createAssistantEntry(state, entry.content);
96254
96443
  case "thinking": return new ThinkingComponent(entry.content, state.colors, true);
96255
96444
  case "tool_call":
@@ -96392,7 +96581,7 @@ function buildImagePart(att) {
96392
96581
  }
96393
96582
  //#endregion
96394
96583
  //#region src/tui/theme/pi-tui-theme.ts
96395
- const HEADING_HASH_PREFIX = /^((?:\x1b\[[0-9;]*m)*)#{1,6}[ \t]+/;
96584
+ const HEADING_HASH_PREFIX = /^((?:\x1B\[[0-9;]*m)*)#{1,6}[ \t]+/;
96396
96585
  function createMarkdownTheme(colors) {
96397
96586
  const stripHash = (text) => text.replace(HEADING_HASH_PREFIX, "$1");
96398
96587
  return {
@@ -96638,7 +96827,7 @@ function fetchSnapshot(gitRoot) {
96638
96827
  const seen = /* @__PURE__ */ new Set();
96639
96828
  for (const path of tracked) seen.add(path);
96640
96829
  for (const path of untracked) seen.add(path);
96641
- const merged = [...seen].sort();
96830
+ const merged = [...seen].toSorted();
96642
96831
  const files = merged.length > MAX_ENTRIES ? merged.slice(0, MAX_ENTRIES) : merged;
96643
96832
  return {
96644
96833
  files,
@@ -96794,6 +96983,7 @@ function createAuthCommands(deps = {}) {
96794
96983
  aliases: [],
96795
96984
  description: "Clear OAuth credentials",
96796
96985
  mode: "both",
96986
+ priority: 40,
96797
96987
  async execute(_args, _ctx) {
96798
96988
  if (deps.client === void 0) return ok$2("OAuth is unavailable: no wire client is attached.");
96799
96989
  if (!(await deps.client.authStatus(providerName)).providers.some((provider) => provider.provider_name === providerName && provider.has_token)) return ok$2("Not logged in.");
@@ -96805,6 +96995,7 @@ function createAuthCommands(deps = {}) {
96805
96995
  aliases: [],
96806
96996
  description: "Start OAuth device code login flow",
96807
96997
  mode: "both",
96998
+ priority: 40,
96808
96999
  async execute(_args, ctx) {
96809
97000
  if (deps.client === void 0) return ok$2("OAuth is unavailable: no wire client is attached.");
96810
97001
  const disposeDeviceCodeHandler = deps.client.onRequest("auth.device_code", (req) => {
@@ -96853,6 +97044,7 @@ const shellCommands = [
96853
97044
  aliases: ["quit", "q"],
96854
97045
  description: "Exit the application",
96855
97046
  mode: "both",
97047
+ priority: 20,
96856
97048
  async execute() {
96857
97049
  return { type: "exit" };
96858
97050
  }
@@ -96862,6 +97054,7 @@ const shellCommands = [
96862
97054
  aliases: ["h", "?"],
96863
97055
  description: "Show available commands and shortcuts",
96864
97056
  mode: "both",
97057
+ priority: 80,
96865
97058
  async execute(_args, _ctx) {
96866
97059
  return ok$1("__show_help__");
96867
97060
  }
@@ -96871,6 +97064,7 @@ const shellCommands = [
96871
97064
  aliases: [],
96872
97065
  description: "Show version information",
96873
97066
  mode: "both",
97067
+ priority: 20,
96874
97068
  async execute(_args, ctx) {
96875
97069
  return ok$1(`Kimi Code v${ctx.appState.version}`);
96876
97070
  }
@@ -96880,6 +97074,7 @@ const shellCommands = [
96880
97074
  aliases: [],
96881
97075
  description: "Start a fresh session in the current workspace",
96882
97076
  mode: "both",
97077
+ priority: 80,
96883
97078
  async execute() {
96884
97079
  return {
96885
97080
  type: "reload",
@@ -96892,6 +97087,7 @@ const shellCommands = [
96892
97087
  aliases: ["resume"],
96893
97088
  description: "Browse and resume sessions",
96894
97089
  mode: "both",
97090
+ priority: 80,
96895
97091
  async execute(_args, _ctx) {
96896
97092
  return ok$1("__show_sessions__");
96897
97093
  }
@@ -96901,6 +97097,7 @@ const shellCommands = [
96901
97097
  aliases: ["rename"],
96902
97098
  description: "Set or show session title",
96903
97099
  mode: "both",
97100
+ priority: 60,
96904
97101
  async execute(args, ctx) {
96905
97102
  const trimmed = args.trim();
96906
97103
  if (trimmed.length === 0) {
@@ -96921,6 +97118,7 @@ const shellCommands = [
96921
97118
  aliases: ["yes"],
96922
97119
  description: "Toggle auto-approve mode",
96923
97120
  mode: "both",
97121
+ priority: 100,
96924
97122
  async execute(args, ctx) {
96925
97123
  let enabled;
96926
97124
  if (args === "on") enabled = true;
@@ -96937,6 +97135,7 @@ const shellCommands = [
96937
97135
  aliases: [],
96938
97136
  description: "Toggle plan mode",
96939
97137
  mode: "both",
97138
+ priority: 100,
96940
97139
  async execute(args, ctx) {
96941
97140
  const subcmd = args.trim().toLowerCase();
96942
97141
  if (subcmd === "view") {
@@ -96964,6 +97163,7 @@ const shellCommands = [
96964
97163
  aliases: [],
96965
97164
  description: "Switch LLM model",
96966
97165
  mode: "both",
97166
+ priority: 100,
96967
97167
  async execute(args, ctx) {
96968
97168
  const trimmed = args.trim();
96969
97169
  if (trimmed.length === 0) return ok$1("__show_model_picker__");
@@ -96976,6 +97176,7 @@ const shellCommands = [
96976
97176
  aliases: ["status"],
96977
97177
  description: "Show session tokens + context window + plan quotas",
96978
97178
  mode: "both",
97179
+ priority: 60,
96979
97180
  async execute() {
96980
97181
  return ok$1("__show_usage__");
96981
97182
  }
@@ -96995,6 +97196,7 @@ const soulCommands = [{
96995
97196
  aliases: [],
96996
97197
  description: "Compact the conversation context",
96997
97198
  mode: "both",
97199
+ priority: 80,
96998
97200
  async execute(args, ctx) {
96999
97201
  const customInstruction = args.trim() || void 0;
97000
97202
  await ctx.client.compact(ctx.appState.sessionId, customInstruction);
@@ -97077,11 +97279,17 @@ var SlashCommandRegistry = class {
97077
97279
  }
97078
97280
  return [...bestScores.values()].toSorted((a, b) => b.score - a.score || a.def.name.localeCompare(b.def.name)).map(({ def }) => def);
97079
97281
  }
97080
- /** List all registered commands, optionally filtered by mode. */
97282
+ /**
97283
+ * List all registered commands, optionally filtered by mode.
97284
+ * Built-in commands come first, sorted alphabetically; skill commands
97285
+ * follow in their original registration order.
97286
+ */
97081
97287
  listAll(mode) {
97082
97288
  const all = [...this.commands.values()];
97083
- if (mode === void 0) return all.toSorted((a, b) => a.name.localeCompare(b.name));
97084
- return all.filter((def) => def.mode === mode || def.mode === "both").toSorted((a, b) => a.name.localeCompare(b.name));
97289
+ const filtered = mode === void 0 ? all : all.filter((def) => def.mode === mode || def.mode === "both");
97290
+ 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));
97291
+ const skills = filtered.filter((def) => def.name.startsWith(SKILL_COMMAND_PREFIX));
97292
+ return [...builtins, ...skills];
97085
97293
  }
97086
97294
  /** Get the number of registered commands. */
97087
97295
  get size() {
@@ -97652,9 +97860,7 @@ var TodoPanelComponent = class {
97652
97860
  render(width) {
97653
97861
  if (this.todos.length === 0) return [];
97654
97862
  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"));
97863
+ const lines = [chalk.hex(c.border)("─".repeat(width)), chalk.hex(c.primary).bold(" Todo")];
97658
97864
  for (const todo of this.todos) lines.push(renderRow(todo, c));
97659
97865
  return lines.map((line) => truncateToWidth(line, width));
97660
97866
  }
@@ -97766,18 +97972,18 @@ var QuestionController = class extends ReverseRpcController {
97766
97972
  //#endregion
97767
97973
  //#region src/tui/theme/colors.ts
97768
97974
  const darkColors = {
97769
- primary: "#1783ff",
97770
- primaryDim: "#0f5ab0",
97975
+ primary: "#4FA8FF",
97976
+ primaryDim: "#2563EB",
97771
97977
  text: "#E0E0E0",
97772
97978
  textDim: "#888888",
97773
97979
  textMuted: "#555555",
97774
97980
  success: "#4EC87E",
97775
97981
  warning: "#E8A838",
97776
97982
  error: "#E85454",
97777
- info: "#1783ff",
97983
+ info: "#4FA8FF",
97778
97984
  border: "#444444",
97779
- prompt: "#1783ff",
97780
- spinner: "#1783ff",
97985
+ prompt: "#4FA8FF",
97986
+ spinner: "#4FA8FF",
97781
97987
  user: "#FFCB6B",
97782
97988
  assistant: "#E0E0E0",
97783
97989
  thinking: "#888888",
@@ -98024,7 +98230,7 @@ function enqueueMessage(state, text) {
98024
98230
  }
98025
98231
  function recallLastQueued(state) {
98026
98232
  if (state.queuedMessages.length === 0) return void 0;
98027
- const last = state.queuedMessages[state.queuedMessages.length - 1];
98233
+ const last = state.queuedMessages.at(-1);
98028
98234
  state.queuedMessages = state.queuedMessages.slice(0, -1);
98029
98235
  return last.text;
98030
98236
  }
@@ -98090,6 +98296,66 @@ function sendMessageInternal(state, addEntry, input, options) {
98090
98296
  });
98091
98297
  });
98092
98298
  }
98299
+ /**
98300
+ * Activate a skill: render a compact activation card (no skill body in
98301
+ * the transcript) and forward the prepared prompt — `skill.content +
98302
+ * "\n\nUser request:\n" + args` — to core via `client.prompt`. Keeps
98303
+ * the skill body out of the user-visible transcript while still feeding
98304
+ * it into the LLM exactly as the legacy path did.
98305
+ */
98306
+ function sendSkillActivation(state, addEntry, skillName, skillArgs, fullPrompt) {
98307
+ addEntry({
98308
+ id: nextTranscriptId(),
98309
+ kind: "skill_activation",
98310
+ turnId: void 0,
98311
+ renderMode: "plain",
98312
+ content: `Activated skill: ${skillName}`,
98313
+ skillName,
98314
+ skillArgs
98315
+ });
98316
+ state.currentTurnId = void 0;
98317
+ state.assistantDraft = "";
98318
+ state.assistantStreamActive = false;
98319
+ state.thinkingDraft = "";
98320
+ disposeActiveThinkingComponent(state);
98321
+ state.activeToolCalls.clear();
98322
+ state.streamingToolCallArguments.clear();
98323
+ state.livePane = {
98324
+ ...state.livePane,
98325
+ mode: "waiting",
98326
+ thinkingText: "",
98327
+ assistantText: "",
98328
+ pendingToolCall: null,
98329
+ pendingApproval: null,
98330
+ pendingQuestion: null
98331
+ };
98332
+ state.appState.isStreaming = true;
98333
+ state.appState.streamingPhase = "waiting";
98334
+ state.appState.streamingStartTime = Date.now();
98335
+ state.footer.setState(state.appState);
98336
+ state.ui.requestRender();
98337
+ state.client.prompt(state.appState.sessionId, { input: fullPrompt }).catch((error) => {
98338
+ const message = error instanceof Error ? error.message : String(error);
98339
+ state.appState.isStreaming = false;
98340
+ state.appState.streamingPhase = "idle";
98341
+ state.livePane = {
98342
+ mode: "idle",
98343
+ thinkingText: "",
98344
+ assistantText: "",
98345
+ pendingToolCall: null,
98346
+ pendingApproval: null,
98347
+ pendingQuestion: null
98348
+ };
98349
+ state.footer.setState(state.appState);
98350
+ addEntry({
98351
+ id: nextTranscriptId(),
98352
+ kind: "status",
98353
+ renderMode: "plain",
98354
+ content: `Skill "${skillName}" failed: ${message}`,
98355
+ color: state.colors.error
98356
+ });
98357
+ });
98358
+ }
98093
98359
  /** Send from user input: enqueue if busy, otherwise send immediately. */
98094
98360
  function sendMessage(state, addEntry, input, options) {
98095
98361
  if (state.appState.isStreaming || state.appState.isCompacting) {
@@ -98789,36 +99055,71 @@ function endCompaction(state, tokensBefore, tokensAfter) {
98789
99055
  state.ui.requestRender();
98790
99056
  }
98791
99057
  //#endregion
99058
+ //#region src/tui/commands/skill-commands.ts
99059
+ const SKILL_ACTIVATION_SENTINEL_PREFIX = "__activate_skill__:";
99060
+ async function fetchSkills(client, sessionId) {
99061
+ const skills = (await client.listSkills(sessionId))?.skills ?? [];
99062
+ const out = [];
99063
+ for (const s of skills) {
99064
+ if (typeof s?.name !== "string" || s.name.length === 0) continue;
99065
+ if (typeof s.content !== "string") continue;
99066
+ out.push({
99067
+ name: s.name,
99068
+ content: s.content,
99069
+ ...typeof s.description === "string" ? { description: s.description } : {}
99070
+ });
99071
+ }
99072
+ return out;
99073
+ }
99074
+ function buildSkillCommand(skill) {
99075
+ return {
99076
+ name: `${SKILL_COMMAND_PREFIX}${skill.name}`,
99077
+ aliases: [],
99078
+ description: skill.description ?? "",
99079
+ mode: "both",
99080
+ async execute(args) {
99081
+ const trimmed = args.trim();
99082
+ const prompt = trimmed.length > 0 ? `${skill.content}\n\nUser request:\n${trimmed}` : skill.content;
99083
+ const payload = {
99084
+ name: skill.name,
99085
+ args: trimmed,
99086
+ prompt
99087
+ };
99088
+ return {
99089
+ type: "ok",
99090
+ message: `${SKILL_ACTIVATION_SENTINEL_PREFIX}${JSON.stringify(payload)}`
99091
+ };
99092
+ }
99093
+ };
99094
+ }
99095
+ //#endregion
98792
99096
  //#region src/tui/commands/skill-dispatch.ts
98793
99097
  /**
98794
- * Attempt to dispatch `/name args` as a skill activation.
99098
+ * 探测 `/name args` 是否对应某个 skill。命中返回 activate(含拼好的
99099
+ * prompt);未命中或查询失败返回 unknown,message 由调用方推到 transcript。
98795
99100
  */
98796
99101
  async function tryDispatchSkill(client, sessionId, name, args) {
98797
99102
  let skills;
98798
99103
  try {
98799
99104
  skills = (await client.listSkills(sessionId))?.skills ?? [];
98800
- } catch (err) {
99105
+ } catch (error) {
98801
99106
  return {
98802
- matched: false,
98803
- message: `Unknown command: /${name} (skill lookup failed: ${err instanceof Error ? err.message : String(err)})`
99107
+ kind: "unknown",
99108
+ message: `Unknown command: /${name} (skill lookup failed: ${error instanceof Error ? error.message : String(error)})`
98804
99109
  };
98805
99110
  }
98806
- if (!skills.some((s) => s.name === name)) return {
98807
- matched: false,
99111
+ const skill = skills.find((s) => s.name === name);
99112
+ if (skill === void 0 || typeof skill.content !== "string") return {
99113
+ kind: "unknown",
98808
99114
  message: `Unknown command: /${name}`
98809
99115
  };
98810
- try {
98811
- await client.activateSkill(sessionId, name, args);
98812
- return {
98813
- matched: true,
98814
- message: `Skill "${name}" activated.`
98815
- };
98816
- } catch (err) {
98817
- return {
98818
- matched: true,
98819
- message: `Skill "${name}" failed: ${err instanceof Error ? err.message : String(err)}`
98820
- };
98821
- }
99116
+ const trimmed = args.trim();
99117
+ return {
99118
+ kind: "activate",
99119
+ name,
99120
+ args: trimmed,
99121
+ prompt: trimmed.length > 0 ? `${skill.content}\n\nUser request:\n${trimmed}` : skill.content
99122
+ };
98822
99123
  }
98823
99124
  //#endregion
98824
99125
  //#region src/tui/commands/dispatch.ts
@@ -98827,7 +99128,12 @@ async function dispatchSlashCommand(input, state, buildCtx, addEntry) {
98827
99128
  if (!parsed) return false;
98828
99129
  const def = state.registry.find(parsed.name);
98829
99130
  if (!def) {
99131
+ const ctx = buildCtx();
98830
99132
  const result = await tryDispatchSkill(state.client, state.appState.sessionId, parsed.name, parsed.args);
99133
+ if (result.kind === "activate") {
99134
+ ctx.activateSkill(result.name, result.args, result.prompt);
99135
+ return true;
99136
+ }
98831
99137
  addEntry({
98832
99138
  id: `slash-${Date.now()}`,
98833
99139
  kind: "status",
@@ -98895,6 +99201,24 @@ async function dispatchSlashCommand(input, state, buildCtx, addEntry) {
98895
99201
  ctx.sendAsMessage(msg);
98896
99202
  return true;
98897
99203
  }
99204
+ if (result.message.startsWith("__activate_skill__:")) {
99205
+ const raw = result.message.slice(19);
99206
+ let payload;
99207
+ try {
99208
+ payload = JSON.parse(raw);
99209
+ } catch (error) {
99210
+ addEntry({
99211
+ id: `slash-err-${Date.now()}`,
99212
+ kind: "status",
99213
+ renderMode: "plain",
99214
+ content: `Skill activation failed: malformed payload (${error instanceof Error ? error.message : String(error)})`,
99215
+ color: state.colors.error
99216
+ });
99217
+ return true;
99218
+ }
99219
+ ctx.activateSkill(payload.name, payload.args, payload.prompt);
99220
+ return true;
99221
+ }
98898
99222
  addEntry({
98899
99223
  id: `slash-${Date.now()}`,
98900
99224
  kind: "status",
@@ -98986,7 +99310,7 @@ function handleContentDelta(ectx, data) {
98986
99310
  //#region src/tui/handlers/tool.ts
98987
99311
  const STREAMING_FIELD_RE = /"(path|file_path|command|pattern|query|url|description|title|name)"\s*:\s*"((?:\\.|[^"\\])*)"/g;
98988
99312
  function unescapeJsonString(s) {
98989
- return s.replace(/\\(["\\/bfnrt])/g, (_, ch) => {
99313
+ return s.replaceAll(/\\(["\\/bfnrt])/g, (_, ch) => {
98990
99314
  switch (ch) {
98991
99315
  case "n": return "\n";
98992
99316
  case "t": return " ";
@@ -99269,7 +99593,7 @@ var ChoicePickerComponent = class extends Container {
99269
99593
  super();
99270
99594
  this.opts = opts;
99271
99595
  const currentIdx = opts.options.findIndex((o) => o.value === opts.currentValue);
99272
- this.selectedIndex = currentIdx >= 0 ? currentIdx : 0;
99596
+ this.selectedIndex = Math.max(currentIdx, 0);
99273
99597
  }
99274
99598
  handleInput(data) {
99275
99599
  if (matchesKey(data, Key.escape)) {
@@ -99292,12 +99616,13 @@ var ChoicePickerComponent = class extends Container {
99292
99616
  }
99293
99617
  render(width) {
99294
99618
  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
99619
  const hint = this.opts.hint ?? "↑↓ navigate · Enter select · Esc cancel";
99299
- lines.push(chalk.hex(colors.textMuted)(` ${hint}`));
99300
- lines.push("");
99620
+ const lines = [
99621
+ chalk.hex(colors.primary)("".repeat(width)),
99622
+ chalk.hex(colors.primary).bold(` ${this.opts.title}`),
99623
+ chalk.hex(colors.textMuted)(` ${hint}`),
99624
+ ""
99625
+ ];
99301
99626
  for (let i = 0; i < this.opts.options.length; i++) {
99302
99627
  const opt = this.opts.options[i];
99303
99628
  const isSelected = i === this.selectedIndex;
@@ -99455,31 +99780,28 @@ var HelpPanelComponent = class extends Container {
99455
99780
  const muted = chalk.hex(c.textMuted);
99456
99781
  const kbdColor = chalk.hex(c.warning);
99457
99782
  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
99783
  const shortcuts = this.opts.shortcuts ?? DEFAULT_KEYBOARD_SHORTCUTS;
99466
99784
  const kbdWidth = Math.max(8, ...shortcuts.map((s) => s.keys.length));
99467
- for (const s of shortcuts) lines.push(` ${kbdColor(s.keys.padEnd(kbdWidth))} ${dim(s.description)}`);
99468
- lines.push("");
99469
- lines.push(` ${chalk.bold("Slash commands")}`);
99470
- const sortedCmds = [...this.opts.commands].sort((a, b) => a.name.localeCompare(b.name));
99785
+ const sortedCmds = [...this.opts.commands].toSorted((a, b) => a.name.localeCompare(b.name));
99471
99786
  const cmdLabels = sortedCmds.map((c) => {
99472
99787
  const aliases = c.aliases.length > 0 ? ` (${c.aliases.map((a) => "/" + a).join(", ")})` : "";
99473
99788
  return `/${c.name}${aliases}`;
99474
99789
  });
99475
99790
  const cmdWidth = Math.max(12, ...cmdLabels.map((l) => l.length));
99476
- for (let i = 0; i < sortedCmds.length; i++) {
99477
- const cmd = sortedCmds[i];
99478
- const label = cmdLabels[i];
99479
- lines.push(` ${slashColor(label.padEnd(cmdWidth))} ${dim(cmd.description)}`);
99480
- }
99481
- lines.push("");
99482
- lines.push(accent("".repeat(width)));
99791
+ const lines = [
99792
+ accent("─".repeat(width)),
99793
+ accent.bold(" help ") + muted("· Esc / Enter / q to close · ↑↓ scroll"),
99794
+ "",
99795
+ ` ${dim("Sure, Kimi is ready to help! Just send a message to get started.")}`,
99796
+ "",
99797
+ ` ${chalk.bold("Keyboard shortcuts")}`,
99798
+ ...shortcuts.map((s) => ` ${kbdColor(s.keys.padEnd(kbdWidth))} ${dim(s.description)}`),
99799
+ "",
99800
+ ` ${chalk.bold("Slash commands")}`,
99801
+ ...sortedCmds.map((cmd, i) => ` ${slashColor(cmdLabels[i].padEnd(cmdWidth))} ${dim(cmd.description)}`),
99802
+ "",
99803
+ accent("─".repeat(width))
99804
+ ];
99483
99805
  const content = lines.slice(1, lines.length - 1);
99484
99806
  const maxVisible = Math.max(5, this.opts.maxVisible ?? 24);
99485
99807
  if (content.length > maxVisible) {
@@ -99490,7 +99812,7 @@ var HelpPanelComponent = class extends Container {
99490
99812
  lines[0],
99491
99813
  ...slice,
99492
99814
  scrollInfo,
99493
- lines[lines.length - 1]
99815
+ lines.at(-1)
99494
99816
  ].map((line) => truncateToWidth(line, width));
99495
99817
  }
99496
99818
  this.scrollTop = 0;
@@ -99664,8 +99986,7 @@ var SessionPickerComponent = class extends Container {
99664
99986
  }
99665
99987
  render(width) {
99666
99988
  const colors = this.colors;
99667
- const lines = [];
99668
- lines.push(chalk.hex(colors.primary)("─".repeat(width)));
99989
+ const lines = [chalk.hex(colors.primary)("─".repeat(width))];
99669
99990
  if (this.loading) {
99670
99991
  lines.push(chalk.hex(colors.primary).bold("Sessions"));
99671
99992
  lines.push(chalk.hex(colors.textMuted)("Loading sessions..."));
@@ -100409,30 +100730,30 @@ function renderDisplayBlock(block, expanded) {
100409
100730
  if (block.danger !== void 0) lines.push(chalk.red.bold(`⚠ potentially destructive: ${block.danger}`));
100410
100731
  (block.command.length > 0 ? block.command.split("\n") : [""]).forEach((cmdLine, idx) => {
100411
100732
  const prefix = idx === 0 ? chalk.cyan("$") : chalk.dim("·");
100412
- lines.push(`${prefix} ${chalk.gray(cmdLine)}`);
100733
+ lines.push(`${prefix} ${chalk.whiteBright(cmdLine)}`);
100413
100734
  });
100414
100735
  if (block.description !== void 0 && block.description.length > 0) lines.push(` ${chalk.dim(block.description)}`);
100415
100736
  return lines;
100416
100737
  }
100417
100738
  case "file_op": {
100418
- const lines = [`${chalk.cyan(block.operation.padEnd(5))} ${chalk.gray(block.path)}`];
100739
+ const lines = [`${chalk.cyan(block.operation.padEnd(5))} ${chalk.whiteBright(block.path)}`];
100419
100740
  if (block.detail !== void 0 && block.detail.length > 0) lines.push(chalk.dim(block.detail));
100420
100741
  return lines;
100421
100742
  }
100422
- case "url_fetch": return [`${chalk.cyan((block.method ?? "GET").toUpperCase().padEnd(5))} ${chalk.gray(block.url)}`];
100743
+ case "url_fetch": return [`${chalk.cyan((block.method ?? "GET").toUpperCase().padEnd(5))} ${chalk.whiteBright(block.url)}`];
100423
100744
  case "search": {
100424
- const lines = [`${chalk.cyan("search")} ${chalk.gray(block.query)}`];
100745
+ const lines = [`${chalk.cyan("search")} ${chalk.whiteBright(block.query)}`];
100425
100746
  if (block.scope !== void 0 && block.scope.length > 0) lines.push(chalk.dim(`scope: ${block.scope}`));
100426
100747
  return lines;
100427
100748
  }
100428
100749
  case "invocation": {
100429
- const lines = [`${chalk.cyan(block.kind.padEnd(5))} ${chalk.gray(block.name)}`];
100750
+ const lines = [`${chalk.cyan(block.kind.padEnd(5))} ${chalk.whiteBright(block.name)}`];
100430
100751
  if (block.description !== void 0 && block.description.length > 0) lines.push(chalk.dim(truncateOneLine(block.description, 200)));
100431
100752
  return lines;
100432
100753
  }
100433
- case "brief": return block.text ? block.text.split("\n").map((line) => line.length > 0 ? chalk.gray(line) : "") : [];
100434
- case "background_task": return [chalk.gray(`${block.status} ${block.kind} task ${block.task_id}: ${block.description}`)];
100435
- case "todo": return block.items.map((item) => chalk.gray(`- [${item.status}] ${item.title}`));
100754
+ case "brief": return block.text ? block.text.split("\n").map((line) => line.length > 0 ? chalk.whiteBright(line) : "") : [];
100755
+ case "background_task": return [chalk.whiteBright(`${block.status} ${block.kind} task ${block.task_id}: ${block.description}`)];
100756
+ case "todo": return block.items.map((item) => chalk.whiteBright(`- [${item.status}] ${item.title}`));
100436
100757
  default: return [];
100437
100758
  }
100438
100759
  }
@@ -100452,9 +100773,13 @@ function isDuplicateBriefBlock(block, description) {
100452
100773
  const borderColor = chalk.yellow;
100453
100774
  const selectColor = chalk.cyan;
100454
100775
  function headerFor(toolName) {
100455
- if (toolName === "ExitPlanMode") return "Approve this plan?";
100456
- if (toolName === "EnterPlanMode") return "Enter plan mode?";
100457
- return `${toolName} need approve?`;
100776
+ switch (toolName) {
100777
+ case "Bash": return "Run this command?";
100778
+ case "Write": return "Write this file?";
100779
+ case "Edit": return "Apply these edits?";
100780
+ case "TaskStop": return "Stop this task?";
100781
+ default: return `Approve ${toolName}?`;
100782
+ }
100458
100783
  }
100459
100784
  var ApprovalPanelComponent = class extends Container {
100460
100785
  focused = false;
@@ -100565,7 +100890,7 @@ var ApprovalPanelComponent = class extends Container {
100565
100890
  const labelWithNum = `${String(num)}. ${option.label}`;
100566
100891
  if (this.feedbackMode && option.requires_feedback === true && isSelected) lines.push(indent(this.renderInlineFeedbackLine(width - 2, labelWithNum)));
100567
100892
  else if (isSelected) lines.push(indent(`${selectColor.bold("▶")} ${selectColor.bold(labelWithNum)}`));
100568
- else lines.push(indent(chalk.gray(` ${labelWithNum}`)));
100893
+ else lines.push(indent(chalk.whiteBright(` ${labelWithNum}`)));
100569
100894
  }
100570
100895
  lines.push("");
100571
100896
  if (this.feedbackMode) lines.push(indent(chalk.dim("Type feedback · ↵ submit.")));
@@ -101209,10 +101534,7 @@ function registerReverseRPCHandlers(state, client, callbacks) {
101209
101534
  showPanel: (payload) => showQuestionDialog(state, payload),
101210
101535
  hidePanel: () => hideQuestionDialog(state)
101211
101536
  });
101212
- const disposers = [];
101213
- disposers.push(client.onRequest("approval.request", createApprovalRequestHandler(state)));
101214
- disposers.push(client.onRequest("question.ask", createQuestionAskHandler(state)));
101215
- return disposers;
101537
+ return [client.onRequest("approval.request", createApprovalRequestHandler(state)), client.onRequest("question.ask", createQuestionAskHandler(state))];
101216
101538
  }
101217
101539
  //#endregion
101218
101540
  //#region src/utils/clipboard/clipboard-native.ts
@@ -101554,44 +101876,6 @@ function readUInt32BE(b, off) {
101554
101876
  return b[off] * 16777216 + (b[off + 1] << 16) + (b[off + 2] << 8) + b[off + 3] >>> 0;
101555
101877
  }
101556
101878
  //#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
101879
  //#region src/tui/components/editor/file-mention-provider.ts
101596
101880
  /**
101597
101881
  * `@file` autocomplete provider for the input box.
@@ -101701,7 +101985,7 @@ function rankForEmptyQuery(files, snapshot) {
101701
101985
  const result = [];
101702
101986
  const cap = MAX_SUGGESTIONS_WHEN_EMPTY;
101703
101987
  const inFiles = new Set(files);
101704
- const byRecency = [...snapshot.recencyOrder.entries()].filter(([path]) => inFiles.has(path)).sort((a, b) => a[1] - b[1]);
101988
+ const byRecency = [...snapshot.recencyOrder.entries()].filter(([path]) => inFiles.has(path)).toSorted((a, b) => a[1] - b[1]);
101705
101989
  for (const [path] of byRecency) {
101706
101990
  if (result.length >= cap) break;
101707
101991
  if (picked.has(path)) continue;
@@ -101709,7 +101993,7 @@ function rankForEmptyQuery(files, snapshot) {
101709
101993
  result.push(path);
101710
101994
  }
101711
101995
  if (result.length < cap) {
101712
- const byMtime = files.filter((p) => !picked.has(p) && snapshot.mtimeByPath.has(p)).sort((a, b) => (snapshot.mtimeByPath.get(b) ?? 0) - (snapshot.mtimeByPath.get(a) ?? 0));
101996
+ 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
101997
  for (const path of byMtime) {
101714
101998
  if (result.length >= cap) break;
101715
101999
  picked.add(path);
@@ -101717,7 +102001,7 @@ function rankForEmptyQuery(files, snapshot) {
101717
102001
  }
101718
102002
  }
101719
102003
  if (result.length < cap) {
101720
- const rest = files.filter((p) => !picked.has(p)).sort((a, b) => basename(a).localeCompare(basename(b)) || a.localeCompare(b));
102004
+ const rest = files.filter((p) => !picked.has(p)).toSorted((a, b) => basename(a).localeCompare(basename(b)) || a.localeCompare(b));
101721
102005
  for (const path of rest) {
101722
102006
  if (result.length >= cap) break;
101723
102007
  result.push(path);
@@ -102238,6 +102522,7 @@ var KimiTUI = class {
102238
102522
  showUsage(this.state);
102239
102523
  },
102240
102524
  sendAsMessage: (text) => sendMessage(this.state, (e) => this.addEntry(e), text),
102525
+ activateSkill: (name, args, fullPrompt) => sendSkillActivation(this.state, (e) => this.addEntry(e), name, args, fullPrompt),
102241
102526
  performReload: (action) => performReload(this.state, action, this.buildInputHooks())
102242
102527
  };
102243
102528
  }
@@ -102285,9 +102570,9 @@ async function runShell(opts, version) {
102285
102570
  isReplaying: false,
102286
102571
  streamingPhase: "idle",
102287
102572
  streamingStartTime: 0,
102288
- theme: ctx.theme,
102573
+ theme: "dark",
102289
102574
  version,
102290
- editorCommand: ctx.defaultEditor.length > 0 ? ctx.defaultEditor : null,
102575
+ editorCommand: null,
102291
102576
  availableModels: ctx.availableModels,
102292
102577
  sessionTitle: null
102293
102578
  };
@@ -102421,7 +102706,7 @@ async function promptForInstallConfirmation(options) {
102421
102706
  const output = options.output ?? process.stdout;
102422
102707
  const choices = createInstallPromptChoices(options.target);
102423
102708
  let selectedIndex = getDefaultInstallPromptSelection(choices);
102424
- return await new Promise((resolve) => {
102709
+ return new Promise((resolve) => {
102425
102710
  let lineCount = 0;
102426
102711
  const hadRawMode = "isRaw" in input ? input.isRaw === true : false;
102427
102712
  const canSetRawMode = typeof input.setRawMode === "function";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kfc-code-cli",
3
- "version": "0.0.1-alpha.7",
3
+ "version": "0.0.1-alpha.9",
4
4
  "description": "KFC crazy ",
5
5
  "license": "MIT",
6
6
  "bin": {