kimiflare 0.30.0 → 0.31.0

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/index.js CHANGED
@@ -1978,15 +1978,132 @@ You do not address the user. If you must reference what you're about to ask the
1978
1978
  - Continuing to search after the decision can already be made.
1979
1979
  - Hiding uncertainty inside confident prose.
1980
1980
 
1981
- When in doubt, deliver the smaller artifact sooner. When your Brief is complete, call the hand_off tool to pass your findings to the Coding Agent.
1981
+ When in doubt, deliver the smaller artifact sooner.
1982
+
1983
+ # Critical hand-off rule
1984
+
1985
+ When your Brief is complete, you MUST call the hand_off tool to transfer control to the next agent. Simply saying you have handed off is NOT sufficient \u2014 the tool call is required. If you do not call hand_off, your work will be stranded and the next agent will never run.
1986
+
1987
+ You MUST include the full Brief text in your final assistant message BEFORE calling the hand_off tool. The next agent receives your last assistant message in its entirety \u2014 no summarization, no truncation. If you produce the Brief in one message and then call hand_off in a separate message with only "Handing off now," the next agent will see only "Handing off now" and will not know what to implement.
1988
+
1989
+ Correct: One assistant message containing the full Brief + the hand_off tool call.
1990
+ Incorrect: Brief in message N, then "Handing off" + hand_off in message N+1.
1991
+ Incorrect: Saying "I have handed off" without calling the hand_off tool.
1982
1992
 
1983
1993
  `;
1984
1994
  case "coding":
1985
- return `You are the Coding Agent of kimiflare. You write code, edit files, and execute tools directly. Do not ask the user to do your work for you. Implement the changes yourself.
1995
+ return `You are the Coding Agent in kimiflare. You write, modify, debug, and reason about code. You receive tasks from the General Agent or research briefs from the Research Agent. Your audience is sometimes the user directly, sometimes another agent.
1996
+
1997
+ # Your job
1998
+
1999
+ Implement the task as scoped. Correctly, narrowly, and in a way that fits the codebase you're working in. Stop when it's done.
2000
+
2001
+ # How to think
2002
+
2003
+ 1. Read before you write. Look at the existing code \u2014 patterns, utilities, conventions, naming. Match the codebase's style, don't impose your own. The repo should look like one author wrote it even after you've worked in it.
2004
+
2005
+ 2. Stay in scope. Touch what the task requires and nothing else. If you notice something else worth fixing, mention it \u2014 don't fix it uninvited. Scope creep is the most common way coding agents make things worse.
2006
+
2007
+ 3. Trust the runtime. When something doesn't work, run it, read the actual error, and update your understanding. Don't argue with reality based on what the docs or types said. The runtime is the source of truth.
2008
+
2009
+ 4. Be honest about uncertainty before acting, not after. "I'm going to try X \u2014 if it fails I'll try Y" is right. Confident execution followed by silent breakage is wrong.
2010
+
2011
+ 5. Ask only when ambiguity is load-bearing. If a choice would meaningfully change the result and you can't infer the user's intent, ask. If it's a trivial choice, make it and move on.
2012
+
2013
+ 6. Done means done. Working, fitting the codebase, tests passing where applicable, loose ends named. Not "the command exited zero." Don't claim done when you only have passing.
2014
+
2015
+ # Working style
2016
+
2017
+ - Small, verifiable steps over large speculative ones.
2018
+ - Run the code. Read the output. Believe the output.
2019
+ - Prefer existing utilities over new ones. Prefer the codebase's patterns over your defaults.
2020
+ - New dependencies are a real cost. Justify them or skip them.
2021
+ - Comments narrate why, not what. If the code needs a comment to explain what it does, the code is probably wrong.
2022
+
2023
+ # Voice
2024
+
2025
+ Direct. No throat-clearing, no narration of obvious steps, no celebration of completion. When you explain something, explain only what isn't already visible in the code or output.
2026
+
2027
+ # Output
2028
+
2029
+ Show the work \u2014 the diff, the file, the command output \u2014 and a one- or two-line summary of what you did and anything the next agent or the user should know. That's it. No "I hope this helps." No "let me know if you'd like me to..."
2030
+
2031
+ If something didn't work or you couldn't finish cleanly, say so plainly with what you tried and what you'd try next.
2032
+
2033
+ # Things that are not your job
2034
+
2035
+ - Investigating broad questions (Research Agent's job).
2036
+ - Routing or chatting (General Agent's job).
2037
+ - Improving the codebase beyond the task at hand.
2038
+ - Producing long explanations of code the reader can read.
2039
+
2040
+ # Receiving work from the Research Agent
2041
+
2042
+ When you are activated after a Research Agent hand-off, the full Research Brief is included in the system message that precedes your turn. Read it carefully \u2014 it contains the decision, findings, recommendation, confidence levels, open questions, and risks. Do not ask the user to repeat what the Research Agent already determined.
2043
+
2044
+ When your implementation is complete, you MUST call the hand_off tool to return to the General Agent. Simply saying you are done is NOT sufficient \u2014 the tool call is required. If you do not call hand_off, your work will be stranded and the General Agent will never run.
1986
2045
 
1987
2046
  `;
1988
2047
  case "generalist":
1989
- return `You are the Generalist Agent of kimiflare. You handle conversational queries, memory management, and high-level task coordination.
2048
+ return `You are the General Agent in kimiflare. You are the user's primary point of contact. Behind you are two specialists: the Research Agent (investigation, analysis, synthesis) and the Coding Agent (writing, modifying, and reasoning about code).
2049
+
2050
+ # Your job
2051
+
2052
+ Triage. Route. Stay out of the way. Handle small stuff. Present specialist work cleanly.
2053
+
2054
+ You are fast and light by design. Substantive thinking is not your job \u2014 it's the specialists' job. Your job is to recognize what kind of help the user needs and get them to the right agent quickly, or to handle the request yourself if it's small enough that routing would be overkill.
2055
+
2056
+ # How to think
2057
+
2058
+ 1. Default to routing. If a request involves real investigation, real synthesis, or real code work, call hand_off to the appropriate specialist. Do not try to answer it yourself just because you can produce something plausible-sounding.
2059
+
2060
+ 2. Route on partial information. You don't need to fully understand the request before routing \u2014 the specialist will ask follow-ups if needed. Spending three turns clarifying before handoff is worse than handing off now and letting the specialist clarify.
2061
+
2062
+ 3. Handle the small stuff yourself. Greetings, clarifications, "what can you do," confirming what just happened, one-line factual answers, formatting preferences, scope adjustments \u2014 these don't need a specialist. Be quick.
2063
+
2064
+ 4. Notice escalation. A conversation that started small can become a research or coding task. When it does, route. Don't keep answering out of inertia.
2065
+
2066
+ 5. Do not editorialize the specialists' output. When work comes back from Research or Coding, present it. Don't summarize it back at the user with your own framing on top. The user can read.
2067
+
2068
+ # Routing rules
2069
+
2070
+ Call hand_off to Research Agent when the user wants:
2071
+ - Information you don't already have, or that may have changed.
2072
+ - Comparison, evaluation, or recommendation between options.
2073
+ - Synthesis across multiple sources.
2074
+ - Investigation of an unfamiliar codebase or library.
2075
+ - Anything where being wrong has real cost.
2076
+
2077
+ Call hand_off to Coding Agent when the user wants:
2078
+ - Code written, modified, debugged, or reviewed.
2079
+ - A file created, edited, or restructured.
2080
+ - A concrete build/run/test action taken.
2081
+
2082
+ Handle yourself when:
2083
+ - The user is making conversation.
2084
+ - The user is asking what you (collectively) can do.
2085
+ - The answer is one line and you're confident.
2086
+ - The user is correcting or adjusting a previous handoff.
2087
+ - Work has come back from a specialist and you're presenting it to the user.
2088
+
2089
+ When in doubt, route. The cost of an unnecessary handoff is small. The cost of you confidently producing wrong work is large.
2090
+
2091
+ # Voice
2092
+
2093
+ Warm, quick, natural. Short sentences. No corporate softeners, no "I'd be happy to," no "great question." Talk like a competent person who respects the user's time.
2094
+
2095
+ # Handoff style
2096
+
2097
+ When you route, say so plainly in one line. "Handing this to the research agent \u2014 back in a moment." or "Coding agent will take this one." Then stop. Don't fill the wait with chatter.
2098
+
2099
+ # Things that are not your job
2100
+
2101
+ - Producing research findings.
2102
+ - Writing or analyzing code.
2103
+ - Synthesizing across many sources.
2104
+ - Long explanations of anything.
2105
+
2106
+ If you find yourself drafting a long response, stop and ask whether this should have been routed. Usually it should have been.
1990
2107
 
1991
2108
  `;
1992
2109
  default:
@@ -5852,6 +5969,7 @@ var init_agent_session = __esm({
5852
5969
  CODING_TOOL_NAMES = [
5853
5970
  "bash",
5854
5971
  "edit",
5972
+ "hand_off",
5855
5973
  "lsp_codeAction",
5856
5974
  "lsp_definition",
5857
5975
  "lsp_diagnostics",
@@ -5868,6 +5986,7 @@ var init_agent_session = __esm({
5868
5986
  "write"
5869
5987
  ].sort((a, b) => a.localeCompare(b));
5870
5988
  GENERALIST_TOOL_NAMES = [
5989
+ "hand_off",
5871
5990
  "memory_forget",
5872
5991
  "memory_recall",
5873
5992
  "memory_remember",
@@ -6095,6 +6214,18 @@ var init_orchestrator = __esm({
6095
6214
  }
6096
6215
  return null;
6097
6216
  }
6217
+ /** Extract the last assistant message content from a session.
6218
+ * Returns the full text in its entirety — no truncation, no summarization.
6219
+ * If the agent has no text content, falls back to synthesis. */
6220
+ extractDeliverable(session) {
6221
+ for (let i = session.messages.length - 1; i >= 0; i--) {
6222
+ const m = session.messages[i];
6223
+ if (m.role === "assistant" && typeof m.content === "string" && m.content.trim().length > 0) {
6224
+ return m.content;
6225
+ }
6226
+ }
6227
+ return null;
6228
+ }
6098
6229
  getToolsForRole(role) {
6099
6230
  const base = getAgentTools(role, this.opts.customAgents);
6100
6231
  if (this.opts.lspTools.length > 0) {
@@ -6252,17 +6383,116 @@ ${summary}`
6252
6383
  session.recentToolCalls = session.recentToolCalls.slice(-8);
6253
6384
  const handOffTarget = this.detectHandOff(session.messages);
6254
6385
  if (handOffTarget && handOffTarget !== this.activeRole) {
6255
- const summary = await this.synthesizeHandoff(this.activeRole, handOffTarget);
6386
+ const fromRole = this.activeRole;
6387
+ const fromSession = this.sessions.get(fromRole);
6388
+ const deliverable = this.extractDeliverable(fromSession);
6389
+ let handoffContent = "";
6390
+ if (deliverable) {
6391
+ handoffContent = `[hand-off from ${fromRole} agent \u2014 agent requested hand-off]
6392
+
6393
+ The ${fromRole} agent completed its work. Here is their full deliverable:
6394
+
6395
+ ${deliverable}`;
6396
+ } else {
6397
+ const summary = await this.synthesizeHandoff(fromRole, handOffTarget);
6398
+ handoffContent = `[hand-off from ${fromRole} agent \u2014 agent requested hand-off]
6399
+ ${summary}`;
6400
+ }
6256
6401
  this.activeRole = handOffTarget;
6257
6402
  const newSession = this.getActiveSession();
6258
- if (summary) {
6403
+ if (handoffContent) {
6259
6404
  newSession.messages.push({
6260
6405
  role: "system",
6261
- content: `[hand-off from ${this.activeRole} agent \u2014 agent requested hand-off]
6262
- ${summary}`
6406
+ content: handoffContent
6263
6407
  });
6264
6408
  }
6409
+ newSession.messages.push(userMessage);
6265
6410
  this.turnCounts.set(handOffTarget, 0);
6411
+ const targetTools = this.getToolsForRole(this.activeRole);
6412
+ const targetCustomAgent = this.opts.customAgents?.find((a) => a.name === this.activeRole);
6413
+ const targetConfig = this.resolveAgentConfig(this.activeRole);
6414
+ const targetModel = targetCustomAgent?.model ?? targetConfig.model ?? this.opts.model;
6415
+ const targetReasoning = targetCustomAgent?.reasoningEffort ?? targetConfig.reasoningEffort ?? this.opts.reasoningEffort;
6416
+ if (targetCustomAgent?.systemPrompt && !newSession.messages.some((m) => m.role === "system" && m.content === targetCustomAgent.systemPrompt)) {
6417
+ newSession.messages.unshift({
6418
+ role: "system",
6419
+ content: targetCustomAgent.systemPrompt
6420
+ });
6421
+ }
6422
+ await runAgentTurn({
6423
+ accountId: this.opts.accountId,
6424
+ apiToken: this.opts.apiToken,
6425
+ model: targetModel,
6426
+ gateway: this.opts.gateway,
6427
+ messages: newSession.messages,
6428
+ tools: [...targetTools, ...this.opts.mcpTools],
6429
+ executor: this.opts.executor,
6430
+ cwd: this.opts.cwd,
6431
+ signal: this.opts.signal,
6432
+ reasoningEffort: targetReasoning,
6433
+ coauthor: this.opts.coauthor,
6434
+ sessionId: this.opts.sessionId,
6435
+ memoryManager: this.opts.memoryManager,
6436
+ keepLastImageTurns: this.opts.keepLastImageTurns,
6437
+ codeMode: this.opts.codeMode,
6438
+ onFileChange: this.opts.onFileChange,
6439
+ callbacks: this.opts.callbacks,
6440
+ recentToolCalls: newSession.recentToolCalls,
6441
+ agentRole: this.activeRole
6442
+ });
6443
+ newSession.recentToolCalls = newSession.recentToolCalls.slice(-8);
6444
+ }
6445
+ if (!handOffTarget && this.activeRole !== "generalist") {
6446
+ const deliverable = this.extractDeliverable(session);
6447
+ if (deliverable && deliverable.length >= 300) {
6448
+ const fromRole = this.activeRole;
6449
+ const handoffContent = `[hand-off from ${fromRole} agent \u2014 auto-detected completion (hand_off tool was not called)]
6450
+
6451
+ The ${fromRole} agent completed its work. Here is their full deliverable:
6452
+
6453
+ ${deliverable}`;
6454
+ this.activeRole = "generalist";
6455
+ const newSession = this.getActiveSession();
6456
+ newSession.messages.push({
6457
+ role: "system",
6458
+ content: handoffContent
6459
+ });
6460
+ newSession.messages.push(userMessage);
6461
+ this.turnCounts.set("generalist", 0);
6462
+ const targetTools = this.getToolsForRole(this.activeRole);
6463
+ const targetCustomAgent = this.opts.customAgents?.find((a) => a.name === this.activeRole);
6464
+ const targetConfig = this.resolveAgentConfig(this.activeRole);
6465
+ const targetModel = targetCustomAgent?.model ?? targetConfig.model ?? this.opts.model;
6466
+ const targetReasoning = targetCustomAgent?.reasoningEffort ?? targetConfig.reasoningEffort ?? this.opts.reasoningEffort;
6467
+ if (targetCustomAgent?.systemPrompt && !newSession.messages.some((m) => m.role === "system" && m.content === targetCustomAgent.systemPrompt)) {
6468
+ newSession.messages.unshift({
6469
+ role: "system",
6470
+ content: targetCustomAgent.systemPrompt
6471
+ });
6472
+ }
6473
+ await runAgentTurn({
6474
+ accountId: this.opts.accountId,
6475
+ apiToken: this.opts.apiToken,
6476
+ model: targetModel,
6477
+ gateway: this.opts.gateway,
6478
+ messages: newSession.messages,
6479
+ tools: [...targetTools, ...this.opts.mcpTools],
6480
+ executor: this.opts.executor,
6481
+ cwd: this.opts.cwd,
6482
+ signal: this.opts.signal,
6483
+ reasoningEffort: targetReasoning,
6484
+ coauthor: this.opts.coauthor,
6485
+ sessionId: this.opts.sessionId,
6486
+ memoryManager: this.opts.memoryManager,
6487
+ keepLastImageTurns: this.opts.keepLastImageTurns,
6488
+ codeMode: this.opts.codeMode,
6489
+ onFileChange: this.opts.onFileChange,
6490
+ callbacks: this.opts.callbacks,
6491
+ recentToolCalls: newSession.recentToolCalls,
6492
+ agentRole: this.activeRole
6493
+ });
6494
+ newSession.recentToolCalls = newSession.recentToolCalls.slice(-8);
6495
+ }
6266
6496
  }
6267
6497
  this.turnCounts.set(this.activeRole, (this.turnCounts.get(this.activeRole) ?? 0) + 1);
6268
6498
  }
@@ -10404,6 +10634,45 @@ var init_renderer2 = __esm({
10404
10634
  }
10405
10635
  });
10406
10636
 
10637
+ // src/commands/builtins.ts
10638
+ var BUILTIN_COMMANDS, BUILTIN_COMMAND_NAMES;
10639
+ var init_builtins = __esm({
10640
+ "src/commands/builtins.ts"() {
10641
+ "use strict";
10642
+ BUILTIN_COMMANDS = [
10643
+ { name: "help", description: "Show keybindings and command list", source: "builtin" },
10644
+ { name: "model", description: "Show current model", source: "builtin" },
10645
+ { name: "mode", argHint: "edit|plan|auto", description: "Switch agent mode", source: "builtin" },
10646
+ { name: "plan", description: "Switch to plan mode", source: "builtin" },
10647
+ { name: "auto", description: "Switch to auto mode", source: "builtin" },
10648
+ { name: "edit", description: "Switch to edit mode", source: "builtin" },
10649
+ { name: "thinking", argHint: "low|medium|high", description: "Set reasoning effort", source: "builtin" },
10650
+ { name: "effort", argHint: "low|medium|high", description: "Alias for /thinking", source: "builtin" },
10651
+ { name: "reasoning", description: "Toggle reasoning visibility", source: "builtin" },
10652
+ { name: "theme", description: "Open theme picker", source: "builtin" },
10653
+ { name: "agent", argHint: "[on|off|status]", description: "Multi-agent mode controls", source: "builtin" },
10654
+ { name: "memory", argHint: "[on|off|clear|search ...]", description: "Manage memory", source: "builtin" },
10655
+ { name: "cost", argHint: "[on|off]", description: "Show cost report or toggle attribution", source: "builtin" },
10656
+ { name: "gateway", argHint: "[status|off|<id>|cache-ttl|skip-cache|...]", description: "Manage AI Gateway", source: "builtin" },
10657
+ { name: "mcp", argHint: "[list|reload]", description: "Manage MCP servers", source: "builtin" },
10658
+ { name: "lsp", argHint: "[config|list|reload|scope]", description: "Manage language servers", source: "builtin" },
10659
+ { name: "command", argHint: "[create|edit|delete|list]", description: "Manage custom slash commands", source: "builtin" },
10660
+ { name: "resume", description: "Pick a past conversation to resume", source: "builtin" },
10661
+ { name: "compact", description: "Summarize old turns to free context", source: "builtin" },
10662
+ { name: "clear", description: "Clear current conversation", source: "builtin" },
10663
+ { name: "init", description: "Scan repo and write KIMI.md", source: "builtin" },
10664
+ { name: "update", description: "Check for updates", source: "builtin" },
10665
+ { name: "hello", description: "Send a voice note to the creator", source: "builtin" },
10666
+ { name: "logout", description: "Clear stored credentials", source: "builtin" },
10667
+ { name: "exit", description: "Exit kimiflare", source: "builtin" },
10668
+ { name: "quit", description: "Alias for /exit", source: "builtin" }
10669
+ ];
10670
+ BUILTIN_COMMAND_NAMES = new Set(
10671
+ BUILTIN_COMMANDS.map((c) => c.name.toLowerCase())
10672
+ );
10673
+ }
10674
+ });
10675
+
10407
10676
  // src/commands/save.ts
10408
10677
  import { mkdir as mkdir9, writeFile as writeFile9, unlink as unlink2 } from "fs/promises";
10409
10678
  import { dirname as dirname6 } from "path";
@@ -11565,6 +11834,125 @@ var init_file_picker = __esm({
11565
11834
  }
11566
11835
  });
11567
11836
 
11837
+ // src/ui/slash-picker.tsx
11838
+ import { Box as Box18, Text as Text19 } from "ink";
11839
+ import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
11840
+ function sourceBadge(source) {
11841
+ if (source === "builtin") return "";
11842
+ if (source === "project") return "project";
11843
+ return "global";
11844
+ }
11845
+ function commandLabel(item) {
11846
+ return `/${item.name}${item.argHint ? ` ${item.argHint}` : ""}`;
11847
+ }
11848
+ function SlashPicker({ items, selectedIndex, theme, query }) {
11849
+ let startIndex = 0;
11850
+ if (selectedIndex >= VISIBLE_LIMIT2) {
11851
+ startIndex = selectedIndex - VISIBLE_LIMIT2 + 1;
11852
+ }
11853
+ const visible = items.slice(startIndex, startIndex + VISIBLE_LIMIT2);
11854
+ const hasMoreAbove = startIndex > 0;
11855
+ const hasMoreBelow = items.length > startIndex + VISIBLE_LIMIT2;
11856
+ const longestLabel = visible.reduce((m, it) => Math.max(m, commandLabel(it).length), 0);
11857
+ const nameColWidth = Math.max(NAME_COL_MIN_WIDTH, longestLabel + NAME_DESC_GAP);
11858
+ return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
11859
+ /* @__PURE__ */ jsx19(Text19, { color: theme.accent, bold: true, children: query ? `Commands matching "/${query}"` : "Slash commands" }),
11860
+ /* @__PURE__ */ jsx19(Text19, { color: theme.info.color, children: "Arrow keys to navigate, Enter to select, Esc to cancel." }),
11861
+ /* @__PURE__ */ jsxs18(Box18, { marginTop: 1, flexDirection: "column", children: [
11862
+ visible.length === 0 && /* @__PURE__ */ jsx19(Text19, { color: theme.info.color, dimColor: true, children: "No matches" }),
11863
+ hasMoreAbove && /* @__PURE__ */ jsxs18(Text19, { color: theme.info.color, dimColor: true, children: [
11864
+ "\u2026 ",
11865
+ startIndex,
11866
+ " more above"
11867
+ ] }),
11868
+ visible.map((item, i) => {
11869
+ const actualIndex = startIndex + i;
11870
+ const isSelected = actualIndex === selectedIndex;
11871
+ const nameCol = commandLabel(item).padEnd(nameColWidth);
11872
+ const badge = sourceBadge(item.source);
11873
+ return /* @__PURE__ */ jsxs18(Text19, { color: isSelected ? theme.accent : void 0, bold: isSelected, children: [
11874
+ isSelected ? "\u203A " : " ",
11875
+ nameCol,
11876
+ /* @__PURE__ */ jsxs18(Text19, { color: theme.info.color, dimColor: true, children: [
11877
+ item.description,
11878
+ badge && ` [${badge}]`
11879
+ ] })
11880
+ ] }, item.name);
11881
+ }),
11882
+ hasMoreBelow && /* @__PURE__ */ jsxs18(Text19, { color: theme.info.color, dimColor: true, children: [
11883
+ "\u2026 ",
11884
+ items.length - (startIndex + VISIBLE_LIMIT2),
11885
+ " more below"
11886
+ ] })
11887
+ ] })
11888
+ ] });
11889
+ }
11890
+ var VISIBLE_LIMIT2, NAME_COL_MIN_WIDTH, NAME_DESC_GAP;
11891
+ var init_slash_picker = __esm({
11892
+ "src/ui/slash-picker.tsx"() {
11893
+ "use strict";
11894
+ VISIBLE_LIMIT2 = 7;
11895
+ NAME_COL_MIN_WIDTH = 18;
11896
+ NAME_DESC_GAP = 2;
11897
+ }
11898
+ });
11899
+
11900
+ // src/util/fuzzy.ts
11901
+ function fuzzyMatch(query, text) {
11902
+ const q = query.toLowerCase();
11903
+ const t = text.toLowerCase();
11904
+ if (q.length === 0) return { matches: true, score: 0 };
11905
+ if (q.length > t.length) return { matches: false, score: 0 };
11906
+ let qi = 0;
11907
+ let score = 0;
11908
+ let lastMatch = -1;
11909
+ let consecutive = 0;
11910
+ for (let i = 0; i < t.length && qi < q.length; i++) {
11911
+ if (t[i] !== q[qi]) continue;
11912
+ const isWordBoundary = i === 0 || /[\s\-_./:]/.test(t[i - 1]);
11913
+ if (lastMatch === i - 1) {
11914
+ consecutive++;
11915
+ score -= consecutive * 5;
11916
+ } else {
11917
+ consecutive = 0;
11918
+ if (lastMatch >= 0) score += (i - lastMatch - 1) * 2;
11919
+ }
11920
+ if (isWordBoundary) score -= 10;
11921
+ score += i * 0.1;
11922
+ lastMatch = i;
11923
+ qi++;
11924
+ }
11925
+ if (qi < q.length) return { matches: false, score: 0 };
11926
+ return { matches: true, score };
11927
+ }
11928
+ function fuzzyFilter(items, query, getText) {
11929
+ const trimmed = query.trim();
11930
+ if (trimmed.length === 0) return items;
11931
+ const tokens = trimmed.split(/\s+/);
11932
+ const scored = [];
11933
+ for (const item of items) {
11934
+ const text = getText(item);
11935
+ let total = 0;
11936
+ let allMatch = true;
11937
+ for (const token of tokens) {
11938
+ const m = fuzzyMatch(token, text);
11939
+ if (!m.matches) {
11940
+ allMatch = false;
11941
+ break;
11942
+ }
11943
+ total += m.score;
11944
+ }
11945
+ if (allMatch) scored.push({ item, score: total });
11946
+ }
11947
+ scored.sort((a, b) => a.score - b.score);
11948
+ return scored.map((s) => s.item);
11949
+ }
11950
+ var init_fuzzy = __esm({
11951
+ "src/util/fuzzy.ts"() {
11952
+ "use strict";
11953
+ }
11954
+ });
11955
+
11568
11956
  // src/cost-attribution/tui-report.ts
11569
11957
  var tui_report_exports = {};
11570
11958
  __export(tui_report_exports, {
@@ -11657,11 +12045,13 @@ var app_exports = {};
11657
12045
  __export(app_exports, {
11658
12046
  buildFilePickerIgnoreList: () => buildFilePickerIgnoreList,
11659
12047
  filterPickerItems: () => filterPickerItems,
12048
+ insertSlashCommand: () => insertSlashCommand,
11660
12049
  renderApp: () => renderApp,
11661
- shouldOpenMentionPicker: () => shouldOpenMentionPicker
12050
+ shouldOpenMentionPicker: () => shouldOpenMentionPicker,
12051
+ shouldOpenSlashPicker: () => shouldOpenSlashPicker
11662
12052
  });
11663
12053
  import React12, { useState as useState9, useRef as useRef3, useEffect as useEffect4, useCallback } from "react";
11664
- import { Box as Box18, Text as Text19, useApp, useInput as useInput5, render } from "ink";
12054
+ import { Box as Box19, Text as Text20, useApp, useInput as useInput5, render } from "ink";
11665
12055
  import SelectInput8 from "ink-select-input";
11666
12056
  import { existsSync as existsSync2, statSync as statSync3 } from "fs";
11667
12057
  import { join as join17 } from "path";
@@ -11670,7 +12060,7 @@ import { spawn as spawn4 } from "child_process";
11670
12060
  import { platform as platform2 } from "os";
11671
12061
  import fg4 from "fast-glob";
11672
12062
  import { readFileSync as readFileSync3 } from "fs";
11673
- import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
12063
+ import { jsx as jsx20, jsxs as jsxs19 } from "react/jsx-runtime";
11674
12064
  function buildFilePickerIgnoreList(cwd) {
11675
12065
  const hardcoded = [
11676
12066
  // Dependencies
@@ -11779,6 +12169,18 @@ function shouldOpenMentionPicker(input, cursorOffset, pickerCancelOffset) {
11779
12169
  }
11780
12170
  return false;
11781
12171
  }
12172
+ function shouldOpenSlashPicker(input, cursorOffset, cancelOffset) {
12173
+ if (cancelOffset === cursorOffset) return false;
12174
+ if (cursorOffset === 0 || input[cursorOffset - 1] !== "/") return false;
12175
+ return /^\s*$/.test(input.slice(0, cursorOffset - 1));
12176
+ }
12177
+ function insertSlashCommand(input, anchor, name) {
12178
+ let tokenEnd = anchor + 1;
12179
+ while (tokenEnd < input.length && !/\s/.test(input[tokenEnd])) tokenEnd++;
12180
+ const head = input.slice(0, anchor + 1) + name;
12181
+ const tail = " " + input.slice(tokenEnd).replace(/^\s+/, "");
12182
+ return { value: head + tail, cursor: head.length + 1 };
12183
+ }
11782
12184
  function gatewayFromConfig(cfg) {
11783
12185
  if (!cfg.aiGatewayId) return void 0;
11784
12186
  return {
@@ -11913,9 +12315,10 @@ function App({
11913
12315
  const [hasUpdate, setHasUpdate] = useState9(initialUpdateResult?.hasUpdate ?? false);
11914
12316
  const [latestVersion, setLatestVersion] = useState9(initialUpdateResult?.latestVersion ?? null);
11915
12317
  const [cursorOffset, setCursorOffset] = useState9(0);
11916
- const [pickerItems, setPickerItems] = useState9([]);
11917
- const [pickerSelected, setPickerSelected] = useState9(0);
11918
- const [pickerAnchor, setPickerAnchor] = useState9(null);
12318
+ const [activePicker, setActivePicker] = useState9(null);
12319
+ const [filePickerItems, setFilePickerItems] = useState9([]);
12320
+ const filePickerLoadedRef = useRef3(false);
12321
+ const [customCommandsVersion, setCustomCommandsVersion] = useState9(0);
11919
12322
  const cacheStableRef = useRef3(initialCfg?.cacheStablePrompts !== false);
11920
12323
  const messagesRef = useRef3(
11921
12324
  makePrefixMessages(cacheStableRef.current, cfg?.model ?? DEFAULT_MODEL, "edit", ALL_TOOLS)
@@ -11951,28 +12354,42 @@ function App({
11951
12354
  const flushTimeoutRef = useRef3(null);
11952
12355
  const customCommandsRef = useRef3([]);
11953
12356
  const pickerCancelRef = useRef3(null);
11954
- const mentionState = React12.useMemo(() => {
12357
+ const pickerAnchor = activePicker?.anchor ?? null;
12358
+ const pickerKind = activePicker?.kind ?? null;
12359
+ const pickerQuery = React12.useMemo(() => {
11955
12360
  if (pickerAnchor === null) return null;
11956
- const query = input.slice(pickerAnchor + 1, cursorOffset);
11957
- return { query, anchor: pickerAnchor };
12361
+ return input.slice(pickerAnchor + 1, cursorOffset);
11958
12362
  }, [input, cursorOffset, pickerAnchor]);
11959
- const filteredPickerItems = React12.useMemo(() => {
11960
- if (!mentionState) return [];
11961
- return filterPickerItems(pickerItems, mentionState.query);
11962
- }, [pickerItems, mentionState]);
12363
+ const filteredFileItems = React12.useMemo(() => {
12364
+ if (pickerKind !== "file" || pickerQuery === null) return [];
12365
+ return filterPickerItems(filePickerItems, pickerQuery);
12366
+ }, [pickerKind, filePickerItems, pickerQuery]);
12367
+ const allSlashCommands = React12.useMemo(() => {
12368
+ const customs = customCommandsRef.current.filter((c) => !BUILTIN_COMMAND_NAMES.has(c.name.toLowerCase())).map((c) => ({
12369
+ name: c.name,
12370
+ description: c.description ?? "",
12371
+ source: c.source
12372
+ }));
12373
+ return [...BUILTIN_COMMANDS, ...customs];
12374
+ }, [customCommandsVersion]);
12375
+ const filteredSlashItems = React12.useMemo(() => {
12376
+ if (pickerKind !== "slash" || pickerQuery === null) return [];
12377
+ return fuzzyFilter(allSlashCommands, pickerQuery, (c) => c.name).slice(0, 50);
12378
+ }, [pickerKind, allSlashCommands, pickerQuery]);
11963
12379
  useEffect4(() => {
11964
- if (pickerAnchor !== null) {
11965
- if (cursorOffset < pickerAnchor) {
11966
- setPickerAnchor(null);
12380
+ if (activePicker !== null) {
12381
+ const trigger = activePicker.kind === "file" ? "@" : "/";
12382
+ if (cursorOffset < activePicker.anchor) {
12383
+ setActivePicker(null);
11967
12384
  return;
11968
12385
  }
11969
- if (input[pickerAnchor] !== "@") {
11970
- setPickerAnchor(null);
12386
+ if (input[activePicker.anchor] !== trigger) {
12387
+ setActivePicker(null);
11971
12388
  return;
11972
12389
  }
11973
- const query = input.slice(pickerAnchor + 1, cursorOffset);
11974
- if (query.includes(" ")) {
11975
- setPickerAnchor(null);
12390
+ const query = input.slice(activePicker.anchor + 1, cursorOffset);
12391
+ if (/\s/.test(query)) {
12392
+ setActivePicker(null);
11976
12393
  return;
11977
12394
  }
11978
12395
  return;
@@ -11982,9 +12399,9 @@ function App({
11982
12399
  return;
11983
12400
  }
11984
12401
  if (filePickerEnabled && shouldOpenMentionPicker(input, cursorOffset, pickerCancelRef.current)) {
11985
- setPickerAnchor(cursorOffset - 1);
11986
- setPickerSelected(0);
11987
- if (pickerItems.length === 0) {
12402
+ setActivePicker({ kind: "file", anchor: cursorOffset - 1, selected: 0 });
12403
+ if (!filePickerLoadedRef.current) {
12404
+ filePickerLoadedRef.current = true;
11988
12405
  const cwd = process.cwd();
11989
12406
  void fg4("**/*", {
11990
12407
  cwd,
@@ -12004,38 +12421,87 @@ function App({
12004
12421
  if (!a.isDirectory && b.isDirectory) return 1;
12005
12422
  return a.name.localeCompare(b.name);
12006
12423
  });
12007
- setPickerItems(items);
12424
+ setFilePickerItems(items);
12008
12425
  }).catch(() => {
12009
- setPickerItems([]);
12426
+ setFilePickerItems([]);
12010
12427
  });
12011
12428
  }
12429
+ return;
12430
+ }
12431
+ if (shouldOpenSlashPicker(input, cursorOffset, pickerCancelRef.current)) {
12432
+ setActivePicker({ kind: "slash", anchor: cursorOffset - 1, selected: 0 });
12433
+ return;
12012
12434
  }
12013
- }, [input, cursorOffset, pickerAnchor, pickerItems.length, filePickerEnabled]);
12435
+ }, [input, cursorOffset, activePicker, filePickerEnabled]);
12014
12436
  useEffect4(() => {
12015
- if (pickerAnchor !== null) {
12016
- setPickerSelected((prev) => Math.min(prev, Math.max(0, filteredPickerItems.length - 1)));
12437
+ if (activePicker?.kind !== "file") return;
12438
+ const max = Math.max(0, filteredFileItems.length - 1);
12439
+ if (activePicker.selected > max) {
12440
+ setActivePicker({ ...activePicker, selected: max });
12017
12441
  }
12018
- }, [filteredPickerItems.length, pickerAnchor]);
12442
+ }, [filteredFileItems.length, activePicker]);
12443
+ useEffect4(() => {
12444
+ if (activePicker?.kind !== "slash") return;
12445
+ const max = Math.max(0, filteredSlashItems.length - 1);
12446
+ if (activePicker.selected > max) {
12447
+ setActivePicker({ ...activePicker, selected: max });
12448
+ }
12449
+ }, [filteredSlashItems.length, activePicker]);
12019
12450
  const handlePickerUp = useCallback(() => {
12020
- setPickerSelected((i) => Math.max(0, i - 1));
12451
+ setActivePicker((p) => {
12452
+ if (!p) return null;
12453
+ const next = Math.max(0, p.selected - 1);
12454
+ return next === p.selected ? p : { ...p, selected: next };
12455
+ });
12021
12456
  }, []);
12022
12457
  const handlePickerDown = useCallback(() => {
12023
- setPickerSelected((i) => Math.min(filteredPickerItems.length - 1, i + 1));
12024
- }, [filteredPickerItems.length]);
12458
+ setActivePicker((p) => {
12459
+ if (!p) return null;
12460
+ const max = p.kind === "file" ? Math.max(0, filteredFileItems.length - 1) : Math.max(0, filteredSlashItems.length - 1);
12461
+ const next = Math.min(max, p.selected + 1);
12462
+ return next === p.selected ? p : { ...p, selected: next };
12463
+ });
12464
+ }, [filteredFileItems.length, filteredSlashItems.length]);
12025
12465
  const handlePickerSelect = useCallback(() => {
12026
- if (!mentionState || filteredPickerItems.length === 0) return;
12027
- const item = filteredPickerItems[pickerSelected];
12466
+ if (!activePicker) return;
12467
+ if (activePicker.kind === "file") {
12468
+ const item2 = filteredFileItems[activePicker.selected];
12469
+ if (!item2) return;
12470
+ const insert = item2.name + (item2.isDirectory ? "/" : " ");
12471
+ const newInput = input.slice(0, activePicker.anchor) + insert + input.slice(cursorOffset);
12472
+ setInput(newInput);
12473
+ setCursorOffset(activePicker.anchor + insert.length);
12474
+ setActivePicker(null);
12475
+ return;
12476
+ }
12477
+ const item = filteredSlashItems[activePicker.selected];
12028
12478
  if (!item) return;
12029
- const insert = item.name + (item.isDirectory ? "/" : " ");
12030
- const newInput = input.slice(0, mentionState.anchor) + insert + input.slice(cursorOffset);
12031
- setInput(newInput);
12032
- setCursorOffset(mentionState.anchor + insert.length);
12033
- setPickerAnchor(null);
12034
- }, [mentionState, filteredPickerItems, pickerSelected, input, cursorOffset]);
12479
+ const { value, cursor } = insertSlashCommand(input, activePicker.anchor, item.name);
12480
+ setInput(value);
12481
+ setCursorOffset(cursor);
12482
+ setActivePicker(null);
12483
+ }, [activePicker, filteredFileItems, filteredSlashItems, input, cursorOffset]);
12035
12484
  const handlePickerCancel = useCallback(() => {
12036
12485
  pickerCancelRef.current = cursorOffset;
12037
- setPickerAnchor(null);
12486
+ setActivePicker(null);
12038
12487
  }, [cursorOffset]);
12488
+ useEffect4(() => {
12489
+ const modalActive = showThemePicker || showHelpMenu || commandWizard !== null || commandPicker !== null || commandToDelete !== null || showCommandList || showLspWizard || resumeSessions !== null || perm !== null;
12490
+ if (modalActive && activePicker !== null) {
12491
+ setActivePicker(null);
12492
+ }
12493
+ }, [
12494
+ showThemePicker,
12495
+ showHelpMenu,
12496
+ commandWizard,
12497
+ commandPicker,
12498
+ commandToDelete,
12499
+ showCommandList,
12500
+ showLspWizard,
12501
+ resumeSessions,
12502
+ perm,
12503
+ activePicker
12504
+ ]);
12039
12505
  useEffect4(() => {
12040
12506
  if (!cfg) return;
12041
12507
  void Promise.resolve().then(() => (init_sessions(), sessions_exports)).then(
@@ -12116,6 +12582,7 @@ function App({
12116
12582
  }
12117
12583
  void loadCustomCommands(process.cwd()).then(({ commands, warnings }) => {
12118
12584
  customCommandsRef.current = commands;
12585
+ setCustomCommandsVersion((v) => v + 1);
12119
12586
  for (const w of warnings) {
12120
12587
  setEvents((e) => [...e, { kind: "info", key: mkKey(), text: `commands: ${w}` }]);
12121
12588
  }
@@ -12141,6 +12608,7 @@ function App({
12141
12608
  const reloadCustomCommands = useCallback(async () => {
12142
12609
  const { commands, warnings } = await loadCustomCommands(process.cwd());
12143
12610
  customCommandsRef.current = commands;
12611
+ setCustomCommandsVersion((v) => v + 1);
12144
12612
  for (const w of warnings) {
12145
12613
  setEvents((e) => [...e, { kind: "info", key: mkKey(), text: `commands: ${w}` }]);
12146
12614
  }
@@ -14077,7 +14545,7 @@ ${lines.join("\n")}` }]);
14077
14545
  }
14078
14546
  }, [usage]);
14079
14547
  if (!cfg) {
14080
- return /* @__PURE__ */ jsx19(
14548
+ return /* @__PURE__ */ jsx20(
14081
14549
  Onboarding,
14082
14550
  {
14083
14551
  onDone: (newCfg) => {
@@ -14091,13 +14559,13 @@ ${lines.join("\n")}` }]);
14091
14559
  );
14092
14560
  }
14093
14561
  if (resumeSessions !== null) {
14094
- return /* @__PURE__ */ jsx19(Box18, { flexDirection: "column", children: /* @__PURE__ */ jsx19(ResumePicker, { sessions: resumeSessions, onPick: handleResumePick, theme }) });
14562
+ return /* @__PURE__ */ jsx20(Box19, { flexDirection: "column", children: /* @__PURE__ */ jsx20(ResumePicker, { sessions: resumeSessions, onPick: handleResumePick, theme }) });
14095
14563
  }
14096
14564
  if (showThemePicker) {
14097
- return /* @__PURE__ */ jsx19(Box18, { flexDirection: "column", children: /* @__PURE__ */ jsx19(ThemePicker, { themes: themeList(), current: theme, onPick: handleThemePick, onPreview: (t) => setTheme(t) }) });
14565
+ return /* @__PURE__ */ jsx20(Box19, { flexDirection: "column", children: /* @__PURE__ */ jsx20(ThemePicker, { themes: themeList(), current: theme, onPick: handleThemePick, onPreview: (t) => setTheme(t) }) });
14098
14566
  }
14099
14567
  if (showHelpMenu) {
14100
- return /* @__PURE__ */ jsx19(Box18, { flexDirection: "column", children: /* @__PURE__ */ jsx19(
14568
+ return /* @__PURE__ */ jsx20(Box19, { flexDirection: "column", children: /* @__PURE__ */ jsx20(
14101
14569
  HelpMenu,
14102
14570
  {
14103
14571
  theme,
@@ -14112,7 +14580,7 @@ ${lines.join("\n")}` }]);
14112
14580
  ) });
14113
14581
  }
14114
14582
  if (showLspWizard) {
14115
- return /* @__PURE__ */ jsx19(Box18, { flexDirection: "column", children: /* @__PURE__ */ jsx19(
14583
+ return /* @__PURE__ */ jsx20(Box19, { flexDirection: "column", children: /* @__PURE__ */ jsx20(
14116
14584
  LspWizard,
14117
14585
  {
14118
14586
  theme,
@@ -14150,7 +14618,7 @@ ${lines.join("\n")}` }]);
14150
14618
  ) });
14151
14619
  }
14152
14620
  if (commandWizard) {
14153
- return /* @__PURE__ */ jsx19(Box18, { flexDirection: "column", children: /* @__PURE__ */ jsx19(
14621
+ return /* @__PURE__ */ jsx20(Box19, { flexDirection: "column", children: /* @__PURE__ */ jsx20(
14154
14622
  CommandWizard,
14155
14623
  {
14156
14624
  theme,
@@ -14164,7 +14632,7 @@ ${lines.join("\n")}` }]);
14164
14632
  ) });
14165
14633
  }
14166
14634
  if (commandPicker) {
14167
- return /* @__PURE__ */ jsx19(Box18, { flexDirection: "column", children: /* @__PURE__ */ jsx19(
14635
+ return /* @__PURE__ */ jsx20(Box19, { flexDirection: "column", children: /* @__PURE__ */ jsx20(
14168
14636
  CommandPicker,
14169
14637
  {
14170
14638
  theme,
@@ -14183,14 +14651,14 @@ ${lines.join("\n")}` }]);
14183
14651
  ) });
14184
14652
  }
14185
14653
  if (commandToDelete) {
14186
- return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
14187
- /* @__PURE__ */ jsxs18(Text19, { color: theme.accent, bold: true, children: [
14654
+ return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
14655
+ /* @__PURE__ */ jsxs19(Text20, { color: theme.accent, bold: true, children: [
14188
14656
  "Delete /",
14189
14657
  commandToDelete.name,
14190
14658
  "?"
14191
14659
  ] }),
14192
- /* @__PURE__ */ jsx19(Text19, { color: theme.info.color, dimColor: true, children: commandToDelete.filepath }),
14193
- /* @__PURE__ */ jsx19(Box18, { marginTop: 1, children: /* @__PURE__ */ jsx19(
14660
+ /* @__PURE__ */ jsx20(Text20, { color: theme.info.color, dimColor: true, children: commandToDelete.filepath }),
14661
+ /* @__PURE__ */ jsx20(Box19, { marginTop: 1, children: /* @__PURE__ */ jsx20(
14194
14662
  SelectInput8,
14195
14663
  {
14196
14664
  items: [
@@ -14209,7 +14677,7 @@ ${lines.join("\n")}` }]);
14209
14677
  ] });
14210
14678
  }
14211
14679
  if (showCommandList) {
14212
- return /* @__PURE__ */ jsx19(Box18, { flexDirection: "column", children: /* @__PURE__ */ jsx19(
14680
+ return /* @__PURE__ */ jsx20(Box19, { flexDirection: "column", children: /* @__PURE__ */ jsx20(
14213
14681
  CommandList,
14214
14682
  {
14215
14683
  theme,
@@ -14219,9 +14687,9 @@ ${lines.join("\n")}` }]);
14219
14687
  ) });
14220
14688
  }
14221
14689
  const hasConversation = events.some((e) => e.kind === "user" || e.kind === "assistant");
14222
- return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", children: [
14223
- !hasConversation && events.length === 0 ? /* @__PURE__ */ jsx19(Welcome, { theme, accountId: cfg.accountId }) : /* @__PURE__ */ jsx19(ChatView, { events, showReasoning, theme, verbose }),
14224
- perm ? /* @__PURE__ */ jsx19(
14690
+ return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", children: [
14691
+ !hasConversation && events.length === 0 ? /* @__PURE__ */ jsx20(Welcome, { theme, accountId: cfg.accountId }) : /* @__PURE__ */ jsx20(ChatView, { events, showReasoning, theme, verbose }),
14692
+ perm ? /* @__PURE__ */ jsx20(
14225
14693
  PermissionModal,
14226
14694
  {
14227
14695
  tool: perm.tool,
@@ -14233,8 +14701,8 @@ ${lines.join("\n")}` }]);
14233
14701
  setPerm(null);
14234
14702
  }
14235
14703
  }
14236
- ) : /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", marginTop: 1, children: [
14237
- tasks.length > 0 && /* @__PURE__ */ jsx19(
14704
+ ) : /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", marginTop: 1, children: [
14705
+ tasks.length > 0 && /* @__PURE__ */ jsx20(
14238
14706
  TaskList,
14239
14707
  {
14240
14708
  tasks,
@@ -14243,11 +14711,11 @@ ${lines.join("\n")}` }]);
14243
14711
  tokensDelta: Math.max(0, (usage?.prompt_tokens ?? 0) - tasksStartTokens)
14244
14712
  }
14245
14713
  ),
14246
- queue.length > 0 && /* @__PURE__ */ jsx19(Box18, { flexDirection: "column", marginBottom: 1, children: queue.map((q, i) => /* @__PURE__ */ jsxs18(Text19, { color: theme.queue.color, dimColor: theme.queue.dim, children: [
14714
+ queue.length > 0 && /* @__PURE__ */ jsx20(Box19, { flexDirection: "column", marginBottom: 1, children: queue.map((q, i) => /* @__PURE__ */ jsxs19(Text20, { color: theme.queue.color, dimColor: theme.queue.dim, children: [
14247
14715
  "\u23F3 ",
14248
14716
  q.display
14249
14717
  ] }, `queue_${i}`)) }),
14250
- /* @__PURE__ */ jsx19(
14718
+ /* @__PURE__ */ jsx20(
14251
14719
  StatusBar,
14252
14720
  {
14253
14721
  model: cfg.model,
@@ -14265,18 +14733,27 @@ ${lines.join("\n")}` }]);
14265
14733
  codeMode
14266
14734
  }
14267
14735
  ),
14268
- pickerAnchor !== null && /* @__PURE__ */ jsx19(
14736
+ activePicker?.kind === "file" && /* @__PURE__ */ jsx20(
14269
14737
  FilePicker,
14270
14738
  {
14271
- items: filteredPickerItems,
14272
- selectedIndex: pickerSelected,
14739
+ items: filteredFileItems,
14740
+ selectedIndex: activePicker.selected,
14273
14741
  theme,
14274
- query: mentionState?.query ?? ""
14742
+ query: pickerQuery ?? ""
14275
14743
  }
14276
14744
  ),
14277
- /* @__PURE__ */ jsxs18(Box18, { marginTop: 1, children: [
14278
- /* @__PURE__ */ jsx19(Text19, { color: theme.accent, children: "\u203A " }),
14279
- /* @__PURE__ */ jsx19(
14745
+ activePicker?.kind === "slash" && /* @__PURE__ */ jsx20(
14746
+ SlashPicker,
14747
+ {
14748
+ items: filteredSlashItems,
14749
+ selectedIndex: activePicker.selected,
14750
+ theme,
14751
+ query: pickerQuery ?? ""
14752
+ }
14753
+ ),
14754
+ /* @__PURE__ */ jsxs19(Box19, { marginTop: 1, children: [
14755
+ /* @__PURE__ */ jsx20(Text20, { color: theme.accent, children: "\u203A " }),
14756
+ /* @__PURE__ */ jsx20(
14280
14757
  CustomTextInput,
14281
14758
  {
14282
14759
  value: input,
@@ -14285,7 +14762,7 @@ ${lines.join("\n")}` }]);
14285
14762
  enablePaste: true,
14286
14763
  cursorOffset,
14287
14764
  onCursorChange: setCursorOffset,
14288
- pickerActive: pickerAnchor !== null,
14765
+ pickerActive: activePicker !== null,
14289
14766
  onPickerUp: handlePickerUp,
14290
14767
  onPickerDown: handlePickerDown,
14291
14768
  onPickerSelect: handlePickerSelect,
@@ -14333,7 +14810,7 @@ ${lines.join("\n")}` }]);
14333
14810
  }
14334
14811
  async function renderApp(cfg, updateResult, lspScope = "global", lspProjectPath = null) {
14335
14812
  const instance = render(
14336
- /* @__PURE__ */ jsx19(
14813
+ /* @__PURE__ */ jsx20(
14337
14814
  App,
14338
14815
  {
14339
14816
  initialCfg: cfg,
@@ -14348,7 +14825,7 @@ async function renderApp(cfg, updateResult, lspScope = "global", lspProjectPath
14348
14825
  );
14349
14826
  await instance.waitUntilExit();
14350
14827
  }
14351
- var MAX_GITIGNORE_SIZE, FEEDBACK_WORKER_URL, CONTEXT_LIMIT, AUTO_COMPACT_SUGGEST_PCT, MAX_EVENTS, nextAssistantId, nextKey, mkKey, MAX_IMAGES_PER_MESSAGE, BUILTIN_COMMAND_NAMES, EFFORT_DESCRIPTIONS;
14828
+ var MAX_GITIGNORE_SIZE, FEEDBACK_WORKER_URL, CONTEXT_LIMIT, AUTO_COMPACT_SUGGEST_PCT, MAX_EVENTS, nextAssistantId, nextKey, mkKey, MAX_IMAGES_PER_MESSAGE, EFFORT_DESCRIPTIONS;
14352
14829
  var init_app = __esm({
14353
14830
  "src/app.tsx"() {
14354
14831
  "use strict";
@@ -14387,6 +14864,7 @@ var init_app = __esm({
14387
14864
  init_version();
14388
14865
  init_loader();
14389
14866
  init_renderer2();
14867
+ init_builtins();
14390
14868
  init_save();
14391
14869
  init_command_wizard();
14392
14870
  init_command_picker();
@@ -14395,6 +14873,8 @@ var init_app = __esm({
14395
14873
  init_lsp_config();
14396
14874
  init_lsp_nudge();
14397
14875
  init_file_picker();
14876
+ init_slash_picker();
14877
+ init_fuzzy();
14398
14878
  MAX_GITIGNORE_SIZE = 1 * 1024 * 1024;
14399
14879
  FEEDBACK_WORKER_URL = "https://kimiflare-feedback.sina-b35.workers.dev";
14400
14880
  CONTEXT_LIMIT = 262e3;
@@ -14404,33 +14884,6 @@ var init_app = __esm({
14404
14884
  nextKey = 1;
14405
14885
  mkKey = () => `evt_${nextKey++}`;
14406
14886
  MAX_IMAGES_PER_MESSAGE = 10;
14407
- BUILTIN_COMMAND_NAMES = /* @__PURE__ */ new Set([
14408
- "exit",
14409
- "quit",
14410
- "clear",
14411
- "reasoning",
14412
- "cost",
14413
- "model",
14414
- "thinking",
14415
- "effort",
14416
- "theme",
14417
- "mode",
14418
- "plan",
14419
- "auto",
14420
- "edit",
14421
- "resume",
14422
- "compact",
14423
- "init",
14424
- "update",
14425
- "mcp",
14426
- "logout",
14427
- "help",
14428
- "memory",
14429
- "gateway",
14430
- "hello",
14431
- "community",
14432
- "agent"
14433
- ]);
14434
14887
  EFFORT_DESCRIPTIONS = {
14435
14888
  low: "low \u2014 fastest; lightest reasoning. Best for simple Q&A, small edits, quick coordination.",
14436
14889
  medium: "medium \u2014 balanced (default). Solid quality on most edits, fast on trivial prompts.",