kimiflare 0.25.0 → 0.26.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
@@ -83,6 +83,7 @@ async function loadConfig() {
83
83
  const envMemoryMaxAgeDays = readNumberEnv("KIMIFLARE_MEMORY_MAX_AGE_DAYS");
84
84
  const envMemoryMaxEntries = readNumberEnv("KIMIFLARE_MEMORY_MAX_ENTRIES");
85
85
  const envMemoryEmbeddingModel = process.env.KIMIFLARE_MEMORY_EMBEDDING_MODEL;
86
+ const envPlumbingModel = process.env.KIMIFLARE_PLUMBING_MODEL;
86
87
  const envCodeMode = readBooleanEnv("KIMIFLARE_CODE_MODE");
87
88
  if (envAccount && envToken) {
88
89
  return {
@@ -107,6 +108,7 @@ async function loadConfig() {
107
108
  memoryMaxAgeDays: envMemoryMaxAgeDays,
108
109
  memoryMaxEntries: envMemoryMaxEntries,
109
110
  memoryEmbeddingModel: envMemoryEmbeddingModel,
111
+ plumbingModel: envPlumbingModel,
110
112
  codeMode: envCodeMode
111
113
  };
112
114
  }
@@ -137,6 +139,7 @@ async function loadConfig() {
137
139
  memoryMaxAgeDays: envMemoryMaxAgeDays ?? parsed.memoryMaxAgeDays,
138
140
  memoryMaxEntries: envMemoryMaxEntries ?? parsed.memoryMaxEntries,
139
141
  memoryEmbeddingModel: envMemoryEmbeddingModel ?? parsed.memoryEmbeddingModel,
142
+ plumbingModel: envPlumbingModel ?? parsed.plumbingModel,
140
143
  codeMode: envCodeMode ?? parsed.codeMode
141
144
  };
142
145
  }
@@ -880,7 +883,7 @@ function schemaToTsType(prop, required = true) {
880
883
  types.push(`${itemType}[]`);
881
884
  } else if (prop.type === "object") {
882
885
  if (prop.properties && Object.keys(prop.properties).length > 0) {
883
- const entries = Object.entries(prop.properties).map(([key, val]) => {
886
+ const entries = Object.entries(prop.properties).sort(([a], [b]) => a.localeCompare(b)).map(([key, val]) => {
884
887
  const isReq = prop.required?.includes(key) ?? false;
885
888
  return ` ${key}${isReq ? "" : "?"}: ${schemaToTsType(val, isReq)};`;
886
889
  }).join("\n");
@@ -901,7 +904,7 @@ ${entries}
901
904
  return required ? result : `${result} | undefined`;
902
905
  }
903
906
  function generateInterface(name, properties, required = []) {
904
- const entries = Object.entries(properties).map(([key, val]) => {
907
+ const entries = Object.entries(properties).sort(([a], [b]) => a.localeCompare(b)).map(([key, val]) => {
905
908
  const isReq = required.includes(key);
906
909
  return ` ${key}${isReq ? "" : "?"}: ${schemaToTsType(val, isReq)};`;
907
910
  }).join("\n");
@@ -921,13 +924,14 @@ function generateTypeScriptApi(tools) {
921
924
  const inputInterfaces = [];
922
925
  const outputInterfaces = [];
923
926
  const methodEntries = [];
924
- for (const tool of tools) {
927
+ for (const tool of [...tools].sort((a, b) => a.name.localeCompare(b.name))) {
925
928
  const baseName = sanitizeTypeName(tool.name);
926
929
  const inputName = `${baseName}_Input`;
927
930
  const outputName = `${baseName}_Output`;
928
931
  const params = tool.parameters;
929
- if (params.properties && Object.keys(params.properties).length > 0) {
930
- inputInterfaces.push(generateInterface(inputName, params.properties, params.required));
932
+ const sortedPropKeys = params.properties ? Object.keys(params.properties).sort((a, b) => a.localeCompare(b)) : [];
933
+ if (sortedPropKeys.length > 0 && params.properties) {
934
+ inputInterfaces.push(generateInterface(inputName, params.properties, [...params.required ?? []].sort((a, b) => a.localeCompare(b))));
931
935
  inputInterfaces.push("");
932
936
  methodEntries.push(` /**`);
933
937
  methodEntries.push(` * ${tool.description.replace(/\n/g, "\n * ")}`);
@@ -1148,7 +1152,14 @@ async function runAgentTurn(opts2) {
1148
1152
  let toolDefs;
1149
1153
  let codeModeApiString = "";
1150
1154
  if (codeMode) {
1151
- codeModeApiString = generateTypeScriptApi(opts2.tools);
1155
+ const toolsKey = stableStringify(opts2.tools);
1156
+ const cached = codeModeApiCache.get(toolsKey);
1157
+ if (cached) {
1158
+ codeModeApiString = cached;
1159
+ } else {
1160
+ codeModeApiString = generateTypeScriptApi(opts2.tools);
1161
+ codeModeApiCache.set(toolsKey, codeModeApiString);
1162
+ }
1152
1163
  toolDefs = [
1153
1164
  {
1154
1165
  type: "function",
@@ -1183,6 +1194,9 @@ Use console.log() to return results. Only console.log output will be sent back t
1183
1194
  }
1184
1195
  let turn = 0;
1185
1196
  let lastUsage = null;
1197
+ const recentToolCalls = [];
1198
+ const LOOP_WINDOW = 8;
1199
+ const LOOP_THRESHOLD = 2;
1186
1200
  for (let iter = 0; iter < max; iter++) {
1187
1201
  turn++;
1188
1202
  const previousMessages = opts2.messages.slice();
@@ -1313,6 +1327,28 @@ Use console.log() to return results. Only console.log output will be sent back t
1313
1327
  }
1314
1328
  for (const tc of toolCalls) {
1315
1329
  if (opts2.signal.aborted) throw new DOMException("aborted", "AbortError");
1330
+ const loopSignature = `${tc.function.name}:${stableStringify(tc.function.arguments)}`;
1331
+ const loopCount = recentToolCalls.filter((s) => s === loopSignature).length;
1332
+ if (loopCount >= LOOP_THRESHOLD) {
1333
+ const warning = `Loop detected: you have called ${tc.function.name} with the same arguments multiple times in a row. Consider a different approach.`;
1334
+ const loopResult = {
1335
+ tool_call_id: tc.id,
1336
+ name: tc.function.name,
1337
+ content: warning,
1338
+ ok: false
1339
+ };
1340
+ toolResults.push(loopResult);
1341
+ opts2.messages.push({
1342
+ role: "tool",
1343
+ tool_call_id: tc.id,
1344
+ content: sanitizeString(warning),
1345
+ name: tc.function.name
1346
+ });
1347
+ opts2.callbacks.onToolResult?.(loopResult);
1348
+ recentToolCalls.push(loopSignature);
1349
+ if (recentToolCalls.length > LOOP_WINDOW) recentToolCalls.shift();
1350
+ continue;
1351
+ }
1316
1352
  if (codeMode && tc.function.name === "execute_code") {
1317
1353
  const args = JSON.parse(tc.function.arguments || "{}");
1318
1354
  const code = args.code || "";
@@ -1353,6 +1389,8 @@ ${sandboxResult.output}` : sandboxResult.output;
1353
1389
  name: "execute_code"
1354
1390
  });
1355
1391
  opts2.callbacks.onToolResult?.(result);
1392
+ recentToolCalls.push(loopSignature);
1393
+ if (recentToolCalls.length > LOOP_WINDOW) recentToolCalls.shift();
1356
1394
  } else {
1357
1395
  const result = await opts2.executor.run(
1358
1396
  { id: tc.id, name: tc.function.name, arguments: tc.function.arguments },
@@ -1367,6 +1405,8 @@ ${sandboxResult.output}` : sandboxResult.output;
1367
1405
  name: result.name
1368
1406
  });
1369
1407
  opts2.callbacks.onToolResult?.(result);
1408
+ recentToolCalls.push(loopSignature);
1409
+ if (recentToolCalls.length > LOOP_WINDOW) recentToolCalls.shift();
1370
1410
  }
1371
1411
  }
1372
1412
  if (opts2.sessionId && lastUsage) {
@@ -1392,6 +1432,7 @@ function validateToolArguments(raw) {
1392
1432
  return "{}";
1393
1433
  }
1394
1434
  }
1435
+ var codeModeApiCache;
1395
1436
  var init_loop = __esm({
1396
1437
  "src/agent/loop.ts"() {
1397
1438
  "use strict";
@@ -1401,6 +1442,7 @@ var init_loop = __esm({
1401
1442
  init_cost_debug();
1402
1443
  init_strip_reasoning();
1403
1444
  init_code_mode();
1445
+ codeModeApiCache = /* @__PURE__ */ new Map();
1404
1446
  }
1405
1447
  });
1406
1448
 
@@ -3236,6 +3278,39 @@ function emptySessionState(task = "") {
3236
3278
  artifact_index: {}
3237
3279
  };
3238
3280
  }
3281
+ function serializeArtifactStore(store) {
3282
+ const MAX_ARTIFACT_CHARS = 5e4;
3283
+ const out = [];
3284
+ for (const a of store.list()) {
3285
+ out.push({
3286
+ id: a.id,
3287
+ type: a.type,
3288
+ summary: a.summary,
3289
+ raw: a.raw.slice(0, MAX_ARTIFACT_CHARS),
3290
+ source: a.source,
3291
+ path: a.path,
3292
+ lineRange: a.lineRange,
3293
+ ts: a.ts
3294
+ });
3295
+ }
3296
+ return out;
3297
+ }
3298
+ function deserializeArtifactStore(data) {
3299
+ const store = new ArtifactStore();
3300
+ for (const a of data) {
3301
+ store.add({
3302
+ id: a.id,
3303
+ type: a.type,
3304
+ summary: a.summary,
3305
+ raw: a.raw,
3306
+ source: a.source,
3307
+ path: a.path,
3308
+ lineRange: a.lineRange,
3309
+ ts: a.ts
3310
+ });
3311
+ }
3312
+ return store;
3313
+ }
3239
3314
  function formatRecalledArtifacts(recalled) {
3240
3315
  if (recalled.length === 0) return "";
3241
3316
  const lines = ["[recalled artifacts]"];
@@ -4101,6 +4176,12 @@ var init_chat = __esm({
4101
4176
  evt.text
4102
4177
  ] });
4103
4178
  }
4179
+ if (evt.kind === "memory") {
4180
+ return /* @__PURE__ */ jsxs4(Text4, { color: theme.info.color, dimColor: theme.info.dim, children: [
4181
+ "\u25C8 ",
4182
+ evt.text
4183
+ ] });
4184
+ }
4104
4185
  return /* @__PURE__ */ jsxs4(Text4, { color: theme.error, children: [
4105
4186
  "! ",
4106
4187
  evt.text
@@ -5427,7 +5508,7 @@ function HelpMenu({ theme, themes, currentThemeName, customCommands, onDone, onC
5427
5508
  key: cat.key
5428
5509
  }));
5429
5510
  if (customs.length > 0) {
5430
- items2.push({ label: "Custom commands", value: "custom", key: "custom" });
5511
+ items2.push({ label: "Run custom commands", value: "custom", key: "custom" });
5431
5512
  }
5432
5513
  items2.push({ label: "(close)", value: "__close__", key: "__close__" });
5433
5514
  return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
@@ -5611,6 +5692,16 @@ var init_help_menu = __esm({
5611
5692
  { command: "/community", description: "join our Discord server" }
5612
5693
  ]
5613
5694
  },
5695
+ {
5696
+ key: "commands",
5697
+ label: "Commands",
5698
+ commands: [
5699
+ { command: "/command create", description: "create a new custom slash command" },
5700
+ { command: "/command edit", description: "edit an existing custom command" },
5701
+ { command: "/command delete", description: "delete a custom command" },
5702
+ { command: "/command list", description: "list all custom commands" }
5703
+ ]
5704
+ },
5614
5705
  {
5615
5706
  key: "config",
5616
5707
  label: "Config",
@@ -6425,11 +6516,11 @@ function updateAccessedAt(db, ids) {
6425
6516
  function searchMemoriesFts(db, query, repoPath, limit = 50) {
6426
6517
  const sql = repoPath ? `SELECT m.*, rank FROM memories m
6427
6518
  JOIN memories_fts fts ON m.rowid = fts.rowid
6428
- WHERE memories_fts MATCH ? AND m.repo_path = ? AND m.forgotten = 0 AND m.superseded_by IS NULL
6519
+ WHERE memories_fts MATCH ? AND m.repo_path = ? AND m.forgotten = 0 AND m.superseded_by IS NULL AND m.category != 'task'
6429
6520
  ORDER BY rank
6430
6521
  LIMIT ?` : `SELECT m.*, rank FROM memories m
6431
6522
  JOIN memories_fts fts ON m.rowid = fts.rowid
6432
- WHERE memories_fts MATCH ? AND m.forgotten = 0 AND m.superseded_by IS NULL
6523
+ WHERE memories_fts MATCH ? AND m.forgotten = 0 AND m.superseded_by IS NULL AND m.category != 'task'
6433
6524
  ORDER BY rank
6434
6525
  LIMIT ?`;
6435
6526
  const params = repoPath ? [`${query}*`, repoPath, limit] : [`${query}*`, limit];
@@ -6442,7 +6533,7 @@ function searchMemoriesFts(db, query, repoPath, limit = 50) {
6442
6533
  function listMemoriesForVectorSearch(db, repoPath, since, limit = 2e3) {
6443
6534
  const rows = db.prepare(
6444
6535
  `SELECT * FROM memories
6445
- WHERE repo_path = ? AND created_at >= ? AND forgotten = 0 AND superseded_by IS NULL
6536
+ WHERE repo_path = ? AND created_at >= ? AND forgotten = 0 AND superseded_by IS NULL AND category != 'task'
6446
6537
  ORDER BY accessed_at DESC
6447
6538
  LIMIT ?`
6448
6539
  ).all(repoPath, since, limit);
@@ -6889,7 +6980,20 @@ function redactSecrets(text) {
6889
6980
  }
6890
6981
  return result;
6891
6982
  }
6892
- var SECRET_PATTERNS, VERIFY_SYSTEM, TOPIC_KEY_SYSTEM, HYPOTHETICAL_QUERIES_SYSTEM, MemoryManager;
6983
+ function deterministicTopicKey(content) {
6984
+ return content.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().replace(/\s+/g, "_").slice(0, 60);
6985
+ }
6986
+ function pickTopicKey(content, existingKeys) {
6987
+ const normalized = deterministicTopicKey(content);
6988
+ if (!normalized) return null;
6989
+ for (const existing of existingKeys) {
6990
+ if (normalized.includes(existing) || existing.includes(normalized)) {
6991
+ return existing;
6992
+ }
6993
+ }
6994
+ return normalized;
6995
+ }
6996
+ var SECRET_PATTERNS, VERIFY_SYSTEM, HYPOTHETICAL_QUERIES_SYSTEM, MemoryManager;
6893
6997
  var init_manager2 = __esm({
6894
6998
  "src/memory/manager.ts"() {
6895
6999
  "use strict";
@@ -6917,19 +7021,12 @@ Return a JSON object:
6917
7021
  "confidence": "high" | "medium" | "low",
6918
7022
  "corrected_content": string | null // if minor correction needed, provide it; otherwise null
6919
7023
  }`;
6920
- TOPIC_KEY_SYSTEM = `You are a topic normalization engine. Given a new memory and a list of existing topic keys for this project, decide whether the new memory belongs to an existing topic or needs a new one.
6921
-
6922
- Rules:
6923
- - Topic keys are lowercase snake_case, max 3 words.
6924
- - If the new memory is about the same topic as an existing key, return the existing key.
6925
- - If it's a genuinely new topic, generate a new normalized key.
6926
- - Return ONLY the topic key string, nothing else.`;
6927
7024
  HYPOTHETICAL_QUERIES_SYSTEM = `Given a memory, generate 3-5 short search queries a user might type to find it.
6928
7025
  Cover different phrasings: declarative, interrogative, and keyword-based.
6929
7026
 
6930
7027
  Return a JSON array of strings. Example:
6931
7028
  ["what package manager does this project use?", "pnpm preference", "user likes pnpm over npm"]`;
6932
- MemoryManager = class {
7029
+ MemoryManager = class _MemoryManager {
6933
7030
  db = null;
6934
7031
  opts;
6935
7032
  constructor(opts2) {
@@ -6957,6 +7054,14 @@ Return a JSON array of strings. Example:
6957
7054
  gateway: this.opts.gateway
6958
7055
  };
6959
7056
  }
7057
+ get plumbingLlmOpts() {
7058
+ return {
7059
+ accountId: this.opts.accountId,
7060
+ apiToken: this.opts.apiToken,
7061
+ model: this.opts.plumbingModel ?? "@cf/meta/llama-4-scout-17b-16e-instruct",
7062
+ gateway: this.opts.gateway
7063
+ };
7064
+ }
6960
7065
  shouldRedact() {
6961
7066
  return this.opts.redactSecrets !== false;
6962
7067
  }
@@ -6977,7 +7082,7 @@ Return a JSON array of strings. Example:
6977
7082
  if (verified.corrected_content) {
6978
7083
  safeContent = verified.corrected_content;
6979
7084
  }
6980
- const topicKey = await this.normalizeTopicKey(safeContent, repoPath, signal);
7085
+ const topicKey = this.normalizeTopicKey(safeContent, repoPath);
6981
7086
  const supersededIds = [];
6982
7087
  if (topicKey) {
6983
7088
  const existing = findMemoriesByTopicKey(this.db, repoPath, topicKey);
@@ -7029,6 +7134,38 @@ Return a JSON array of strings. Example:
7029
7134
  }
7030
7135
  return retrieveMemories({ db: this.db, query });
7031
7136
  }
7137
+ /**
7138
+ * Format recalled memories as a compact context block for injection into messages.
7139
+ */
7140
+ static formatRecalled(results) {
7141
+ if (results.length === 0) return "";
7142
+ const lines = ["[recalled memories]"];
7143
+ for (const r of results) {
7144
+ const files = r.memory.relatedFiles.length > 0 ? ` [${r.memory.relatedFiles.join(", ")}]` : "";
7145
+ lines.push(`- [${r.memory.category}] ${r.memory.content}${files}`);
7146
+ }
7147
+ return lines.join("\n");
7148
+ }
7149
+ /**
7150
+ * Synthesize recalled memories into a dense prose paragraph.
7151
+ * Uses the lightweight plumbing model (Scout) to keep costs low.
7152
+ */
7153
+ async synthesizeRecalled(results, signal) {
7154
+ if (results.length === 0) return "";
7155
+ const raw = _MemoryManager.formatRecalled(results);
7156
+ const text = await runKimiText({
7157
+ ...this.plumbingLlmOpts,
7158
+ signal,
7159
+ messages: [
7160
+ {
7161
+ role: "system",
7162
+ content: "You are a context-synthesis engine. Given a list of recalled memories about a codebase, produce a single dense paragraph of context for a coding assistant. Preserve all facts, file paths, and decisions. Do not add information not present in the memories. Be terse."
7163
+ },
7164
+ { role: "user", content: raw }
7165
+ ]
7166
+ });
7167
+ return text || raw;
7168
+ }
7032
7169
  /**
7033
7170
  * Soft-delete a memory by ID.
7034
7171
  */
@@ -7088,7 +7225,7 @@ Return a JSON array of strings. Example:
7088
7225
  }
7089
7226
  async verifyMemory(content, signal) {
7090
7227
  const text = await runKimiText({
7091
- ...this.llmOpts,
7228
+ ...this.plumbingLlmOpts,
7092
7229
  signal,
7093
7230
  messages: [
7094
7231
  { role: "system", content: VERIFY_SYSTEM },
@@ -7112,31 +7249,13 @@ Context: This memory was explicitly provided by the user during a conversation.`
7112
7249
  const corrected = typeof rec.corrected_content === "string" ? rec.corrected_content : null;
7113
7250
  return { valid, corrected_content: corrected };
7114
7251
  }
7115
- async normalizeTopicKey(content, repoPath, signal) {
7252
+ normalizeTopicKey(content, repoPath) {
7116
7253
  const existingKeys = listTopicKeys(this.db, repoPath);
7117
- const keysBlock = existingKeys.length > 0 ? existingKeys.join("\n") : "(none yet)";
7118
- const text = await runKimiText({
7119
- ...this.llmOpts,
7120
- signal,
7121
- messages: [
7122
- { role: "system", content: TOPIC_KEY_SYSTEM },
7123
- {
7124
- role: "user",
7125
- content: `Existing topic keys:
7126
- ${keysBlock}
7127
-
7128
- New memory: "${content}"
7129
-
7130
- Return only the topic key string.`
7131
- }
7132
- ]
7133
- });
7134
- const key = text.trim().toLowerCase().replace(/\s+/g, "_").replace(/[^a-z0-9_]/g, "").slice(0, 60);
7135
- return key || null;
7254
+ return pickTopicKey(content, existingKeys);
7136
7255
  }
7137
7256
  async generateHypotheticalQueries(content, signal) {
7138
7257
  const text = await runKimiText({
7139
- ...this.llmOpts,
7258
+ ...this.plumbingLlmOpts,
7140
7259
  signal,
7141
7260
  messages: [
7142
7261
  { role: "system", content: HYPOTHETICAL_QUERIES_SYSTEM },
@@ -7194,6 +7313,18 @@ var init_state = __esm({
7194
7313
  });
7195
7314
 
7196
7315
  // src/commands/frontmatter.ts
7316
+ function serializeFrontmatter(data) {
7317
+ const lines = [];
7318
+ for (const [key, value] of Object.entries(data)) {
7319
+ if (value === void 0 || value === "") continue;
7320
+ lines.push(`${key}: ${value}`);
7321
+ }
7322
+ if (lines.length === 0) return "";
7323
+ return `---
7324
+ ${lines.join("\n")}
7325
+ ---
7326
+ `;
7327
+ }
7197
7328
  function parseFrontmatter(input) {
7198
7329
  const errors = [];
7199
7330
  if (!FENCE.test(input)) {
@@ -7532,19 +7663,566 @@ var init_renderer = __esm({
7532
7663
  }
7533
7664
  });
7534
7665
 
7666
+ // src/commands/save.ts
7667
+ import { mkdir as mkdir8, writeFile as writeFile8, unlink as unlink2 } from "fs/promises";
7668
+ import { dirname as dirname5 } from "path";
7669
+ async function saveCustomCommand(opts2) {
7670
+ const dir = opts2.source === "project" ? projectCommandsDir(opts2.cwd) : globalCommandsDir();
7671
+ const filepath = `${dir}/${opts2.name}.md`;
7672
+ const data = {};
7673
+ if (opts2.description) data.description = opts2.description;
7674
+ if (opts2.mode) data.mode = opts2.mode;
7675
+ if (opts2.model) data.model = opts2.model;
7676
+ if (opts2.effort) data.effort = opts2.effort;
7677
+ const frontmatter = serializeFrontmatter(data);
7678
+ const content = frontmatter + opts2.template;
7679
+ await mkdir8(dirname5(filepath), { recursive: true });
7680
+ await writeFile8(filepath, content, "utf8");
7681
+ return { filepath };
7682
+ }
7683
+ async function deleteCustomCommand(cmd) {
7684
+ await unlink2(cmd.filepath);
7685
+ }
7686
+ var init_save = __esm({
7687
+ "src/commands/save.ts"() {
7688
+ "use strict";
7689
+ init_frontmatter();
7690
+ init_loader();
7691
+ }
7692
+ });
7693
+
7694
+ // src/ui/command-wizard.tsx
7695
+ import { useState as useState7 } from "react";
7696
+ import { Box as Box13, Text as Text14, useInput as useInput3, useWindowSize as useWindowSize2 } from "ink";
7697
+ import SelectInput5 from "ink-select-input";
7698
+ import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
7699
+ function CommandWizard({ theme, mode, initial, existingNames, builtinNames, onDone, onSave }) {
7700
+ const [step, setStep] = useState7("name");
7701
+ const [name, setName] = useState7(initial?.name ?? "");
7702
+ const [description, setDescription] = useState7(initial?.description ?? "");
7703
+ const [template, setTemplate] = useState7(initial?.template ?? "");
7704
+ const [cmdMode, setCmdMode] = useState7(initial?.mode);
7705
+ const [cmdEffort, setCmdEffort] = useState7(initial?.effort);
7706
+ const [cmdModel, setCmdModel] = useState7(initial?.model);
7707
+ const [source, setSource] = useState7(initial?.source ?? "project");
7708
+ const [error, setError] = useState7(null);
7709
+ const { columns } = useWindowSize2();
7710
+ const totalSteps = 5;
7711
+ const stepIndex = step === "name" ? 1 : step === "description" ? 2 : step === "template" ? 3 : step === "advanced" || step === "mode" || step === "effort" || step === "model" ? 4 : step === "location" ? 4 : 5;
7712
+ const isEditingSelf = (n) => initial !== void 0 && initial.name === n;
7713
+ const validateName = (n) => {
7714
+ const trimmed = n.trim();
7715
+ if (!trimmed) return "name is required";
7716
+ if (!NAME_RE.test(trimmed)) return "invalid name: use letters, numbers, _ - / only; must start with a letter";
7717
+ if (builtinNames.has(trimmed.toLowerCase())) return `/${trimmed} is a built-in command`;
7718
+ if (existingNames.includes(trimmed) && !isEditingSelf(trimmed)) return `/${trimmed} already exists`;
7719
+ return null;
7720
+ };
7721
+ useInput3((_input, key) => {
7722
+ if (key.escape) {
7723
+ onDone();
7724
+ }
7725
+ });
7726
+ const handleNameSubmit = (value) => {
7727
+ const trimmed = value.trim();
7728
+ const err = validateName(trimmed);
7729
+ if (err) {
7730
+ setError(err);
7731
+ return;
7732
+ }
7733
+ setError(null);
7734
+ setName(trimmed);
7735
+ setStep("description");
7736
+ };
7737
+ const handleDescriptionSubmit = (value) => {
7738
+ setDescription(value.trim());
7739
+ setStep("template");
7740
+ };
7741
+ const handleTemplateSubmit = (value) => {
7742
+ const trimmed = value.trim();
7743
+ if (!trimmed) {
7744
+ setError("template cannot be empty");
7745
+ return;
7746
+ }
7747
+ setError(null);
7748
+ setTemplate(trimmed);
7749
+ setStep("advanced");
7750
+ };
7751
+ const handleAdvancedChoice = (choice) => {
7752
+ if (choice === "skip") {
7753
+ setCmdMode(void 0);
7754
+ setCmdEffort(void 0);
7755
+ setCmdModel("");
7756
+ if (mode === "edit" && initial) {
7757
+ setSource(initial.source);
7758
+ setStep("confirm");
7759
+ } else {
7760
+ setStep("location");
7761
+ }
7762
+ } else {
7763
+ setStep("mode");
7764
+ }
7765
+ };
7766
+ const handleModeChoice = (m) => {
7767
+ setCmdMode(m === "none" ? void 0 : m);
7768
+ setStep("effort");
7769
+ };
7770
+ const handleEffortChoice = (e) => {
7771
+ setCmdEffort(e === "none" ? void 0 : e);
7772
+ setStep("model");
7773
+ };
7774
+ const handleModelSubmit = (value) => {
7775
+ const trimmed = value.trim();
7776
+ setCmdModel(trimmed || void 0);
7777
+ if (mode === "edit" && initial) {
7778
+ setSource(initial.source);
7779
+ setStep("confirm");
7780
+ } else {
7781
+ setStep("location");
7782
+ }
7783
+ };
7784
+ const handleLocationChoice = (s) => {
7785
+ setSource(s);
7786
+ setStep("confirm");
7787
+ };
7788
+ const handleConfirm = (choice) => {
7789
+ if (choice === "cancel") {
7790
+ onDone();
7791
+ return;
7792
+ }
7793
+ onSave({
7794
+ name,
7795
+ description: description || void 0,
7796
+ template,
7797
+ source,
7798
+ mode: cmdMode,
7799
+ model: cmdModel || void 0,
7800
+ effort: cmdEffort
7801
+ });
7802
+ };
7803
+ const previewContent = () => {
7804
+ const data = {};
7805
+ if (description) data.description = description;
7806
+ if (cmdMode) data.mode = cmdMode;
7807
+ if (cmdModel) data.model = cmdModel;
7808
+ if (cmdEffort) data.effort = cmdEffort;
7809
+ const fm = Object.entries(data).map(([k, v]) => `${k}: ${v}`).join("\n");
7810
+ if (fm) {
7811
+ return `---
7812
+ ${fm}
7813
+ ---
7814
+ ${template}`;
7815
+ }
7816
+ return template;
7817
+ };
7818
+ const renderStep = () => {
7819
+ switch (step) {
7820
+ case "name":
7821
+ return /* @__PURE__ */ jsxs13(Fragment2, { children: [
7822
+ /* @__PURE__ */ jsxs13(Text14, { color: theme.accent, bold: true, children: [
7823
+ mode === "create" ? "Create" : "Edit",
7824
+ " custom command \u2014 Name (",
7825
+ stepIndex,
7826
+ "/",
7827
+ totalSteps,
7828
+ ")"
7829
+ ] }),
7830
+ error && /* @__PURE__ */ jsx14(Text14, { color: theme.error, children: error }),
7831
+ /* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(
7832
+ CustomTextInput,
7833
+ {
7834
+ value: name,
7835
+ onChange: setName,
7836
+ onSubmit: handleNameSubmit,
7837
+ focus: true
7838
+ }
7839
+ ) }),
7840
+ /* @__PURE__ */ jsx14(Text14, { color: theme.info.color, dimColor: true, children: "letters, numbers, _ - / only; must start with a letter" })
7841
+ ] });
7842
+ case "description":
7843
+ return /* @__PURE__ */ jsxs13(Fragment2, { children: [
7844
+ /* @__PURE__ */ jsxs13(Text14, { color: theme.accent, bold: true, children: [
7845
+ mode === "create" ? "Create" : "Edit",
7846
+ " custom command \u2014 Description (",
7847
+ stepIndex,
7848
+ "/",
7849
+ totalSteps,
7850
+ ")"
7851
+ ] }),
7852
+ /* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(
7853
+ CustomTextInput,
7854
+ {
7855
+ value: description,
7856
+ onChange: setDescription,
7857
+ onSubmit: handleDescriptionSubmit,
7858
+ focus: true
7859
+ }
7860
+ ) }),
7861
+ /* @__PURE__ */ jsx14(Text14, { color: theme.info.color, dimColor: true, children: "Press Enter to skip" })
7862
+ ] });
7863
+ case "template": {
7864
+ const guide = /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", paddingLeft: 1, children: [
7865
+ /* @__PURE__ */ jsx14(Text14, { color: theme.accent, bold: true, children: "What is this?" }),
7866
+ /* @__PURE__ */ jsx14(Text14, { color: theme.info.color, dimColor: true, children: "A prompt template \u2014 instructions to the AI." }),
7867
+ /* @__PURE__ */ jsxs13(Text14, { color: theme.info.color, dimColor: true, children: [
7868
+ "When you type /",
7869
+ name || "yourcommand",
7870
+ " later, this gets sent to the model."
7871
+ ] }),
7872
+ /* @__PURE__ */ jsxs13(Box13, { marginTop: 1, flexDirection: "column", children: [
7873
+ /* @__PURE__ */ jsx14(Text14, { color: theme.accent, bold: true, children: "Variables" }),
7874
+ /* @__PURE__ */ jsxs13(Text14, { color: theme.info.color, dimColor: true, children: [
7875
+ " ",
7876
+ "$1, $2 ... \u2192 arguments you type"
7877
+ ] }),
7878
+ /* @__PURE__ */ jsxs13(Text14, { color: theme.info.color, dimColor: true, children: [
7879
+ " ",
7880
+ "$ARGUMENTS \u2192 everything after the command"
7881
+ ] })
7882
+ ] }),
7883
+ /* @__PURE__ */ jsxs13(Box13, { marginTop: 1, flexDirection: "column", children: [
7884
+ /* @__PURE__ */ jsx14(Text14, { color: theme.accent, bold: true, children: "Dynamic inlines" }),
7885
+ /* @__PURE__ */ jsxs13(Text14, { color: theme.info.color, dimColor: true, children: [
7886
+ " ",
7887
+ "!`git diff` \u2192 shell output inlined"
7888
+ ] }),
7889
+ /* @__PURE__ */ jsxs13(Text14, { color: theme.info.color, dimColor: true, children: [
7890
+ " ",
7891
+ "@README.md \u2192 file contents inlined"
7892
+ ] })
7893
+ ] }),
7894
+ /* @__PURE__ */ jsxs13(Box13, { marginTop: 1, flexDirection: "column", children: [
7895
+ /* @__PURE__ */ jsx14(Text14, { color: theme.accent, bold: true, children: "Example" }),
7896
+ /* @__PURE__ */ jsx14(Text14, { color: theme.info.color, dimColor: true, children: "Review this PR diff:" }),
7897
+ /* @__PURE__ */ jsx14(Text14, { color: theme.info.color, dimColor: true, children: "!`git diff main...HEAD`" }),
7898
+ /* @__PURE__ */ jsx14(Text14, { color: theme.info.color, dimColor: true, children: "Focus on: $1" })
7899
+ ] })
7900
+ ] });
7901
+ const inputArea = /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", flexGrow: 1, children: [
7902
+ error && /* @__PURE__ */ jsx14(Text14, { color: theme.error, children: error }),
7903
+ /* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(
7904
+ CustomTextInput,
7905
+ {
7906
+ value: template,
7907
+ onChange: setTemplate,
7908
+ onSubmit: handleTemplateSubmit,
7909
+ focus: true,
7910
+ enablePaste: true
7911
+ }
7912
+ ) }),
7913
+ columns < 100 && /* @__PURE__ */ jsxs13(Fragment2, { children: [
7914
+ /* @__PURE__ */ jsx14(Text14, { color: theme.info.color, dimColor: true, children: "Paste multi-line templates with Ctrl+V." }),
7915
+ /* @__PURE__ */ jsx14(Text14, { color: theme.info.color, dimColor: true, children: "Variables: $1 $2 ... $ARGUMENTS Shell: !`cmd` File: @path" })
7916
+ ] })
7917
+ ] });
7918
+ return /* @__PURE__ */ jsxs13(Fragment2, { children: [
7919
+ /* @__PURE__ */ jsxs13(Text14, { color: theme.accent, bold: true, children: [
7920
+ mode === "create" ? "Create" : "Edit",
7921
+ " custom command \u2014 Template (",
7922
+ stepIndex,
7923
+ "/",
7924
+ totalSteps,
7925
+ ")"
7926
+ ] }),
7927
+ columns >= 100 ? /* @__PURE__ */ jsxs13(Box13, { flexDirection: "row", marginTop: 1, children: [
7928
+ /* @__PURE__ */ jsx14(Box13, { flexDirection: "column", width: "50%", children: inputArea }),
7929
+ /* @__PURE__ */ jsx14(Box13, { flexDirection: "column", width: "50%", children: guide })
7930
+ ] }) : /* @__PURE__ */ jsx14(Box13, { flexDirection: "column", marginTop: 1, children: inputArea })
7931
+ ] });
7932
+ }
7933
+ case "advanced": {
7934
+ const items = [
7935
+ { label: "Set advanced options", value: "set", key: "set" },
7936
+ { label: "Skip", value: "skip", key: "skip" },
7937
+ { label: "\u2190 Cancel", value: "cancel", key: "cancel" }
7938
+ ];
7939
+ return /* @__PURE__ */ jsxs13(Fragment2, { children: [
7940
+ /* @__PURE__ */ jsxs13(Text14, { color: theme.accent, bold: true, children: [
7941
+ mode === "create" ? "Create" : "Edit",
7942
+ " custom command \u2014 Options (",
7943
+ stepIndex,
7944
+ "/",
7945
+ totalSteps,
7946
+ ")"
7947
+ ] }),
7948
+ /* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(
7949
+ SelectInput5,
7950
+ {
7951
+ items,
7952
+ onSelect: (item) => {
7953
+ if (item.value === "cancel") onDone();
7954
+ else handleAdvancedChoice(item.value);
7955
+ }
7956
+ }
7957
+ ) })
7958
+ ] });
7959
+ }
7960
+ case "mode": {
7961
+ const items = [
7962
+ { label: cmdMode === void 0 ? "none \xB7 current" : "none", value: "none", key: "none" },
7963
+ { label: cmdMode === "edit" ? "edit \xB7 current" : "edit", value: "edit", key: "edit" },
7964
+ { label: cmdMode === "plan" ? "plan \xB7 current" : "plan", value: "plan", key: "plan" },
7965
+ { label: cmdMode === "auto" ? "auto \xB7 current" : "auto", value: "auto", key: "auto" },
7966
+ { label: "\u2190 Back", value: "__back__", key: "__back__" }
7967
+ ];
7968
+ return /* @__PURE__ */ jsxs13(Fragment2, { children: [
7969
+ /* @__PURE__ */ jsxs13(Text14, { color: theme.accent, bold: true, children: [
7970
+ "Mode override (",
7971
+ stepIndex,
7972
+ "/",
7973
+ totalSteps,
7974
+ ")"
7975
+ ] }),
7976
+ /* @__PURE__ */ jsx14(Text14, { color: theme.info.color, dimColor: true, children: "Saved to file but not yet enforced at runtime" }),
7977
+ /* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(
7978
+ SelectInput5,
7979
+ {
7980
+ items,
7981
+ onSelect: (item) => {
7982
+ if (item.value === "__back__") setStep("advanced");
7983
+ else handleModeChoice(item.value);
7984
+ }
7985
+ }
7986
+ ) })
7987
+ ] });
7988
+ }
7989
+ case "effort": {
7990
+ const items = [
7991
+ { label: cmdEffort === void 0 ? "none \xB7 current" : "none", value: "none", key: "none" },
7992
+ { label: cmdEffort === "low" ? "low \xB7 current" : "low", value: "low", key: "low" },
7993
+ { label: cmdEffort === "medium" ? "medium \xB7 current" : "medium", value: "medium", key: "medium" },
7994
+ { label: cmdEffort === "high" ? "high \xB7 current" : "high", value: "high", key: "high" },
7995
+ { label: "\u2190 Back", value: "__back__", key: "__back__" }
7996
+ ];
7997
+ return /* @__PURE__ */ jsxs13(Fragment2, { children: [
7998
+ /* @__PURE__ */ jsxs13(Text14, { color: theme.accent, bold: true, children: [
7999
+ "Reasoning effort (",
8000
+ stepIndex,
8001
+ "/",
8002
+ totalSteps,
8003
+ ")"
8004
+ ] }),
8005
+ /* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(
8006
+ SelectInput5,
8007
+ {
8008
+ items,
8009
+ onSelect: (item) => {
8010
+ if (item.value === "__back__") setStep("mode");
8011
+ else handleEffortChoice(item.value);
8012
+ }
8013
+ }
8014
+ ) })
8015
+ ] });
8016
+ }
8017
+ case "model":
8018
+ return /* @__PURE__ */ jsxs13(Fragment2, { children: [
8019
+ /* @__PURE__ */ jsxs13(Text14, { color: theme.accent, bold: true, children: [
8020
+ "Model override (",
8021
+ stepIndex,
8022
+ "/",
8023
+ totalSteps,
8024
+ ")"
8025
+ ] }),
8026
+ /* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(
8027
+ CustomTextInput,
8028
+ {
8029
+ value: cmdModel ?? "",
8030
+ onChange: setCmdModel,
8031
+ onSubmit: handleModelSubmit,
8032
+ focus: true
8033
+ }
8034
+ ) }),
8035
+ /* @__PURE__ */ jsx14(Text14, { color: theme.info.color, dimColor: true, children: "Press Enter to skip" })
8036
+ ] });
8037
+ case "location": {
8038
+ const items = [
8039
+ { label: source === "project" ? "Project \xB7 current" : "Project", value: "project", key: "project" },
8040
+ { label: source === "global" ? "Global \xB7 current" : "Global", value: "global", key: "global" },
8041
+ { label: "\u2190 Back", value: "__back__", key: "__back__" }
8042
+ ];
8043
+ return /* @__PURE__ */ jsxs13(Fragment2, { children: [
8044
+ /* @__PURE__ */ jsxs13(Text14, { color: theme.accent, bold: true, children: [
8045
+ "Save location (",
8046
+ stepIndex,
8047
+ "/",
8048
+ totalSteps,
8049
+ ")"
8050
+ ] }),
8051
+ /* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(
8052
+ SelectInput5,
8053
+ {
8054
+ items,
8055
+ onSelect: (item) => {
8056
+ if (item.value === "__back__") setStep("advanced");
8057
+ else handleLocationChoice(item.value);
8058
+ }
8059
+ }
8060
+ ) }),
8061
+ /* @__PURE__ */ jsx14(Text14, { color: theme.info.color, dimColor: true, children: "Project: .kimiflare/commands/ Global: ~/.config/kimiflare/commands/" })
8062
+ ] });
8063
+ }
8064
+ case "confirm": {
8065
+ const items = [
8066
+ { label: "Save", value: "save", key: "save" },
8067
+ { label: "Cancel", value: "cancel", key: "cancel" }
8068
+ ];
8069
+ return /* @__PURE__ */ jsxs13(Fragment2, { children: [
8070
+ /* @__PURE__ */ jsxs13(Text14, { color: theme.accent, bold: true, children: [
8071
+ mode === "create" ? "Create" : "Edit",
8072
+ " custom command \u2014 Confirm (",
8073
+ stepIndex,
8074
+ "/",
8075
+ totalSteps,
8076
+ ")"
8077
+ ] }),
8078
+ /* @__PURE__ */ jsxs13(Text14, { color: theme.info.color, dimColor: true, children: [
8079
+ source === "project" ? ".kimiflare/commands/" : "~/.config/kimiflare/commands/",
8080
+ name,
8081
+ ".md"
8082
+ ] }),
8083
+ /* @__PURE__ */ jsx14(Box13, { marginTop: 1, flexDirection: "column", children: previewContent().split("\n").map((line, i) => /* @__PURE__ */ jsx14(Text14, { color: theme.info.color, dimColor: true, children: line || " " }, i)) }),
8084
+ /* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(
8085
+ SelectInput5,
8086
+ {
8087
+ items,
8088
+ onSelect: (item) => handleConfirm(item.value)
8089
+ }
8090
+ ) })
8091
+ ] });
8092
+ }
8093
+ }
8094
+ };
8095
+ return /* @__PURE__ */ jsx14(Box13, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: renderStep() });
8096
+ }
8097
+ var NAME_RE;
8098
+ var init_command_wizard = __esm({
8099
+ "src/ui/command-wizard.tsx"() {
8100
+ "use strict";
8101
+ init_text_input();
8102
+ NAME_RE = /^[a-zA-Z][a-zA-Z0-9_\-/]*$/;
8103
+ }
8104
+ });
8105
+
8106
+ // src/ui/command-picker.tsx
8107
+ import { Box as Box14, Text as Text15 } from "ink";
8108
+ import SelectInput6 from "ink-select-input";
8109
+ import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
8110
+ function CommandPicker({ theme, commands, title, onPick }) {
8111
+ const items = commands.map((cmd) => ({
8112
+ label: `/${cmd.name.padEnd(20)} ${cmd.description ?? ""}`,
8113
+ value: cmd,
8114
+ key: cmd.name
8115
+ }));
8116
+ items.push({ label: "\u2190 Cancel", value: null, key: "__cancel__" });
8117
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
8118
+ /* @__PURE__ */ jsx15(Text15, { color: theme.accent, bold: true, children: title }),
8119
+ /* @__PURE__ */ jsx15(Text15, { color: theme.info.color, dimColor: false, children: "Arrow keys to navigate, Enter to select." }),
8120
+ /* @__PURE__ */ jsx15(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx15(
8121
+ SelectInput6,
8122
+ {
8123
+ items,
8124
+ onSelect: (item) => {
8125
+ if (item.key === "__cancel__") {
8126
+ onPick(null);
8127
+ } else {
8128
+ onPick(item.value);
8129
+ }
8130
+ }
8131
+ }
8132
+ ) })
8133
+ ] });
8134
+ }
8135
+ var init_command_picker = __esm({
8136
+ "src/ui/command-picker.tsx"() {
8137
+ "use strict";
8138
+ }
8139
+ });
8140
+
8141
+ // src/ui/command-list.tsx
8142
+ import { Box as Box15, Text as Text16, useInput as useInput4 } from "ink";
8143
+ import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
8144
+ function CommandList({ theme, commands, onDone }) {
8145
+ useInput4((_input, key) => {
8146
+ if (key.escape) {
8147
+ onDone();
8148
+ }
8149
+ });
8150
+ return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
8151
+ /* @__PURE__ */ jsx16(Text16, { color: theme.accent, bold: true, children: "Custom commands" }),
8152
+ /* @__PURE__ */ jsx16(Text16, { color: theme.info.color, dimColor: false, children: "Esc to close." }),
8153
+ /* @__PURE__ */ jsxs15(Box15, { marginTop: 1, flexDirection: "column", children: [
8154
+ commands.length === 0 && /* @__PURE__ */ jsx16(Text16, { color: theme.info.color, dimColor: true, children: "No custom commands found." }),
8155
+ commands.map((cmd) => /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", marginBottom: 1, children: [
8156
+ /* @__PURE__ */ jsxs15(Text16, { color: theme.accent, bold: true, children: [
8157
+ "/",
8158
+ cmd.name
8159
+ ] }),
8160
+ /* @__PURE__ */ jsxs15(Text16, { color: theme.info.color, dimColor: true, children: [
8161
+ " ",
8162
+ "source: ",
8163
+ cmd.source
8164
+ ] }),
8165
+ /* @__PURE__ */ jsxs15(Text16, { color: theme.info.color, dimColor: true, children: [
8166
+ " ",
8167
+ "path: ",
8168
+ cmd.filepath
8169
+ ] }),
8170
+ cmd.description && /* @__PURE__ */ jsxs15(Text16, { color: theme.info.color, dimColor: true, children: [
8171
+ " ",
8172
+ "desc: ",
8173
+ cmd.description
8174
+ ] }),
8175
+ cmd.mode && /* @__PURE__ */ jsxs15(Text16, { color: theme.info.color, dimColor: true, children: [
8176
+ " ",
8177
+ "mode: ",
8178
+ cmd.mode
8179
+ ] }),
8180
+ cmd.effort && /* @__PURE__ */ jsxs15(Text16, { color: theme.info.color, dimColor: true, children: [
8181
+ " ",
8182
+ "effort: ",
8183
+ cmd.effort
8184
+ ] }),
8185
+ cmd.model && /* @__PURE__ */ jsxs15(Text16, { color: theme.info.color, dimColor: true, children: [
8186
+ " ",
8187
+ "model: ",
8188
+ cmd.model
8189
+ ] }),
8190
+ /* @__PURE__ */ jsxs15(Text16, { color: theme.info.color, dimColor: true, children: [
8191
+ " ",
8192
+ "template:"
8193
+ ] }),
8194
+ cmd.template.split("\n").slice(0, 5).map((line, i) => /* @__PURE__ */ jsxs15(Text16, { color: theme.info.color, dimColor: true, children: [
8195
+ " ",
8196
+ line || " "
8197
+ ] }, i)),
8198
+ cmd.template.split("\n").length > 5 && /* @__PURE__ */ jsxs15(Text16, { color: theme.info.color, dimColor: true, children: [
8199
+ " ",
8200
+ "..."
8201
+ ] })
8202
+ ] }, cmd.name))
8203
+ ] })
8204
+ ] });
8205
+ }
8206
+ var init_command_list = __esm({
8207
+ "src/ui/command-list.tsx"() {
8208
+ "use strict";
8209
+ }
8210
+ });
8211
+
7535
8212
  // src/app.tsx
7536
8213
  var app_exports = {};
7537
8214
  __export(app_exports, {
7538
8215
  renderApp: () => renderApp
7539
8216
  });
7540
- import { useState as useState7, useRef as useRef3, useEffect as useEffect4, useCallback } from "react";
7541
- import { Box as Box13, Text as Text14, useApp, useInput as useInput3, render } from "ink";
8217
+ import { useState as useState8, useRef as useRef3, useEffect as useEffect4, useCallback } from "react";
8218
+ import { Box as Box16, Text as Text17, useApp, useInput as useInput5, render } from "ink";
8219
+ import SelectInput7 from "ink-select-input";
7542
8220
  import { existsSync } from "fs";
7543
8221
  import { join as join13 } from "path";
7544
- import { unlink as unlink2 } from "fs/promises";
8222
+ import { unlink as unlink3 } from "fs/promises";
7545
8223
  import { spawn as spawn2 } from "child_process";
7546
8224
  import { platform as platform2 } from "os";
7547
- import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
8225
+ import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
7548
8226
  function gatewayFromConfig(cfg) {
7549
8227
  if (!cfg.aiGatewayId) return void 0;
7550
8228
  return {
@@ -7615,8 +8293,8 @@ function findImagePaths(text) {
7615
8293
  }
7616
8294
  function App({ initialCfg, initialUpdateResult }) {
7617
8295
  const { exit } = useApp();
7618
- const [cfg, setCfg] = useState7(initialCfg);
7619
- const [events, setRawEvents] = useState7([]);
8296
+ const [cfg, setCfg] = useState8(initialCfg);
8297
+ const [events, setRawEvents] = useState8([]);
7620
8298
  const setEvents = useCallback(
7621
8299
  (updater) => {
7622
8300
  setRawEvents((prev) => {
@@ -7626,34 +8304,38 @@ function App({ initialCfg, initialUpdateResult }) {
7626
8304
  },
7627
8305
  []
7628
8306
  );
7629
- const [input, setInput] = useState7("");
7630
- const [busy, setBusy] = useState7(false);
7631
- const [usage, setUsage] = useState7(null);
7632
- const [sessionUsage, setSessionUsage] = useState7(null);
7633
- const [gatewayMeta, setGatewayMeta] = useState7(null);
7634
- const [showReasoning, setShowReasoning] = useState7(false);
7635
- const [perm, setPerm] = useState7(null);
7636
- const [queue, setQueue] = useState7([]);
7637
- const [history, setHistory] = useState7([]);
7638
- const [historyIndex, setHistoryIndex] = useState7(-1);
7639
- const [draftInput, setDraftInput] = useState7("");
7640
- const [mode, setMode] = useState7("edit");
7641
- const [codeMode, setCodeMode] = useState7(initialCfg?.codeMode ?? false);
7642
- const [effort, setEffort] = useState7(
8307
+ const [input, setInput] = useState8("");
8308
+ const [busy, setBusy] = useState8(false);
8309
+ const [usage, setUsage] = useState8(null);
8310
+ const [sessionUsage, setSessionUsage] = useState8(null);
8311
+ const [gatewayMeta, setGatewayMeta] = useState8(null);
8312
+ const [showReasoning, setShowReasoning] = useState8(false);
8313
+ const [perm, setPerm] = useState8(null);
8314
+ const [queue, setQueue] = useState8([]);
8315
+ const [history, setHistory] = useState8([]);
8316
+ const [historyIndex, setHistoryIndex] = useState8(-1);
8317
+ const [draftInput, setDraftInput] = useState8("");
8318
+ const [mode, setMode] = useState8("edit");
8319
+ const [codeMode, setCodeMode] = useState8(initialCfg?.codeMode ?? false);
8320
+ const [effort, setEffort] = useState8(
7643
8321
  initialCfg?.reasoningEffort ?? DEFAULT_REASONING_EFFORT
7644
8322
  );
7645
- const [theme, setTheme] = useState7(resolveTheme(initialCfg?.theme));
7646
- const [resumeSessions, setResumeSessions] = useState7(null);
7647
- const [showThemePicker, setShowThemePicker] = useState7(false);
7648
- const [showHelpMenu, setShowHelpMenu] = useState7(false);
7649
- const [originalTheme, setOriginalTheme] = useState7(null);
7650
- const [tasks, setTasks] = useState7([]);
7651
- const [tasksStartedAt, setTasksStartedAt] = useState7(null);
7652
- const [tasksStartTokens, setTasksStartTokens] = useState7(0);
7653
- const [turnStartedAt, setTurnStartedAt] = useState7(null);
7654
- const [verbose, setVerbose] = useState7(false);
7655
- const [hasUpdate, setHasUpdate] = useState7(initialUpdateResult?.hasUpdate ?? false);
7656
- const [latestVersion, setLatestVersion] = useState7(initialUpdateResult?.latestVersion ?? null);
8323
+ const [theme, setTheme] = useState8(resolveTheme(initialCfg?.theme));
8324
+ const [resumeSessions, setResumeSessions] = useState8(null);
8325
+ const [showThemePicker, setShowThemePicker] = useState8(false);
8326
+ const [showHelpMenu, setShowHelpMenu] = useState8(false);
8327
+ const [originalTheme, setOriginalTheme] = useState8(null);
8328
+ const [commandWizard, setCommandWizard] = useState8(null);
8329
+ const [commandPicker, setCommandPicker] = useState8(null);
8330
+ const [commandToDelete, setCommandToDelete] = useState8(null);
8331
+ const [showCommandList, setShowCommandList] = useState8(false);
8332
+ const [tasks, setTasks] = useState8([]);
8333
+ const [tasksStartedAt, setTasksStartedAt] = useState8(null);
8334
+ const [tasksStartTokens, setTasksStartTokens] = useState8(0);
8335
+ const [turnStartedAt, setTurnStartedAt] = useState8(null);
8336
+ const [verbose, setVerbose] = useState8(false);
8337
+ const [hasUpdate, setHasUpdate] = useState8(initialUpdateResult?.hasUpdate ?? false);
8338
+ const [latestVersion, setLatestVersion] = useState8(initialUpdateResult?.latestVersion ?? null);
7657
8339
  const cacheStableRef = useRef3(initialCfg?.cacheStablePrompts !== false);
7658
8340
  const messagesRef = useRef3(
7659
8341
  makePrefixMessages(cacheStableRef.current, cfg?.model ?? DEFAULT_MODEL, "edit", ALL_TOOLS)
@@ -7677,6 +8359,7 @@ function App({ initialCfg, initialUpdateResult }) {
7677
8359
  const mcpToolsRef = useRef3([]);
7678
8360
  const mcpInitRef = useRef3(false);
7679
8361
  const memoryManagerRef = useRef3(null);
8362
+ const sessionStartRecallRef = useRef3(null);
7680
8363
  const pendingTextRef = useRef3(/* @__PURE__ */ new Map());
7681
8364
  const flushTimeoutRef = useRef3(null);
7682
8365
  const customCommandsRef = useRef3([]);
@@ -7712,6 +8395,7 @@ function App({ initialCfg, initialUpdateResult }) {
7712
8395
  accountId: cfg.accountId,
7713
8396
  apiToken: cfg.apiToken,
7714
8397
  model: cfg.model,
8398
+ plumbingModel: cfg.plumbingModel,
7715
8399
  embeddingModel: cfg.memoryEmbeddingModel,
7716
8400
  gateway: gatewayFromConfig(cfg),
7717
8401
  maxAgeDays: cfg.memoryMaxAgeDays ?? RETENTION.memoryMaxAgeDays,
@@ -7724,7 +8408,7 @@ function App({ initialCfg, initialUpdateResult }) {
7724
8408
  if (total > 0) {
7725
8409
  setEvents((e) => [
7726
8410
  ...e,
7727
- { kind: "info", key: mkKey(), text: `memory cleanup: removed ${total} stale entries` }
8411
+ { kind: "memory", key: mkKey(), text: `memory cleanup: removed ${total} stale entries` }
7728
8412
  ]);
7729
8413
  }
7730
8414
  });
@@ -7732,10 +8416,27 @@ function App({ initialCfg, initialUpdateResult }) {
7732
8416
  if (fixed > 0) {
7733
8417
  setEvents((e) => [
7734
8418
  ...e,
7735
- { kind: "info", key: mkKey(), text: `memory backfill: embedded ${fixed} un-vectorized entries` }
8419
+ { kind: "memory", key: mkKey(), text: `memory backfill: embedded ${fixed} un-vectorized entries` }
7736
8420
  ]);
7737
8421
  }
7738
8422
  });
8423
+ const cwd = process.cwd();
8424
+ sessionStartRecallRef.current = (async () => {
8425
+ try {
8426
+ const results = await manager.recall({ text: cwd, repoPath: cwd, limit: 5 });
8427
+ if (results.length > 0) {
8428
+ const text = await manager.synthesizeRecalled(results);
8429
+ const lastSystemIdx = messagesRef.current.findLastIndex((m) => m.role === "system");
8430
+ const insertIdx = lastSystemIdx >= 0 ? lastSystemIdx + 1 : messagesRef.current.length;
8431
+ messagesRef.current.splice(insertIdx, 0, { role: "system", content: text });
8432
+ setEvents((e) => [
8433
+ ...e,
8434
+ { kind: "memory", key: mkKey(), text: `recalled ${results.length} memory${results.length === 1 ? "" : "ies"} about this repo` }
8435
+ ]);
8436
+ }
8437
+ } catch {
8438
+ }
8439
+ })();
7739
8440
  } else {
7740
8441
  memoryManagerRef.current?.close();
7741
8442
  memoryManagerRef.current = null;
@@ -7754,6 +8455,20 @@ function App({ initialCfg, initialUpdateResult }) {
7754
8455
  }
7755
8456
  });
7756
8457
  }, [cfg, setEvents]);
8458
+ const reloadCustomCommands = useCallback(async () => {
8459
+ const { commands, warnings } = await loadCustomCommands(process.cwd());
8460
+ customCommandsRef.current = commands;
8461
+ for (const w of warnings) {
8462
+ setEvents((e) => [...e, { kind: "info", key: mkKey(), text: `commands: ${w}` }]);
8463
+ }
8464
+ const shadowed = commands.filter((c) => BUILTIN_COMMAND_NAMES.has(c.name.toLowerCase()));
8465
+ for (const c of shadowed) {
8466
+ setEvents((e) => [
8467
+ ...e,
8468
+ { kind: "info", key: mkKey(), text: `commands: /${c.name} (${c.filepath}) shadowed by built-in \u2014 will not run` }
8469
+ ]);
8470
+ }
8471
+ }, [setEvents]);
7757
8472
  useEffect4(() => {
7758
8473
  if (!cfg || updateCheckedRef.current) return;
7759
8474
  updateCheckedRef.current = true;
@@ -7956,12 +8671,13 @@ function App({ initialCfg, initialUpdateResult }) {
7956
8671
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
7957
8672
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
7958
8673
  messages: messagesRef.current,
7959
- sessionState: compiledContextRef.current ? sessionStateRef.current : void 0
8674
+ sessionState: compiledContextRef.current ? sessionStateRef.current : void 0,
8675
+ artifactStore: serializeArtifactStore(artifactStoreRef.current)
7960
8676
  });
7961
8677
  } catch {
7962
8678
  }
7963
8679
  }, [cfg, ensureSessionId]);
7964
- useInput3((inputChar, key) => {
8680
+ useInput5((inputChar, key) => {
7965
8681
  if (key.ctrl && inputChar === "c") {
7966
8682
  if (busy && activeControllerRef.current) {
7967
8683
  activeControllerRef.current.abort();
@@ -8337,8 +9053,26 @@ function App({ initialCfg, initialUpdateResult }) {
8337
9053
  sessionIdRef.current = file.id;
8338
9054
  if (file.sessionState && compiledContextRef.current) {
8339
9055
  sessionStateRef.current = file.sessionState;
9056
+ }
9057
+ if (file.artifactStore) {
9058
+ artifactStoreRef.current = deserializeArtifactStore(file.artifactStore);
9059
+ } else {
8340
9060
  artifactStoreRef.current = new ArtifactStore();
8341
9061
  }
9062
+ const manager = memoryManagerRef.current;
9063
+ if (manager) {
9064
+ try {
9065
+ const cwd = process.cwd();
9066
+ const results = await manager.recall({ text: cwd, repoPath: cwd, limit: 5 });
9067
+ if (results.length > 0) {
9068
+ const text = await manager.synthesizeRecalled(results);
9069
+ const lastSystemIdx = messagesRef.current.findLastIndex((m) => m.role === "system");
9070
+ const insertIdx = lastSystemIdx >= 0 ? lastSystemIdx + 1 : messagesRef.current.length;
9071
+ messagesRef.current.splice(insertIdx, 0, { role: "system", content: text });
9072
+ }
9073
+ } catch {
9074
+ }
9075
+ }
8342
9076
  setEvents([
8343
9077
  {
8344
9078
  kind: "info",
@@ -8656,7 +9390,7 @@ use: /thinking low | medium | high`
8656
9390
  setCfg(next);
8657
9391
  void saveConfig(next).catch(() => {
8658
9392
  });
8659
- setEvents((e) => [...e, { kind: "info", key: mkKey(), text: "memory enabled" }]);
9393
+ setEvents((e) => [...e, { kind: "memory", key: mkKey(), text: "memory enabled" }]);
8660
9394
  return true;
8661
9395
  }
8662
9396
  if (arg === "off") {
@@ -8664,7 +9398,7 @@ use: /thinking low | medium | high`
8664
9398
  setCfg(next);
8665
9399
  void saveConfig(next).catch(() => {
8666
9400
  });
8667
- setEvents((e) => [...e, { kind: "info", key: mkKey(), text: "memory disabled" }]);
9401
+ setEvents((e) => [...e, { kind: "memory", key: mkKey(), text: "memory disabled" }]);
8668
9402
  return true;
8669
9403
  }
8670
9404
  if (!cfg.memoryEnabled) {
@@ -8673,7 +9407,7 @@ use: /thinking low | medium | high`
8673
9407
  }
8674
9408
  if (arg === "clear") {
8675
9409
  const cleared = memoryManagerRef.current?.clearRepo(process.cwd()) ?? 0;
8676
- setEvents((e) => [...e, { kind: "info", key: mkKey(), text: `cleared ${cleared} memories for this repo` }]);
9410
+ setEvents((e) => [...e, { kind: "memory", key: mkKey(), text: `cleared ${cleared} memories for this repo` }]);
8677
9411
  return true;
8678
9412
  }
8679
9413
  if (arg.startsWith("search ")) {
@@ -8808,7 +9542,7 @@ ${lines.join("\n")}` }]);
8808
9542
  return true;
8809
9543
  }
8810
9544
  if (c === "/logout") {
8811
- unlink2(configPath()).catch(() => {
9545
+ unlink3(configPath()).catch(() => {
8812
9546
  });
8813
9547
  setEvents((e) => [
8814
9548
  ...e,
@@ -8817,6 +9551,30 @@ ${lines.join("\n")}` }]);
8817
9551
  setCfg(null);
8818
9552
  return true;
8819
9553
  }
9554
+ if (c === "/command") {
9555
+ const sub = rest[0]?.toLowerCase() ?? "";
9556
+ if (sub === "create") {
9557
+ setCommandWizard({ mode: "create" });
9558
+ return true;
9559
+ }
9560
+ if (sub === "edit") {
9561
+ setCommandPicker({ mode: "edit" });
9562
+ return true;
9563
+ }
9564
+ if (sub === "delete") {
9565
+ setCommandPicker({ mode: "delete" });
9566
+ return true;
9567
+ }
9568
+ if (sub === "list") {
9569
+ setShowCommandList(true);
9570
+ return true;
9571
+ }
9572
+ setEvents((e) => [
9573
+ ...e,
9574
+ { kind: "info", key: mkKey(), text: "usage: /command create | edit | delete | list" }
9575
+ ]);
9576
+ return true;
9577
+ }
8820
9578
  if (c === "/help") {
8821
9579
  setShowHelpMenu(true);
8822
9580
  return true;
@@ -8835,6 +9593,47 @@ ${lines.join("\n")}` }]);
8835
9593
  },
8836
9594
  [handleSlash]
8837
9595
  );
9596
+ const handleCommandSave = useCallback(
9597
+ async (opts2) => {
9598
+ setCommandWizard(null);
9599
+ try {
9600
+ if (commandWizard?.mode === "edit" && commandWizard.initial && commandWizard.initial.name !== opts2.name) {
9601
+ await deleteCustomCommand(commandWizard.initial);
9602
+ }
9603
+ const result = await saveCustomCommand(opts2);
9604
+ await reloadCustomCommands();
9605
+ setEvents((e) => [
9606
+ ...e,
9607
+ { kind: "info", key: mkKey(), text: `saved /${opts2.name} \u2192 ${result.filepath}` }
9608
+ ]);
9609
+ } catch (err) {
9610
+ setEvents((e) => [
9611
+ ...e,
9612
+ { kind: "error", key: mkKey(), text: `failed to save /${opts2.name}: ${err.message}` }
9613
+ ]);
9614
+ }
9615
+ },
9616
+ [commandWizard, reloadCustomCommands, setEvents]
9617
+ );
9618
+ const handleCommandDelete = useCallback(
9619
+ async (cmd) => {
9620
+ setCommandToDelete(null);
9621
+ try {
9622
+ await deleteCustomCommand(cmd);
9623
+ await reloadCustomCommands();
9624
+ setEvents((e) => [
9625
+ ...e,
9626
+ { kind: "info", key: mkKey(), text: `deleted /${cmd.name} (${cmd.filepath})` }
9627
+ ]);
9628
+ } catch (err) {
9629
+ setEvents((e) => [
9630
+ ...e,
9631
+ { kind: "error", key: mkKey(), text: `failed to delete /${cmd.name}: ${err.message}` }
9632
+ ]);
9633
+ }
9634
+ },
9635
+ [reloadCustomCommands, setEvents]
9636
+ );
8838
9637
  const processMessage = useCallback(
8839
9638
  async (text, displayText) => {
8840
9639
  if (!cfg) return;
@@ -8897,6 +9696,10 @@ ${lines.join("\n")}` }]);
8897
9696
  content = parts;
8898
9697
  }
8899
9698
  }
9699
+ if (sessionStartRecallRef.current) {
9700
+ await sessionStartRecallRef.current;
9701
+ sessionStartRecallRef.current = null;
9702
+ }
8900
9703
  setEvents((e) => [...e, { kind: "user", key: mkKey(), text: display, images: images.length > 0 ? images : void 0 }]);
8901
9704
  messagesRef.current.push({ role: "user", content });
8902
9705
  if (compiledContextRef.current) {
@@ -9088,6 +9891,30 @@ ${lines.join("\n")}` }]);
9088
9891
  }
9089
9892
  }
9090
9893
  }
9894
+ const manager = memoryManagerRef.current;
9895
+ if (manager) {
9896
+ try {
9897
+ const cwd = process.cwd();
9898
+ const queryText = sessionStateRef.current.task || cwd;
9899
+ const results = await manager.recall({ text: queryText, repoPath: cwd, limit: 5 });
9900
+ if (results.length > 0) {
9901
+ const text2 = await manager.synthesizeRecalled(results);
9902
+ const lastSystemIdx = messagesRef.current.findLastIndex((m) => m.role === "system");
9903
+ const insertIdx = lastSystemIdx >= 0 ? lastSystemIdx + 1 : messagesRef.current.length;
9904
+ messagesRef.current.splice(insertIdx, 0, { role: "system", content: text2 });
9905
+ setEvents((e) => [
9906
+ ...e,
9907
+ {
9908
+ kind: "memory",
9909
+ key: mkKey(),
9910
+ text: `recalled ${results.length} memory${results.length === 1 ? "" : "ies"} after compaction`
9911
+ }
9912
+ ]);
9913
+ await saveSessionSafe();
9914
+ }
9915
+ } catch {
9916
+ }
9917
+ }
9091
9918
  } catch (e) {
9092
9919
  if (e.name === "AbortError") {
9093
9920
  setEvents((es) => [...es, { kind: "info", key: mkKey(), text: "(aborted)" }]);
@@ -9161,7 +9988,7 @@ ${lines.join("\n")}` }]);
9161
9988
  }
9162
9989
  }, [usage]);
9163
9990
  if (!cfg) {
9164
- return /* @__PURE__ */ jsx14(
9991
+ return /* @__PURE__ */ jsx17(
9165
9992
  Onboarding,
9166
9993
  {
9167
9994
  onDone: (newCfg) => {
@@ -9175,13 +10002,13 @@ ${lines.join("\n")}` }]);
9175
10002
  );
9176
10003
  }
9177
10004
  if (resumeSessions !== null) {
9178
- return /* @__PURE__ */ jsx14(Box13, { flexDirection: "column", children: /* @__PURE__ */ jsx14(ResumePicker, { sessions: resumeSessions, onPick: handleResumePick, theme }) });
10005
+ return /* @__PURE__ */ jsx17(Box16, { flexDirection: "column", children: /* @__PURE__ */ jsx17(ResumePicker, { sessions: resumeSessions, onPick: handleResumePick, theme }) });
9179
10006
  }
9180
10007
  if (showThemePicker) {
9181
- return /* @__PURE__ */ jsx14(Box13, { flexDirection: "column", children: /* @__PURE__ */ jsx14(ThemePicker, { themes: themeList(), current: theme, onPick: handleThemePick, onPreview: (t) => setTheme(t) }) });
10008
+ return /* @__PURE__ */ jsx17(Box16, { flexDirection: "column", children: /* @__PURE__ */ jsx17(ThemePicker, { themes: themeList(), current: theme, onPick: handleThemePick, onPreview: (t) => setTheme(t) }) });
9182
10009
  }
9183
10010
  if (showHelpMenu) {
9184
- return /* @__PURE__ */ jsx14(Box13, { flexDirection: "column", children: /* @__PURE__ */ jsx14(
10011
+ return /* @__PURE__ */ jsx17(Box16, { flexDirection: "column", children: /* @__PURE__ */ jsx17(
9185
10012
  HelpMenu,
9186
10013
  {
9187
10014
  theme,
@@ -9193,10 +10020,79 @@ ${lines.join("\n")}` }]);
9193
10020
  }
9194
10021
  ) });
9195
10022
  }
10023
+ if (commandWizard) {
10024
+ return /* @__PURE__ */ jsx17(Box16, { flexDirection: "column", children: /* @__PURE__ */ jsx17(
10025
+ CommandWizard,
10026
+ {
10027
+ theme,
10028
+ mode: commandWizard.mode,
10029
+ initial: commandWizard.initial,
10030
+ existingNames: customCommandsRef.current.map((c) => c.name),
10031
+ builtinNames: BUILTIN_COMMAND_NAMES,
10032
+ onDone: () => setCommandWizard(null),
10033
+ onSave: handleCommandSave
10034
+ }
10035
+ ) });
10036
+ }
10037
+ if (commandPicker) {
10038
+ return /* @__PURE__ */ jsx17(Box16, { flexDirection: "column", children: /* @__PURE__ */ jsx17(
10039
+ CommandPicker,
10040
+ {
10041
+ theme,
10042
+ commands: customCommandsRef.current,
10043
+ title: commandPicker.mode === "edit" ? "Edit custom command" : "Delete custom command",
10044
+ onPick: (cmd) => {
10045
+ setCommandPicker(null);
10046
+ if (!cmd) return;
10047
+ if (commandPicker.mode === "edit") {
10048
+ setCommandWizard({ mode: "edit", initial: cmd });
10049
+ } else {
10050
+ setCommandToDelete(cmd);
10051
+ }
10052
+ }
10053
+ }
10054
+ ) });
10055
+ }
10056
+ if (commandToDelete) {
10057
+ return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
10058
+ /* @__PURE__ */ jsxs16(Text17, { color: theme.accent, bold: true, children: [
10059
+ "Delete /",
10060
+ commandToDelete.name,
10061
+ "?"
10062
+ ] }),
10063
+ /* @__PURE__ */ jsx17(Text17, { color: theme.info.color, dimColor: true, children: commandToDelete.filepath }),
10064
+ /* @__PURE__ */ jsx17(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx17(
10065
+ SelectInput7,
10066
+ {
10067
+ items: [
10068
+ { label: "Yes, delete", value: "yes", key: "yes" },
10069
+ { label: "Cancel", value: "cancel", key: "cancel" }
10070
+ ],
10071
+ onSelect: (item) => {
10072
+ if (item.value === "yes") {
10073
+ void handleCommandDelete(commandToDelete);
10074
+ } else {
10075
+ setCommandToDelete(null);
10076
+ }
10077
+ }
10078
+ }
10079
+ ) })
10080
+ ] });
10081
+ }
10082
+ if (showCommandList) {
10083
+ return /* @__PURE__ */ jsx17(Box16, { flexDirection: "column", children: /* @__PURE__ */ jsx17(
10084
+ CommandList,
10085
+ {
10086
+ theme,
10087
+ commands: customCommandsRef.current,
10088
+ onDone: () => setShowCommandList(false)
10089
+ }
10090
+ ) });
10091
+ }
9196
10092
  const hasConversation = events.some((e) => e.kind === "user" || e.kind === "assistant");
9197
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
9198
- !hasConversation && events.length === 0 ? /* @__PURE__ */ jsx14(Welcome, { theme, accountId: cfg.accountId }) : /* @__PURE__ */ jsx14(ChatView, { events, showReasoning, theme, verbose }),
9199
- perm ? /* @__PURE__ */ jsx14(
10093
+ return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", children: [
10094
+ !hasConversation && events.length === 0 ? /* @__PURE__ */ jsx17(Welcome, { theme, accountId: cfg.accountId }) : /* @__PURE__ */ jsx17(ChatView, { events, showReasoning, theme, verbose }),
10095
+ perm ? /* @__PURE__ */ jsx17(
9200
10096
  PermissionModal,
9201
10097
  {
9202
10098
  tool: perm.tool,
@@ -9207,8 +10103,8 @@ ${lines.join("\n")}` }]);
9207
10103
  setPerm(null);
9208
10104
  }
9209
10105
  }
9210
- ) : /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", marginTop: 1, children: [
9211
- tasks.length > 0 && /* @__PURE__ */ jsx14(
10106
+ ) : /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", marginTop: 1, children: [
10107
+ tasks.length > 0 && /* @__PURE__ */ jsx17(
9212
10108
  TaskList,
9213
10109
  {
9214
10110
  tasks,
@@ -9217,11 +10113,11 @@ ${lines.join("\n")}` }]);
9217
10113
  tokensDelta: Math.max(0, (usage?.prompt_tokens ?? 0) - tasksStartTokens)
9218
10114
  }
9219
10115
  ),
9220
- queue.length > 0 && /* @__PURE__ */ jsx14(Box13, { flexDirection: "column", marginBottom: 1, children: queue.map((q, i) => /* @__PURE__ */ jsxs13(Text14, { color: theme.queue.color, dimColor: theme.queue.dim, children: [
10116
+ queue.length > 0 && /* @__PURE__ */ jsx17(Box16, { flexDirection: "column", marginBottom: 1, children: queue.map((q, i) => /* @__PURE__ */ jsxs16(Text17, { color: theme.queue.color, dimColor: theme.queue.dim, children: [
9221
10117
  "\u23F3 ",
9222
10118
  q.display
9223
10119
  ] }, `queue_${i}`)) }),
9224
- /* @__PURE__ */ jsx14(
10120
+ /* @__PURE__ */ jsx17(
9225
10121
  StatusBar,
9226
10122
  {
9227
10123
  model: cfg.model,
@@ -9239,9 +10135,9 @@ ${lines.join("\n")}` }]);
9239
10135
  codeMode
9240
10136
  }
9241
10137
  ),
9242
- /* @__PURE__ */ jsxs13(Box13, { marginTop: 1, children: [
9243
- /* @__PURE__ */ jsx14(Text14, { color: theme.accent, children: "\u203A " }),
9244
- /* @__PURE__ */ jsx14(
10138
+ /* @__PURE__ */ jsxs16(Box16, { marginTop: 1, children: [
10139
+ /* @__PURE__ */ jsx17(Text17, { color: theme.accent, children: "\u203A " }),
10140
+ /* @__PURE__ */ jsx17(
9245
10141
  CustomTextInput,
9246
10142
  {
9247
10143
  value: input,
@@ -9290,7 +10186,7 @@ ${lines.join("\n")}` }]);
9290
10186
  ] });
9291
10187
  }
9292
10188
  async function renderApp(cfg, updateResult) {
9293
- const instance = render(/* @__PURE__ */ jsx14(App, { initialCfg: cfg, initialUpdateResult: updateResult }), {
10189
+ const instance = render(/* @__PURE__ */ jsx17(App, { initialCfg: cfg, initialUpdateResult: updateResult }), {
9294
10190
  incrementalRendering: true
9295
10191
  });
9296
10192
  await instance.waitUntilExit();
@@ -9331,6 +10227,10 @@ var init_app = __esm({
9331
10227
  init_version();
9332
10228
  init_loader();
9333
10229
  init_renderer();
10230
+ init_save();
10231
+ init_command_wizard();
10232
+ init_command_picker();
10233
+ init_command_list();
9334
10234
  FEEDBACK_WORKER_URL = "https://kimiflare-feedback.sina-b35.workers.dev";
9335
10235
  CONTEXT_LIMIT = 262e3;
9336
10236
  AUTO_COMPACT_SUGGEST_PCT = 0.8;