kimiflare 0.7.0 → 0.8.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
@@ -142,7 +142,33 @@ var init_errors = __esm({
142
142
  }
143
143
  });
144
144
 
145
+ // src/agent/messages.ts
146
+ function sanitizeString(str) {
147
+ return str.replace(/[\uD800-\uDFFF]/g, "\uFFFD");
148
+ }
149
+ function jsonReplacer(_key, value) {
150
+ if (typeof value === "string") {
151
+ return sanitizeString(value);
152
+ }
153
+ return value;
154
+ }
155
+ var init_messages = __esm({
156
+ "src/agent/messages.ts"() {
157
+ "use strict";
158
+ }
159
+ });
160
+
145
161
  // src/agent/client.ts
162
+ function cleanErrorMessage(msg) {
163
+ return msg.replace(/^(AiError:\s*)+/, "").trim();
164
+ }
165
+ function isRetryable(err, attempt) {
166
+ if (attempt >= MAX_ATTEMPTS - 1) return false;
167
+ if (err.code !== void 0 && RETRYABLE_CODES.has(err.code)) return true;
168
+ if (err.httpStatus !== void 0 && err.httpStatus >= 500 && err.httpStatus < 600) return true;
169
+ if (err.message.includes("Internal server error")) return true;
170
+ return false;
171
+ }
146
172
  async function* runKimi(opts2) {
147
173
  const url = `https://api.cloudflare.com/client/v4/accounts/${opts2.accountId}/ai/run/${opts2.model}`;
148
174
  const body = {
@@ -156,15 +182,26 @@ async function* runKimi(opts2) {
156
182
  body.reasoning_effort = opts2.reasoningEffort;
157
183
  }
158
184
  for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
159
- const res = await fetch(url, {
160
- method: "POST",
161
- headers: {
162
- Authorization: `Bearer ${opts2.apiToken}`,
163
- "Content-Type": "application/json"
164
- },
165
- body: JSON.stringify(body),
166
- signal: opts2.signal
167
- });
185
+ let res;
186
+ try {
187
+ res = await fetch(url, {
188
+ method: "POST",
189
+ headers: {
190
+ Authorization: `Bearer ${opts2.apiToken}`,
191
+ "Content-Type": "application/json"
192
+ },
193
+ body: JSON.stringify(body, jsonReplacer),
194
+ signal: opts2.signal
195
+ });
196
+ } catch (fetchErr) {
197
+ const msg = fetchErr instanceof Error ? fetchErr.message : String(fetchErr);
198
+ if (attempt < MAX_ATTEMPTS - 1) {
199
+ const delay = 500 * 2 ** attempt + Math.random() * 250;
200
+ await sleep(delay, opts2.signal);
201
+ continue;
202
+ }
203
+ throw new KimiApiError(`kimiflare: network error: ${msg}`, void 0, void 0);
204
+ }
168
205
  const contentType = res.headers.get("content-type") ?? "";
169
206
  if (!contentType.includes("text/event-stream")) {
170
207
  const text = await res.text();
@@ -174,13 +211,15 @@ async function* runKimi(opts2) {
174
211
  } catch {
175
212
  }
176
213
  const err = extractCloudflareError(parsed);
177
- if (err?.code === RETRYABLE_CODE && attempt < MAX_ATTEMPTS - 1) {
214
+ const rawMsg = err?.message ?? `HTTP ${res.status}: ${text.slice(0, 300)}`;
215
+ const msg = cleanErrorMessage(rawMsg);
216
+ const apiErr = new KimiApiError(`kimiflare: ${msg}`, err?.code, res.status);
217
+ if (isRetryable(apiErr, attempt)) {
178
218
  const delay = 500 * 2 ** attempt + Math.random() * 250;
179
219
  await sleep(delay, opts2.signal);
180
220
  continue;
181
221
  }
182
- const msg = err?.message ?? `HTTP ${res.status}: ${text.slice(0, 300)}`;
183
- throw new KimiApiError(`kimiflare: ${msg}`, err?.code, res.status);
222
+ throw apiErr;
184
223
  }
185
224
  if (!res.body) throw new KimiApiError("kimiflare: empty response body", void 0, res.status);
186
225
  yield* parseStream(res.body, opts2.signal);
@@ -257,9 +296,14 @@ async function* parseStream(body, signal) {
257
296
  }
258
297
  function extractCloudflareError(parsed) {
259
298
  if (!parsed || typeof parsed !== "object") return null;
260
- const p = parsed;
261
- if (p.success === false && Array.isArray(p.errors) && p.errors.length > 0) {
262
- return { code: p.errors[0]?.code, message: p.errors[0]?.message };
299
+ const cf = parsed;
300
+ if (cf.success === false && Array.isArray(cf.errors) && cf.errors.length > 0) {
301
+ return { code: cf.errors[0]?.code, message: cf.errors[0]?.message };
302
+ }
303
+ const oai = parsed;
304
+ if (oai.object === "error" && typeof oai.message === "string") {
305
+ const codeNum = typeof oai.code === "number" ? oai.code : void 0;
306
+ return { code: codeNum, message: oai.message };
263
307
  }
264
308
  return null;
265
309
  }
@@ -277,13 +321,14 @@ function sleep(ms, signal) {
277
321
  signal?.addEventListener("abort", onAbort, { once: true });
278
322
  });
279
323
  }
280
- var RETRYABLE_CODE, MAX_ATTEMPTS;
324
+ var RETRYABLE_CODES, MAX_ATTEMPTS;
281
325
  var init_client = __esm({
282
326
  "src/agent/client.ts"() {
283
327
  "use strict";
284
328
  init_sse();
285
329
  init_errors();
286
- RETRYABLE_CODE = 3040;
330
+ init_messages();
331
+ RETRYABLE_CODES = /* @__PURE__ */ new Set([3040]);
287
332
  MAX_ATTEMPTS = 5;
288
333
  }
289
334
  });
@@ -360,9 +405,17 @@ async function runAgentTurn(opts2) {
360
405
  }
361
406
  const assistantMsg = {
362
407
  role: "assistant",
363
- content: content || null,
364
- ...reasoning ? { reasoning_content: reasoning } : {},
365
- ...toolCalls.length ? { tool_calls: toolCalls } : {}
408
+ content: content ? sanitizeString(content) : null,
409
+ ...reasoning ? { reasoning_content: sanitizeString(reasoning) } : {},
410
+ ...toolCalls.length ? {
411
+ tool_calls: toolCalls.map((tc) => ({
412
+ ...tc,
413
+ function: {
414
+ name: tc.function.name,
415
+ arguments: sanitizeString(tc.function.arguments)
416
+ }
417
+ }))
418
+ } : {}
366
419
  };
367
420
  opts2.messages.push(assistantMsg);
368
421
  opts2.callbacks.onAssistantFinal?.(assistantMsg);
@@ -377,7 +430,7 @@ async function runAgentTurn(opts2) {
377
430
  opts2.messages.push({
378
431
  role: "tool",
379
432
  tool_call_id: result.tool_call_id,
380
- content: result.content,
433
+ content: sanitizeString(result.content),
381
434
  name: result.name
382
435
  });
383
436
  opts2.callbacks.onToolResult?.(result);
@@ -390,6 +443,7 @@ var init_loop = __esm({
390
443
  "use strict";
391
444
  init_client();
392
445
  init_registry();
446
+ init_messages();
393
447
  }
394
448
  });
395
449
 
@@ -1118,6 +1172,120 @@ var init_executor = __esm({
1118
1172
  }
1119
1173
  });
1120
1174
 
1175
+ // src/util/update-check.ts
1176
+ import { readFile as readFile6, writeFile as writeFile4, mkdir as mkdir3, access } from "fs/promises";
1177
+ import { homedir as homedir4 } from "os";
1178
+ import { join as join3, dirname as dirname2 } from "path";
1179
+ import { fileURLToPath } from "url";
1180
+ function cachePath() {
1181
+ const xdg = process.env.XDG_CONFIG_HOME || join3(homedir4(), ".config");
1182
+ return join3(xdg, "kimiflare", "update-check.json");
1183
+ }
1184
+ async function findPackageJson(startDir) {
1185
+ let dir = startDir;
1186
+ while (true) {
1187
+ const candidate = join3(dir, "package.json");
1188
+ try {
1189
+ const raw = await readFile6(candidate, "utf8");
1190
+ const parsed = JSON.parse(raw);
1191
+ if (parsed.name === "kimiflare" && parsed.version) {
1192
+ return { path: candidate, version: parsed.version };
1193
+ }
1194
+ } catch {
1195
+ }
1196
+ const parent = dirname2(dir);
1197
+ if (parent === dir) break;
1198
+ dir = parent;
1199
+ }
1200
+ return null;
1201
+ }
1202
+ async function readLocalVersion() {
1203
+ const here = dirname2(fileURLToPath(import.meta.url));
1204
+ const found = await findPackageJson(here);
1205
+ return found?.version ?? null;
1206
+ }
1207
+ async function readCache() {
1208
+ try {
1209
+ const raw = await readFile6(cachePath(), "utf8");
1210
+ const parsed = JSON.parse(raw);
1211
+ if (Date.now() - parsed.checkedAt < CACHE_TTL_MS) {
1212
+ return parsed;
1213
+ }
1214
+ } catch {
1215
+ }
1216
+ return null;
1217
+ }
1218
+ async function writeCache(entry) {
1219
+ const p = cachePath();
1220
+ await mkdir3(dirname2(p), { recursive: true });
1221
+ await writeFile4(p, JSON.stringify(entry), "utf8");
1222
+ }
1223
+ async function fetchLatestVersion() {
1224
+ try {
1225
+ const res = await fetch(NPM_REGISTRY, {
1226
+ headers: { "User-Agent": "kimiflare-update-checker", Accept: "application/json" }
1227
+ });
1228
+ if (!res.ok) return null;
1229
+ const data = await res.json();
1230
+ return data.version ?? null;
1231
+ } catch {
1232
+ return null;
1233
+ }
1234
+ }
1235
+ function stripV(v) {
1236
+ return v.startsWith("v") ? v.slice(1) : v;
1237
+ }
1238
+ function isNewer(local, remote) {
1239
+ const a = stripV(local).split(".").map(Number);
1240
+ const b = stripV(remote).split(".").map(Number);
1241
+ for (let i = 0; i < Math.max(a.length, b.length); i++) {
1242
+ const av = a[i] ?? 0;
1243
+ const bv = b[i] ?? 0;
1244
+ if (av < bv) return true;
1245
+ if (av > bv) return false;
1246
+ }
1247
+ return false;
1248
+ }
1249
+ async function checkForUpdate(force = false) {
1250
+ const localVersion = await readLocalVersion();
1251
+ if (!localVersion) return { hasUpdate: false, localVersion: null, latestVersion: null };
1252
+ if (!force) {
1253
+ const cached = await readCache();
1254
+ if (cached) {
1255
+ return { hasUpdate: cached.hasUpdate, localVersion, latestVersion: cached.latestVersion };
1256
+ }
1257
+ }
1258
+ const latestVersion = await fetchLatestVersion();
1259
+ if (!latestVersion) {
1260
+ return { hasUpdate: false, localVersion, latestVersion: null };
1261
+ }
1262
+ const hasUpdate = isNewer(localVersion, latestVersion);
1263
+ await writeCache({ checkedAt: Date.now(), latestVersion, hasUpdate });
1264
+ return { hasUpdate, localVersion, latestVersion };
1265
+ }
1266
+ async function isGitRepo() {
1267
+ let dir = dirname2(fileURLToPath(import.meta.url));
1268
+ while (true) {
1269
+ try {
1270
+ await access(join3(dir, ".git"));
1271
+ return true;
1272
+ } catch {
1273
+ }
1274
+ const parent = dirname2(dir);
1275
+ if (parent === dir) break;
1276
+ dir = parent;
1277
+ }
1278
+ return false;
1279
+ }
1280
+ var CACHE_TTL_MS, NPM_REGISTRY;
1281
+ var init_update_check = __esm({
1282
+ "src/util/update-check.ts"() {
1283
+ "use strict";
1284
+ CACHE_TTL_MS = 60 * 60 * 1e3;
1285
+ NPM_REGISTRY = "https://registry.npmjs.org/kimiflare/latest";
1286
+ }
1287
+ });
1288
+
1121
1289
  // src/agent/compact.ts
1122
1290
  function indexOfNthUserFromEnd(messages, n) {
1123
1291
  let seen = 0;
@@ -1530,7 +1698,7 @@ import { useEffect, useState } from "react";
1530
1698
  import { Box as Box5, Text as Text5 } from "ink";
1531
1699
  import Spinner3 from "ink-spinner";
1532
1700
  import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1533
- function StatusBar({ model, usage, thinking, turnStartedAt, theme, mode, effort, contextLimit }) {
1701
+ function StatusBar({ model, usage, thinking, turnStartedAt, theme, mode, effort, contextLimit, hasUpdate, latestVersion }) {
1534
1702
  const [now, setNow] = useState(Date.now());
1535
1703
  const modeColor = mode === "plan" ? theme.modeBadge.plan : mode === "auto" ? theme.modeBadge.auto : theme.modeBadge.edit;
1536
1704
  const warn = usage && usage.prompt_tokens / contextLimit >= 0.8;
@@ -1564,6 +1732,12 @@ function StatusBar({ model, usage, thinking, turnStartedAt, theme, mode, effort,
1564
1732
  warn ? /* @__PURE__ */ jsxs5(Text5, { color: theme.warn, bold: true, children: [
1565
1733
  " \xB7 ",
1566
1734
  "/compact recommended"
1735
+ ] }) : null,
1736
+ hasUpdate ? /* @__PURE__ */ jsxs5(Text5, { color: theme.warn, bold: true, children: [
1737
+ " \xB7 ",
1738
+ "update available",
1739
+ latestVersion ? ` \u2192 ${latestVersion}` : "",
1740
+ " \xB7 run /update"
1567
1741
  ] }) : null
1568
1742
  ] })
1569
1743
  ] });
@@ -1637,10 +1811,16 @@ var init_permission = __esm({
1637
1811
  });
1638
1812
 
1639
1813
  // src/ui/resume-picker.tsx
1640
- import { Box as Box7, Text as Text7 } from "ink";
1814
+ import { useState as useState2 } from "react";
1815
+ import { Box as Box7, Text as Text7, useWindowSize } from "ink";
1641
1816
  import SelectInput2 from "ink-select-input";
1642
1817
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1643
1818
  function ResumePicker({ sessions, onPick, theme }) {
1819
+ const { rows } = useWindowSize();
1820
+ const [page, setPage] = useState2(0);
1821
+ const pageSize = Math.max(MIN_PAGE_SIZE, rows - HEADER_ROWS - FOOTER_ROWS);
1822
+ const totalPages = Math.max(1, Math.ceil(sessions.length / pageSize));
1823
+ const safePage = Math.min(page, totalPages - 1);
1644
1824
  if (sessions.length === 0) {
1645
1825
  return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
1646
1826
  /* @__PURE__ */ jsx7(Text7, { color: theme.accent, bold: true, children: "Resume a session" }),
@@ -1654,20 +1834,39 @@ function ResumePicker({ sessions, onPick, theme }) {
1654
1834
  ) })
1655
1835
  ] });
1656
1836
  }
1657
- const items = sessions.map((s) => ({
1837
+ const start = safePage * pageSize;
1838
+ const end = Math.min(start + pageSize, sessions.length);
1839
+ const pageSessions = sessions.slice(start, end);
1840
+ const items = pageSessions.map((s) => ({
1658
1841
  label: `${formatDate(s.updatedAt)} \xB7 ${s.messageCount} msgs \xB7 ${s.firstPrompt}`,
1659
1842
  value: s.id
1660
1843
  }));
1844
+ if (safePage > 0) {
1845
+ items.push({ label: "\u2190 previous page", value: "__prev__" });
1846
+ }
1847
+ if (safePage < totalPages - 1) {
1848
+ items.push({ label: "\u2192 next page", value: "__next__" });
1849
+ }
1661
1850
  items.push({ label: "(cancel)", value: "__cancel__" });
1662
1851
  return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
1663
1852
  /* @__PURE__ */ jsx7(Text7, { color: theme.accent, bold: true, children: "Resume a session" }),
1664
- /* @__PURE__ */ jsx7(Text7, { color: theme.info.color, dimColor: theme.info.dim, children: "Arrow keys to select, Enter to confirm." }),
1853
+ /* @__PURE__ */ jsxs7(Text7, { color: theme.info.color, dimColor: theme.info.dim, children: [
1854
+ "Arrow keys to select, Enter to confirm. Page ",
1855
+ safePage + 1,
1856
+ " of ",
1857
+ totalPages,
1858
+ " (",
1859
+ sessions.length,
1860
+ " total)"
1861
+ ] }),
1665
1862
  /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(
1666
1863
  SelectInput2,
1667
1864
  {
1668
1865
  items,
1669
1866
  onSelect: (item) => {
1670
1867
  if (item.value === "__cancel__") return onPick(null);
1868
+ if (item.value === "__prev__") return setPage((p) => Math.max(0, p - 1));
1869
+ if (item.value === "__next__") return setPage((p) => Math.min(totalPages - 1, p + 1));
1671
1870
  const picked = sessions.find((s) => s.id === item.value) ?? null;
1672
1871
  onPick(picked);
1673
1872
  }
@@ -1688,19 +1887,70 @@ function formatDate(iso) {
1688
1887
  return iso;
1689
1888
  }
1690
1889
  }
1890
+ var HEADER_ROWS, FOOTER_ROWS, MIN_PAGE_SIZE;
1691
1891
  var init_resume_picker = __esm({
1692
1892
  "src/ui/resume-picker.tsx"() {
1693
1893
  "use strict";
1894
+ HEADER_ROWS = 5;
1895
+ FOOTER_ROWS = 2;
1896
+ MIN_PAGE_SIZE = 5;
1694
1897
  }
1695
1898
  });
1696
1899
 
1697
- // src/ui/task-list.tsx
1698
- import { useEffect as useEffect2, useState as useState2 } from "react";
1900
+ // src/ui/theme-picker.tsx
1699
1901
  import { Box as Box8, Text as Text8 } from "ink";
1700
- import Spinner4 from "ink-spinner";
1902
+ import SelectInput3 from "ink-select-input";
1701
1903
  import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1904
+ function ThemePicker({ themes, current, onPick }) {
1905
+ const items = themes.map((t) => ({
1906
+ label: t.label,
1907
+ value: t.name,
1908
+ key: t.name
1909
+ }));
1910
+ items.push({ label: "(cancel)", value: "__cancel__", key: "__cancel__" });
1911
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", borderStyle: "round", borderColor: current.accent, paddingX: 1, children: [
1912
+ /* @__PURE__ */ jsx8(Text8, { color: current.accent, bold: true, children: "Pick a theme" }),
1913
+ /* @__PURE__ */ jsx8(Text8, { color: current.info.color, dimColor: current.info.dim, children: "Arrow keys to preview, Enter to confirm." }),
1914
+ /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(
1915
+ SelectInput3,
1916
+ {
1917
+ items,
1918
+ onHighlight: (item) => {
1919
+ if (item.value !== "__cancel__") {
1920
+ const highlighted = themes.find((t) => t.name === item.value);
1921
+ if (highlighted) onPick(highlighted);
1922
+ }
1923
+ },
1924
+ onSelect: (item) => {
1925
+ if (item.value === "__cancel__") return onPick(null);
1926
+ const picked = themes.find((t) => t.name === item.value) ?? null;
1927
+ onPick(picked);
1928
+ },
1929
+ itemComponent: ({ label, isSelected }) => {
1930
+ const theme = themes.find((t) => t.label === label);
1931
+ const color = theme?.accent ?? current.accent;
1932
+ return /* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsxs8(Text8, { color, bold: isSelected, dimColor: !isSelected, children: [
1933
+ isSelected ? "\u203A " : " ",
1934
+ label
1935
+ ] }) });
1936
+ }
1937
+ }
1938
+ ) })
1939
+ ] });
1940
+ }
1941
+ var init_theme_picker = __esm({
1942
+ "src/ui/theme-picker.tsx"() {
1943
+ "use strict";
1944
+ }
1945
+ });
1946
+
1947
+ // src/ui/task-list.tsx
1948
+ import { useEffect as useEffect2, useState as useState3 } from "react";
1949
+ import { Box as Box9, Text as Text9 } from "ink";
1950
+ import Spinner4 from "ink-spinner";
1951
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1702
1952
  function TaskList({ tasks, theme, startedAt, tokensDelta }) {
1703
- const [now, setNow] = useState2(Date.now());
1953
+ const [now, setNow] = useState3(Date.now());
1704
1954
  useEffect2(() => {
1705
1955
  if (startedAt === null) return;
1706
1956
  const allDone2 = tasks.length > 0 && tasks.every((t) => t.status === "completed");
@@ -1718,18 +1968,18 @@ function TaskList({ tasks, theme, startedAt, tokensDelta }) {
1718
1968
  const headerStats = [elapsed, tokensDelta > 0 ? `\u2191 ${formatTokens(tokensDelta)} tokens` : null].filter(Boolean).join(" \xB7 ");
1719
1969
  const visibleTasks = tasks.slice(0, MAX_VISIBLE);
1720
1970
  const hiddenPending = Math.max(0, tasks.length - visibleTasks.length);
1721
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginBottom: 1, children: [
1722
- /* @__PURE__ */ jsxs8(Box8, { children: [
1723
- /* @__PURE__ */ jsx8(Text8, { color: allDone ? "green" : theme.accent, bold: true, children: header }),
1724
- headerStats && /* @__PURE__ */ jsxs8(Text8, { color: theme.info.color, dimColor: theme.info.dim, children: [
1971
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, children: [
1972
+ /* @__PURE__ */ jsxs9(Box9, { children: [
1973
+ /* @__PURE__ */ jsx9(Text9, { color: allDone ? "green" : theme.accent, bold: true, children: header }),
1974
+ headerStats && /* @__PURE__ */ jsxs9(Text9, { color: theme.info.color, dimColor: theme.info.dim, children: [
1725
1975
  " ",
1726
1976
  "(",
1727
1977
  headerStats,
1728
1978
  ")"
1729
1979
  ] })
1730
1980
  ] }),
1731
- visibleTasks.map((t) => /* @__PURE__ */ jsx8(TaskRow, { task: t, theme }, t.id)),
1732
- hiddenPending > 0 && /* @__PURE__ */ jsxs8(Text8, { color: theme.info.color, dimColor: theme.info.dim, children: [
1981
+ visibleTasks.map((t) => /* @__PURE__ */ jsx9(TaskRow, { task: t, theme }, t.id)),
1982
+ hiddenPending > 0 && /* @__PURE__ */ jsxs9(Text9, { color: theme.info.color, dimColor: theme.info.dim, children: [
1733
1983
  " ",
1734
1984
  "\u2026 +",
1735
1985
  hiddenPending,
@@ -1739,21 +1989,21 @@ function TaskList({ tasks, theme, startedAt, tokensDelta }) {
1739
1989
  }
1740
1990
  function TaskRow({ task, theme }) {
1741
1991
  if (task.status === "completed") {
1742
- return /* @__PURE__ */ jsxs8(Text8, { color: theme.info.color, dimColor: theme.info.dim, children: [
1992
+ return /* @__PURE__ */ jsxs9(Text9, { color: theme.info.color, dimColor: theme.info.dim, children: [
1743
1993
  " ",
1744
1994
  "\u2713 ",
1745
- /* @__PURE__ */ jsx8(Text8, { strikethrough: true, children: task.title })
1995
+ /* @__PURE__ */ jsx9(Text9, { strikethrough: true, children: task.title })
1746
1996
  ] });
1747
1997
  }
1748
1998
  if (task.status === "in_progress") {
1749
- return /* @__PURE__ */ jsxs8(Text8, { color: theme.accent, bold: true, children: [
1999
+ return /* @__PURE__ */ jsxs9(Text9, { color: theme.accent, bold: true, children: [
1750
2000
  " ",
1751
- /* @__PURE__ */ jsx8(Spinner4, { type: "dots" }),
2001
+ /* @__PURE__ */ jsx9(Spinner4, { type: "dots" }),
1752
2002
  " ",
1753
2003
  task.title
1754
2004
  ] });
1755
2005
  }
1756
- return /* @__PURE__ */ jsxs8(Text8, { color: theme.info.color, dimColor: theme.info.dim, children: [
2006
+ return /* @__PURE__ */ jsxs9(Text9, { color: theme.info.color, dimColor: theme.info.dim, children: [
1757
2007
  " ",
1758
2008
  "\u2610 ",
1759
2009
  task.title
@@ -2299,9 +2549,9 @@ var init_source = __esm({
2299
2549
  });
2300
2550
 
2301
2551
  // src/ui/text-input.tsx
2302
- import { useState as useState3, useEffect as useEffect3, useRef } from "react";
2303
- import { Text as Text9, useInput } from "ink";
2304
- import { jsx as jsx9 } from "react/jsx-runtime";
2552
+ import { useState as useState4, useEffect as useEffect3, useRef } from "react";
2553
+ import { Text as Text10, useInput } from "ink";
2554
+ import { jsx as jsx10 } from "react/jsx-runtime";
2305
2555
  function shouldTreatAsPaste(input) {
2306
2556
  if (input.length >= PASTE_CHAR_THRESHOLD) return true;
2307
2557
  const newlines = (input.match(/\n/g) ?? []).length;
@@ -2334,7 +2584,7 @@ function CustomTextInput({
2334
2584
  mask,
2335
2585
  enablePaste = false
2336
2586
  }) {
2337
- const [cursorOffset, setCursorOffset] = useState3(value.length);
2587
+ const [cursorOffset, setCursorOffset] = useState4(value.length);
2338
2588
  const pastesRef = useRef(/* @__PURE__ */ new Map());
2339
2589
  useEffect3(() => {
2340
2590
  if (!focus) return;
@@ -2478,7 +2728,7 @@ function CustomTextInput({
2478
2728
  } else if (cursorOffset === displayValue.length) {
2479
2729
  renderedValue += source_default.inverse(" ");
2480
2730
  }
2481
- return /* @__PURE__ */ jsx9(Text9, { children: renderedValue });
2731
+ return /* @__PURE__ */ jsx10(Text10, { children: renderedValue });
2482
2732
  }
2483
2733
  function findPasteTokenEndingAt(value, pos, pastes) {
2484
2734
  if (pos <= 0 || value[pos - 1] !== "]") return -1;
@@ -2499,128 +2749,16 @@ var init_text_input = __esm({
2499
2749
  }
2500
2750
  });
2501
2751
 
2502
- // src/util/update-check.ts
2503
- import { readFile as readFile6, writeFile as writeFile4, mkdir as mkdir3, access } from "fs/promises";
2504
- import { homedir as homedir4 } from "os";
2505
- import { join as join3, dirname as dirname2 } from "path";
2506
- import { fileURLToPath } from "url";
2507
- function cachePath() {
2508
- const xdg = process.env.XDG_CONFIG_HOME || join3(homedir4(), ".config");
2509
- return join3(xdg, "kimiflare", "update-check.json");
2510
- }
2511
- async function findPackageJson(startDir) {
2512
- let dir = startDir;
2513
- while (true) {
2514
- const candidate = join3(dir, "package.json");
2515
- try {
2516
- const raw = await readFile6(candidate, "utf8");
2517
- const parsed = JSON.parse(raw);
2518
- if (parsed.name === "kimiflare" && parsed.version) {
2519
- return { path: candidate, version: parsed.version };
2520
- }
2521
- } catch {
2522
- }
2523
- const parent = dirname2(dir);
2524
- if (parent === dir) break;
2525
- dir = parent;
2526
- }
2527
- return null;
2528
- }
2529
- async function readLocalVersion() {
2530
- const here = dirname2(fileURLToPath(import.meta.url));
2531
- const found = await findPackageJson(here);
2532
- return found?.version ?? null;
2533
- }
2534
- async function readCache() {
2535
- try {
2536
- const raw = await readFile6(cachePath(), "utf8");
2537
- const parsed = JSON.parse(raw);
2538
- if (Date.now() - parsed.checkedAt < CACHE_TTL_MS) {
2539
- return parsed;
2540
- }
2541
- } catch {
2542
- }
2543
- return null;
2544
- }
2545
- async function writeCache(entry) {
2546
- const p = cachePath();
2547
- await mkdir3(dirname2(p), { recursive: true });
2548
- await writeFile4(p, JSON.stringify(entry), "utf8");
2549
- }
2550
- async function fetchLatestVersion() {
2551
- try {
2552
- const res = await fetch(NPM_REGISTRY, {
2553
- headers: { "User-Agent": "kimiflare-update-checker", Accept: "application/json" }
2554
- });
2555
- if (!res.ok) return null;
2556
- const data = await res.json();
2557
- return data.version ?? null;
2558
- } catch {
2559
- return null;
2560
- }
2561
- }
2562
- function stripV(v) {
2563
- return v.startsWith("v") ? v.slice(1) : v;
2564
- }
2565
- function isNewer(local, remote) {
2566
- const a = stripV(local).split(".").map(Number);
2567
- const b = stripV(remote).split(".").map(Number);
2568
- for (let i = 0; i < Math.max(a.length, b.length); i++) {
2569
- const av = a[i] ?? 0;
2570
- const bv = b[i] ?? 0;
2571
- if (av < bv) return true;
2572
- if (av > bv) return false;
2573
- }
2574
- return false;
2575
- }
2576
- async function checkForUpdate() {
2577
- const localVersion = await readLocalVersion();
2578
- if (!localVersion) return { hasUpdate: false, localVersion: null, latestVersion: null };
2579
- const cached = await readCache();
2580
- if (cached) {
2581
- return { hasUpdate: cached.hasUpdate, localVersion, latestVersion: cached.latestVersion };
2582
- }
2583
- const latestVersion = await fetchLatestVersion();
2584
- if (!latestVersion) {
2585
- return { hasUpdate: false, localVersion, latestVersion: null };
2586
- }
2587
- const hasUpdate = isNewer(localVersion, latestVersion);
2588
- await writeCache({ checkedAt: Date.now(), latestVersion, hasUpdate });
2589
- return { hasUpdate, localVersion, latestVersion };
2590
- }
2591
- async function isGitRepo() {
2592
- let dir = dirname2(fileURLToPath(import.meta.url));
2593
- while (true) {
2594
- try {
2595
- await access(join3(dir, ".git"));
2596
- return true;
2597
- } catch {
2598
- }
2599
- const parent = dirname2(dir);
2600
- if (parent === dir) break;
2601
- dir = parent;
2602
- }
2603
- return false;
2604
- }
2605
- var CACHE_TTL_MS, NPM_REGISTRY;
2606
- var init_update_check = __esm({
2607
- "src/util/update-check.ts"() {
2608
- "use strict";
2609
- CACHE_TTL_MS = 60 * 60 * 1e3;
2610
- NPM_REGISTRY = "https://registry.npmjs.org/kimiflare/latest";
2611
- }
2612
- });
2613
-
2614
2752
  // src/ui/onboarding.tsx
2615
- import { useState as useState4 } from "react";
2616
- import { Box as Box9, Text as Text10 } from "ink";
2617
- import { Fragment, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
2753
+ import { useState as useState5 } from "react";
2754
+ import { Box as Box10, Text as Text11 } from "ink";
2755
+ import { Fragment, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
2618
2756
  function Onboarding({ onDone }) {
2619
- const [step, setStep] = useState4("accountId");
2620
- const [accountId, setAccountId] = useState4("");
2621
- const [apiToken, setApiToken] = useState4("");
2622
- const [model, setModel] = useState4(DEFAULT_MODEL);
2623
- const [savedPath, setSavedPath] = useState4(null);
2757
+ const [step, setStep] = useState5("accountId");
2758
+ const [accountId, setAccountId] = useState5("");
2759
+ const [apiToken, setApiToken] = useState5("");
2760
+ const [model, setModel] = useState5(DEFAULT_MODEL);
2761
+ const [savedPath, setSavedPath] = useState5(null);
2624
2762
  const stepIndex = STEPS.indexOf(step) + 1;
2625
2763
  const handleAccountIdSubmit = (value) => {
2626
2764
  const trimmed = value.trim();
@@ -2649,26 +2787,26 @@ function Onboarding({ onDone }) {
2649
2787
  setSavedPath(`error: ${e.message}`);
2650
2788
  }
2651
2789
  };
2652
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", paddingY: 1, children: [
2653
- /* @__PURE__ */ jsxs9(Box9, { marginBottom: 1, children: [
2654
- /* @__PURE__ */ jsx10(Text10, { bold: true, color: "cyan", children: "kimiflare" }),
2655
- /* @__PURE__ */ jsxs9(Text10, { color: "gray", dimColor: true, children: [
2790
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", paddingY: 1, children: [
2791
+ /* @__PURE__ */ jsxs10(Box10, { marginBottom: 1, children: [
2792
+ /* @__PURE__ */ jsx11(Text11, { bold: true, color: "cyan", children: "kimiflare" }),
2793
+ /* @__PURE__ */ jsxs10(Text11, { color: "gray", dimColor: true, children: [
2656
2794
  " ",
2657
2795
  "Terminal coding agent"
2658
2796
  ] })
2659
2797
  ] }),
2660
- /* @__PURE__ */ jsxs9(Text10, { color: "gray", dimColor: true, children: [
2798
+ /* @__PURE__ */ jsxs10(Text11, { color: "gray", dimColor: true, children: [
2661
2799
  "Step ",
2662
2800
  stepIndex,
2663
2801
  " of ",
2664
2802
  STEPS.length
2665
2803
  ] }),
2666
- /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexDirection: "column", children: [
2667
- step === "accountId" && /* @__PURE__ */ jsxs9(Fragment, { children: [
2668
- /* @__PURE__ */ jsx10(Text10, { children: "Enter your Cloudflare Account ID" }),
2669
- /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, children: [
2670
- /* @__PURE__ */ jsx10(Text10, { color: "cyan", children: "\u203A " }),
2671
- /* @__PURE__ */ jsx10(
2804
+ /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, flexDirection: "column", children: [
2805
+ step === "accountId" && /* @__PURE__ */ jsxs10(Fragment, { children: [
2806
+ /* @__PURE__ */ jsx11(Text11, { children: "Enter your Cloudflare Account ID" }),
2807
+ /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, children: [
2808
+ /* @__PURE__ */ jsx11(Text11, { color: "cyan", children: "\u203A " }),
2809
+ /* @__PURE__ */ jsx11(
2672
2810
  CustomTextInput,
2673
2811
  {
2674
2812
  value: accountId,
@@ -2678,12 +2816,12 @@ function Onboarding({ onDone }) {
2678
2816
  )
2679
2817
  ] })
2680
2818
  ] }),
2681
- step === "apiToken" && /* @__PURE__ */ jsxs9(Fragment, { children: [
2682
- /* @__PURE__ */ jsx10(Text10, { children: "Enter your Cloudflare API Token" }),
2683
- /* @__PURE__ */ jsx10(Text10, { color: "gray", dimColor: true, children: "Create one at https://dash.cloudflare.com/profile/api-tokens" }),
2684
- /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, children: [
2685
- /* @__PURE__ */ jsx10(Text10, { color: "cyan", children: "\u203A " }),
2686
- /* @__PURE__ */ jsx10(
2819
+ step === "apiToken" && /* @__PURE__ */ jsxs10(Fragment, { children: [
2820
+ /* @__PURE__ */ jsx11(Text11, { children: "Enter your Cloudflare API Token" }),
2821
+ /* @__PURE__ */ jsx11(Text11, { color: "gray", dimColor: true, children: "Create one at https://dash.cloudflare.com/profile/api-tokens" }),
2822
+ /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, children: [
2823
+ /* @__PURE__ */ jsx11(Text11, { color: "cyan", children: "\u203A " }),
2824
+ /* @__PURE__ */ jsx11(
2687
2825
  CustomTextInput,
2688
2826
  {
2689
2827
  value: apiToken,
@@ -2694,15 +2832,15 @@ function Onboarding({ onDone }) {
2694
2832
  )
2695
2833
  ] })
2696
2834
  ] }),
2697
- step === "model" && /* @__PURE__ */ jsxs9(Fragment, { children: [
2698
- /* @__PURE__ */ jsx10(Text10, { children: "Model ID (press Enter for default)" }),
2699
- /* @__PURE__ */ jsxs9(Text10, { color: "gray", dimColor: true, children: [
2835
+ step === "model" && /* @__PURE__ */ jsxs10(Fragment, { children: [
2836
+ /* @__PURE__ */ jsx11(Text11, { children: "Model ID (press Enter for default)" }),
2837
+ /* @__PURE__ */ jsxs10(Text11, { color: "gray", dimColor: true, children: [
2700
2838
  "default: ",
2701
2839
  DEFAULT_MODEL
2702
2840
  ] }),
2703
- /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, children: [
2704
- /* @__PURE__ */ jsx10(Text10, { color: "cyan", children: "\u203A " }),
2705
- /* @__PURE__ */ jsx10(
2841
+ /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, children: [
2842
+ /* @__PURE__ */ jsx11(Text11, { color: "cyan", children: "\u203A " }),
2843
+ /* @__PURE__ */ jsx11(
2706
2844
  CustomTextInput,
2707
2845
  {
2708
2846
  value: model,
@@ -2712,10 +2850,10 @@ function Onboarding({ onDone }) {
2712
2850
  )
2713
2851
  ] })
2714
2852
  ] }),
2715
- step === "confirm" && /* @__PURE__ */ jsxs9(Fragment, { children: [
2716
- /* @__PURE__ */ jsx10(Text10, { children: "Ready to save configuration" }),
2717
- /* @__PURE__ */ jsxs9(
2718
- Box9,
2853
+ step === "confirm" && /* @__PURE__ */ jsxs10(Fragment, { children: [
2854
+ /* @__PURE__ */ jsx11(Text11, { children: "Ready to save configuration" }),
2855
+ /* @__PURE__ */ jsxs10(
2856
+ Box10,
2719
2857
  {
2720
2858
  flexDirection: "column",
2721
2859
  marginTop: 1,
@@ -2724,25 +2862,25 @@ function Onboarding({ onDone }) {
2724
2862
  borderColor: "gray",
2725
2863
  paddingX: 1,
2726
2864
  children: [
2727
- /* @__PURE__ */ jsxs9(Text10, { color: "gray", children: [
2865
+ /* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
2728
2866
  "Account ID: ",
2729
2867
  accountId
2730
2868
  ] }),
2731
- /* @__PURE__ */ jsxs9(Text10, { color: "gray", children: [
2869
+ /* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
2732
2870
  "API Token: ",
2733
2871
  "\u2022".repeat(apiToken.length)
2734
2872
  ] }),
2735
- /* @__PURE__ */ jsxs9(Text10, { color: "gray", children: [
2873
+ /* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
2736
2874
  "Model: ",
2737
2875
  model
2738
2876
  ] })
2739
2877
  ]
2740
2878
  }
2741
2879
  ),
2742
- /* @__PURE__ */ jsx10(Text10, { children: "Press Enter to confirm, or Ctrl+C to cancel" }),
2743
- /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, children: [
2744
- /* @__PURE__ */ jsx10(Text10, { color: "cyan", children: "\u203A " }),
2745
- /* @__PURE__ */ jsx10(
2880
+ /* @__PURE__ */ jsx11(Text11, { children: "Press Enter to confirm, or Ctrl+C to cancel" }),
2881
+ /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, children: [
2882
+ /* @__PURE__ */ jsx11(Text11, { color: "cyan", children: "\u203A " }),
2883
+ /* @__PURE__ */ jsx11(
2746
2884
  CustomTextInput,
2747
2885
  {
2748
2886
  value: "",
@@ -2753,7 +2891,7 @@ function Onboarding({ onDone }) {
2753
2891
  )
2754
2892
  ] })
2755
2893
  ] }),
2756
- savedPath && /* @__PURE__ */ jsxs9(Text10, { color: "green", children: [
2894
+ savedPath && /* @__PURE__ */ jsxs10(Text11, { color: "green", children: [
2757
2895
  "Config saved to ",
2758
2896
  savedPath
2759
2897
  ] })
@@ -2771,26 +2909,26 @@ var init_onboarding = __esm({
2771
2909
  });
2772
2910
 
2773
2911
  // src/ui/welcome.tsx
2774
- import { Box as Box10, Text as Text11 } from "ink";
2775
- import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
2912
+ import { Box as Box11, Text as Text12 } from "ink";
2913
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
2776
2914
  function Welcome({ theme }) {
2777
- return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", marginBottom: 1, children: [
2778
- /* @__PURE__ */ jsxs10(Box10, { marginBottom: 1, children: [
2779
- /* @__PURE__ */ jsx11(Text11, { bold: true, color: theme.accent, children: "kimiflare" }),
2780
- /* @__PURE__ */ jsxs10(Text11, { color: theme.info.color, dimColor: theme.info.dim, children: [
2915
+ return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginBottom: 1, children: [
2916
+ /* @__PURE__ */ jsxs11(Box11, { marginBottom: 1, children: [
2917
+ /* @__PURE__ */ jsx12(Text12, { bold: true, color: theme.accent, children: "kimiflare" }),
2918
+ /* @__PURE__ */ jsxs11(Text12, { color: theme.info.color, dimColor: theme.info.dim, children: [
2781
2919
  " ",
2782
2920
  "Ready when you are."
2783
2921
  ] })
2784
2922
  ] }),
2785
- /* @__PURE__ */ jsx11(Box10, { flexDirection: "column", children: SUGGESTIONS.map((s, i) => /* @__PURE__ */ jsxs10(Box10, { children: [
2786
- /* @__PURE__ */ jsxs10(Text11, { color: theme.info.color, dimColor: theme.info.dim, children: [
2923
+ /* @__PURE__ */ jsx12(Box11, { flexDirection: "column", children: SUGGESTIONS.map((s, i) => /* @__PURE__ */ jsxs11(Box11, { children: [
2924
+ /* @__PURE__ */ jsxs11(Text12, { color: theme.info.color, dimColor: theme.info.dim, children: [
2787
2925
  " ",
2788
2926
  "\u203A",
2789
2927
  " "
2790
2928
  ] }),
2791
- /* @__PURE__ */ jsx11(Text11, { color: theme.user, children: s })
2929
+ /* @__PURE__ */ jsx12(Text12, { color: theme.user, children: s })
2792
2930
  ] }, i)) }),
2793
- /* @__PURE__ */ jsx11(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text11, { color: theme.info.color, dimColor: theme.info.dim, children: "Type a message or /help for commands \xB7 ctrl-c to exit \xB7 shift+tab to cycle modes" }) })
2931
+ /* @__PURE__ */ jsx12(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text12, { color: theme.info.color, dimColor: theme.info.dim, children: "Type a message or /help for commands \xB7 ctrl-c to exit \xB7 shift+tab to cycle modes" }) })
2794
2932
  ] });
2795
2933
  }
2796
2934
  var SUGGESTIONS;
@@ -2813,7 +2951,10 @@ function resolveTheme(name) {
2813
2951
  function themeNames() {
2814
2952
  return Object.keys(THEMES);
2815
2953
  }
2816
- var dark, light, highContrast, THEMES, DEFAULT_THEME_NAME;
2954
+ function themeList() {
2955
+ return Object.values(THEMES);
2956
+ }
2957
+ var dark, light, highContrast, dracula, nord, oneDark, monokai, solarizedDark, solarizedLight, tokyoNight, gruvboxDark, gruvboxLight, catppuccinMocha, rosePine, THEMES, DEFAULT_THEME_NAME;
2817
2958
  var init_theme = __esm({
2818
2959
  "src/ui/theme.ts"() {
2819
2960
  "use strict";
@@ -2865,10 +3006,197 @@ var init_theme = __esm({
2865
3006
  accent: "cyanBright",
2866
3007
  modeBadge: { plan: "blueBright", auto: "greenBright", edit: "cyanBright" }
2867
3008
  };
3009
+ dracula = {
3010
+ name: "dracula",
3011
+ label: "dracula (purple & cyan, popular dark)",
3012
+ user: "cyanBright",
3013
+ assistant: void 0,
3014
+ reasoning: { color: "magenta", dim: true },
3015
+ info: { color: "gray", dim: true },
3016
+ error: "redBright",
3017
+ warn: "yellowBright",
3018
+ tool: "magentaBright",
3019
+ spinner: "cyanBright",
3020
+ permission: "yellowBright",
3021
+ queue: { color: "gray", dim: true },
3022
+ accent: "magentaBright",
3023
+ modeBadge: { plan: "blueBright", auto: "greenBright", edit: "magentaBright" }
3024
+ };
3025
+ nord = {
3026
+ name: "nord",
3027
+ label: "nord (arctic blue & frost, calm dark)",
3028
+ user: "cyan",
3029
+ assistant: void 0,
3030
+ reasoning: { color: "blue", dim: true },
3031
+ info: { color: "blue", dim: true },
3032
+ error: "red",
3033
+ warn: "yellow",
3034
+ tool: "cyan",
3035
+ spinner: "cyan",
3036
+ permission: "yellow",
3037
+ queue: { color: "blue", dim: true },
3038
+ accent: "cyan",
3039
+ modeBadge: { plan: "blue", auto: "green", edit: "cyan" }
3040
+ };
3041
+ oneDark = {
3042
+ name: "one-dark",
3043
+ label: "one-dark (Atom's classic dark)",
3044
+ user: "cyan",
3045
+ assistant: void 0,
3046
+ reasoning: { color: "gray", dim: true },
3047
+ info: { color: "gray", dim: true },
3048
+ error: "red",
3049
+ warn: "yellow",
3050
+ tool: "blue",
3051
+ spinner: "cyan",
3052
+ permission: "yellow",
3053
+ queue: { color: "gray", dim: true },
3054
+ accent: "blue",
3055
+ modeBadge: { plan: "blue", auto: "green", edit: "cyan" }
3056
+ };
3057
+ monokai = {
3058
+ name: "monokai",
3059
+ label: "monokai (vibrant magenta & yellow)",
3060
+ user: "magentaBright",
3061
+ assistant: void 0,
3062
+ reasoning: { color: "gray", dim: true },
3063
+ info: { color: "gray", dim: true },
3064
+ error: "redBright",
3065
+ warn: "yellowBright",
3066
+ tool: "cyanBright",
3067
+ spinner: "yellowBright",
3068
+ permission: "yellowBright",
3069
+ queue: { color: "gray", dim: true },
3070
+ accent: "magentaBright",
3071
+ modeBadge: { plan: "blueBright", auto: "greenBright", edit: "magentaBright" }
3072
+ };
3073
+ solarizedDark = {
3074
+ name: "solarized-dark",
3075
+ label: "solarized-dark (muted blue & yellow)",
3076
+ user: "cyan",
3077
+ assistant: void 0,
3078
+ reasoning: { color: "blue", dim: true },
3079
+ info: { color: "blue", dim: true },
3080
+ error: "red",
3081
+ warn: "yellow",
3082
+ tool: "cyan",
3083
+ spinner: "yellow",
3084
+ permission: "yellow",
3085
+ queue: { color: "blue", dim: true },
3086
+ accent: "cyan",
3087
+ modeBadge: { plan: "blue", auto: "green", edit: "cyan" }
3088
+ };
3089
+ solarizedLight = {
3090
+ name: "solarized-light",
3091
+ label: "solarized-light (light beige & cyan)",
3092
+ user: "blue",
3093
+ assistant: void 0,
3094
+ reasoning: { color: "cyan", dim: false },
3095
+ info: { color: "cyan", dim: false },
3096
+ error: "red",
3097
+ warn: "yellow",
3098
+ tool: "blue",
3099
+ spinner: "blue",
3100
+ permission: "yellow",
3101
+ queue: { color: "cyan", dim: false },
3102
+ accent: "blue",
3103
+ modeBadge: { plan: "blue", auto: "green", edit: "blue" }
3104
+ };
3105
+ tokyoNight = {
3106
+ name: "tokyo-night",
3107
+ label: "tokyo-night (deep blue & purple)",
3108
+ user: "cyanBright",
3109
+ assistant: void 0,
3110
+ reasoning: { color: "blue", dim: true },
3111
+ info: { color: "blue", dim: true },
3112
+ error: "redBright",
3113
+ warn: "yellow",
3114
+ tool: "magentaBright",
3115
+ spinner: "cyanBright",
3116
+ permission: "yellow",
3117
+ queue: { color: "blue", dim: true },
3118
+ accent: "magentaBright",
3119
+ modeBadge: { plan: "blueBright", auto: "greenBright", edit: "magentaBright" }
3120
+ };
3121
+ gruvboxDark = {
3122
+ name: "gruvbox-dark",
3123
+ label: "gruvbox-dark (warm retro dark)",
3124
+ user: "yellow",
3125
+ assistant: void 0,
3126
+ reasoning: { color: "gray", dim: true },
3127
+ info: { color: "gray", dim: true },
3128
+ error: "red",
3129
+ warn: "yellowBright",
3130
+ tool: "cyan",
3131
+ spinner: "yellow",
3132
+ permission: "yellowBright",
3133
+ queue: { color: "gray", dim: true },
3134
+ accent: "yellow",
3135
+ modeBadge: { plan: "blue", auto: "green", edit: "yellow" }
3136
+ };
3137
+ gruvboxLight = {
3138
+ name: "gruvbox-light",
3139
+ label: "gruvbox-light (warm retro light)",
3140
+ user: "blue",
3141
+ assistant: void 0,
3142
+ reasoning: { color: "blackBright", dim: false },
3143
+ info: { color: "blackBright", dim: false },
3144
+ error: "red",
3145
+ warn: "yellow",
3146
+ tool: "cyan",
3147
+ spinner: "blue",
3148
+ permission: "yellow",
3149
+ queue: { color: "blackBright", dim: false },
3150
+ accent: "blue",
3151
+ modeBadge: { plan: "blue", auto: "green", edit: "blue" }
3152
+ };
3153
+ catppuccinMocha = {
3154
+ name: "catppuccin-mocha",
3155
+ label: "catppuccin-mocha (pastel pink & lavender)",
3156
+ user: "magentaBright",
3157
+ assistant: void 0,
3158
+ reasoning: { color: "gray", dim: true },
3159
+ info: { color: "gray", dim: true },
3160
+ error: "redBright",
3161
+ warn: "yellow",
3162
+ tool: "cyanBright",
3163
+ spinner: "cyanBright",
3164
+ permission: "yellow",
3165
+ queue: { color: "gray", dim: true },
3166
+ accent: "magentaBright",
3167
+ modeBadge: { plan: "blueBright", auto: "greenBright", edit: "magentaBright" }
3168
+ };
3169
+ rosePine = {
3170
+ name: "rose-pine",
3171
+ label: "rose-pine (soft rose & foam)",
3172
+ user: "magenta",
3173
+ assistant: void 0,
3174
+ reasoning: { color: "gray", dim: true },
3175
+ info: { color: "gray", dim: true },
3176
+ error: "red",
3177
+ warn: "yellow",
3178
+ tool: "cyan",
3179
+ spinner: "magenta",
3180
+ permission: "yellow",
3181
+ queue: { color: "gray", dim: true },
3182
+ accent: "magenta",
3183
+ modeBadge: { plan: "blue", auto: "green", edit: "magenta" }
3184
+ };
2868
3185
  THEMES = {
2869
3186
  dark,
2870
3187
  light,
2871
- "high-contrast": highContrast
3188
+ "high-contrast": highContrast,
3189
+ dracula,
3190
+ nord,
3191
+ "one-dark": oneDark,
3192
+ monokai,
3193
+ "solarized-dark": solarizedDark,
3194
+ "solarized-light": solarizedLight,
3195
+ "tokyo-night": tokyoNight,
3196
+ "gruvbox-dark": gruvboxDark,
3197
+ "gruvbox-light": gruvboxLight,
3198
+ "catppuccin-mocha": catppuccinMocha,
3199
+ "rose-pine": rosePine
2872
3200
  };
2873
3201
  DEFAULT_THEME_NAME = "dark";
2874
3202
  }
@@ -2943,36 +3271,40 @@ var app_exports = {};
2943
3271
  __export(app_exports, {
2944
3272
  renderApp: () => renderApp
2945
3273
  });
2946
- import { useState as useState5, useRef as useRef2, useEffect as useEffect4, useCallback } from "react";
2947
- import { Box as Box11, Text as Text12, useApp, useInput as useInput2, render } from "ink";
3274
+ import { useState as useState6, useRef as useRef2, useEffect as useEffect4, useCallback } from "react";
3275
+ import { Box as Box12, Text as Text13, useApp, useInput as useInput2, render } from "ink";
2948
3276
  import { existsSync } from "fs";
2949
3277
  import { join as join5 } from "path";
2950
3278
  import { unlink } from "fs/promises";
2951
- import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
2952
- function App({ initialCfg }) {
3279
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
3280
+ function App({ initialCfg, initialUpdateResult }) {
2953
3281
  const { exit } = useApp();
2954
- const [cfg, setCfg] = useState5(initialCfg);
2955
- const [events, setEvents] = useState5([]);
2956
- const [input, setInput] = useState5("");
2957
- const [busy, setBusy] = useState5(false);
2958
- const [usage, setUsage] = useState5(null);
2959
- const [showReasoning, setShowReasoning] = useState5(false);
2960
- const [perm, setPerm] = useState5(null);
2961
- const [queue, setQueue] = useState5([]);
2962
- const [history, setHistory] = useState5([]);
2963
- const [historyIndex, setHistoryIndex] = useState5(-1);
2964
- const [draftInput, setDraftInput] = useState5("");
2965
- const [mode, setMode] = useState5("edit");
2966
- const [effort, setEffort] = useState5(
3282
+ const [cfg, setCfg] = useState6(initialCfg);
3283
+ const [events, setEvents] = useState6([]);
3284
+ const [input, setInput] = useState6("");
3285
+ const [busy, setBusy] = useState6(false);
3286
+ const [usage, setUsage] = useState6(null);
3287
+ const [showReasoning, setShowReasoning] = useState6(false);
3288
+ const [perm, setPerm] = useState6(null);
3289
+ const [queue, setQueue] = useState6([]);
3290
+ const [history, setHistory] = useState6([]);
3291
+ const [historyIndex, setHistoryIndex] = useState6(-1);
3292
+ const [draftInput, setDraftInput] = useState6("");
3293
+ const [mode, setMode] = useState6("edit");
3294
+ const [effort, setEffort] = useState6(
2967
3295
  initialCfg?.reasoningEffort ?? DEFAULT_REASONING_EFFORT
2968
3296
  );
2969
- const [theme, setTheme] = useState5(resolveTheme(initialCfg?.theme));
2970
- const [resumeSessions, setResumeSessions] = useState5(null);
2971
- const [tasks, setTasks] = useState5([]);
2972
- const [tasksStartedAt, setTasksStartedAt] = useState5(null);
2973
- const [tasksStartTokens, setTasksStartTokens] = useState5(0);
2974
- const [turnStartedAt, setTurnStartedAt] = useState5(null);
2975
- const [verbose, setVerbose] = useState5(false);
3297
+ const [theme, setTheme] = useState6(resolveTheme(initialCfg?.theme));
3298
+ const [resumeSessions, setResumeSessions] = useState6(null);
3299
+ const [showThemePicker, setShowThemePicker] = useState6(false);
3300
+ const [originalTheme, setOriginalTheme] = useState6(null);
3301
+ const [tasks, setTasks] = useState6([]);
3302
+ const [tasksStartedAt, setTasksStartedAt] = useState6(null);
3303
+ const [tasksStartTokens, setTasksStartTokens] = useState6(0);
3304
+ const [turnStartedAt, setTurnStartedAt] = useState6(null);
3305
+ const [verbose, setVerbose] = useState6(false);
3306
+ const [hasUpdate, setHasUpdate] = useState6(initialUpdateResult?.hasUpdate ?? false);
3307
+ const [latestVersion, setLatestVersion] = useState6(initialUpdateResult?.latestVersion ?? null);
2976
3308
  const messagesRef = useRef2([
2977
3309
  {
2978
3310
  role: "system",
@@ -2993,12 +3325,42 @@ function App({ initialCfg }) {
2993
3325
  const tasksRef = useRef2([]);
2994
3326
  const usageRef = useRef2(null);
2995
3327
  const updateCheckedRef = useRef2(false);
3328
+ const updateNudgedRef = useRef2(false);
2996
3329
  const compactSuggestedRef = useRef2(false);
2997
3330
  useEffect4(() => {
2998
3331
  if (!cfg || updateCheckedRef.current) return;
2999
3332
  updateCheckedRef.current = true;
3333
+ if (initialUpdateResult) {
3334
+ if (initialUpdateResult.hasUpdate && !updateNudgedRef.current) {
3335
+ updateNudgedRef.current = true;
3336
+ setHasUpdate(true);
3337
+ setLatestVersion(initialUpdateResult.latestVersion);
3338
+ setEvents((e) => [
3339
+ ...e,
3340
+ {
3341
+ kind: "info",
3342
+ key: mkKey(),
3343
+ text: `update available: ${initialUpdateResult.localVersion} \u2192 ${initialUpdateResult.latestVersion}`
3344
+ }
3345
+ ]);
3346
+ void isGitRepo().then((git) => {
3347
+ setEvents((e) => [
3348
+ ...e,
3349
+ {
3350
+ kind: "info",
3351
+ key: mkKey(),
3352
+ text: git ? "run: git pull && npm install && npm run build then restart kimiflare" : "run: npm update -g kimiflare then restart"
3353
+ }
3354
+ ]);
3355
+ });
3356
+ }
3357
+ return;
3358
+ }
3000
3359
  void checkForUpdate().then((result) => {
3001
- if (result.hasUpdate) {
3360
+ if (result.hasUpdate && !updateNudgedRef.current) {
3361
+ updateNudgedRef.current = true;
3362
+ setHasUpdate(true);
3363
+ setLatestVersion(result.latestVersion);
3002
3364
  setEvents((e) => [
3003
3365
  ...e,
3004
3366
  {
@@ -3019,7 +3381,7 @@ function App({ initialCfg }) {
3019
3381
  });
3020
3382
  }
3021
3383
  });
3022
- }, [cfg]);
3384
+ }, [cfg, initialUpdateResult]);
3023
3385
  useEffect4(() => {
3024
3386
  modeRef.current = mode;
3025
3387
  messagesRef.current[0] = {
@@ -3038,6 +3400,39 @@ function App({ initialCfg }) {
3038
3400
  useEffect4(() => {
3039
3401
  effortRef.current = effort;
3040
3402
  }, [effort]);
3403
+ useEffect4(() => {
3404
+ if (!cfg) return;
3405
+ const id = setInterval(() => {
3406
+ void checkForUpdate().then((result) => {
3407
+ if (result.hasUpdate) {
3408
+ setHasUpdate(true);
3409
+ setLatestVersion(result.latestVersion);
3410
+ if (!updateNudgedRef.current) {
3411
+ updateNudgedRef.current = true;
3412
+ setEvents((e) => [
3413
+ ...e,
3414
+ {
3415
+ kind: "info",
3416
+ key: mkKey(),
3417
+ text: `update available: ${result.localVersion} \u2192 ${result.latestVersion}`
3418
+ }
3419
+ ]);
3420
+ void isGitRepo().then((git) => {
3421
+ setEvents((e) => [
3422
+ ...e,
3423
+ {
3424
+ kind: "info",
3425
+ key: mkKey(),
3426
+ text: git ? "run: git pull && npm install && npm run build then restart kimiflare" : "run: npm update -g kimiflare then restart"
3427
+ }
3428
+ ]);
3429
+ });
3430
+ }
3431
+ }
3432
+ });
3433
+ }, 30 * 60 * 1e3);
3434
+ return () => clearInterval(id);
3435
+ }, [cfg]);
3041
3436
  const saveSessionSafe = useCallback(async () => {
3042
3437
  if (!cfg) return;
3043
3438
  if (!sessionIdRef.current) {
@@ -3080,6 +3475,11 @@ function App({ initialCfg }) {
3080
3475
  setVerbose((v) => !v);
3081
3476
  return;
3082
3477
  }
3478
+ if (key.ctrl && inputChar === "t") {
3479
+ setOriginalTheme(theme);
3480
+ setShowThemePicker(true);
3481
+ return;
3482
+ }
3083
3483
  });
3084
3484
  const updateAssistant = useCallback(
3085
3485
  (id, patch) => {
@@ -3150,7 +3550,7 @@ function App({ initialCfg }) {
3150
3550
  }
3151
3551
  }, [cfg, busy, saveSessionSafe]);
3152
3552
  const openResumePicker = useCallback(async () => {
3153
- const sessions = await listSessions(30);
3553
+ const sessions = await listSessions(200);
3154
3554
  setResumeSessions(sessions);
3155
3555
  }, []);
3156
3556
  const runInit = useCallback(async () => {
@@ -3189,7 +3589,7 @@ function App({ initialCfg }) {
3189
3589
  "Do not call `tasks_set` for this. Just read what you need, then write the file."
3190
3590
  ].join("\n");
3191
3591
  setEvents((e) => [...e, { kind: "user", key: mkKey(), text: "/init" }]);
3192
- messagesRef.current.push({ role: "user", content: prompt });
3592
+ messagesRef.current.push({ role: "user", content: sanitizeString(prompt) });
3193
3593
  setBusy(true);
3194
3594
  setTurnStartedAt(Date.now());
3195
3595
  const controller = new AbortController();
@@ -3318,6 +3718,27 @@ function App({ initialCfg }) {
3318
3718
  },
3319
3719
  []
3320
3720
  );
3721
+ const handleThemePick = useCallback(
3722
+ (picked) => {
3723
+ if (!picked) {
3724
+ if (originalTheme) setTheme(originalTheme);
3725
+ setShowThemePicker(false);
3726
+ setOriginalTheme(null);
3727
+ return;
3728
+ }
3729
+ setTheme(picked);
3730
+ setCfg((c) => c ? { ...c, theme: picked.name } : c);
3731
+ if (cfg) void saveConfig({ ...cfg, theme: picked.name }).catch(() => {
3732
+ });
3733
+ setShowThemePicker(false);
3734
+ setOriginalTheme(null);
3735
+ setEvents((e) => [
3736
+ ...e,
3737
+ { kind: "info", key: mkKey(), text: `theme: ${picked.label}` }
3738
+ ]);
3739
+ },
3740
+ [cfg, originalTheme]
3741
+ );
3321
3742
  const handleSlash = useCallback(
3322
3743
  (cmd) => {
3323
3744
  const raw = cmd.trim();
@@ -3337,6 +3758,7 @@ function App({ initialCfg }) {
3337
3758
  setTasksStartedAt(null);
3338
3759
  setTasksStartTokens(0);
3339
3760
  compactSuggestedRef.current = false;
3761
+ updateNudgedRef.current = false;
3340
3762
  return true;
3341
3763
  }
3342
3764
  if (c === "/reasoning") {
@@ -3403,14 +3825,8 @@ use: /thinking low | medium | high`
3403
3825
  }
3404
3826
  if (c === "/theme") {
3405
3827
  if (!arg) {
3406
- setEvents((e) => [
3407
- ...e,
3408
- {
3409
- kind: "info",
3410
- key: mkKey(),
3411
- text: `current: ${theme.name} \xB7 available: ${themeNames().join(", ")}`
3412
- }
3413
- ]);
3828
+ setOriginalTheme(theme);
3829
+ setShowThemePicker(true);
3414
3830
  return true;
3415
3831
  }
3416
3832
  const next = resolveTheme(arg);
@@ -3481,8 +3897,10 @@ use: /thinking low | medium | high`
3481
3897
  return true;
3482
3898
  }
3483
3899
  if (c === "/update") {
3484
- void checkForUpdate().then((result) => {
3900
+ void checkForUpdate(true).then((result) => {
3485
3901
  if (result.hasUpdate) {
3902
+ setHasUpdate(true);
3903
+ setLatestVersion(result.latestVersion);
3486
3904
  setEvents((e) => [
3487
3905
  ...e,
3488
3906
  {
@@ -3502,6 +3920,8 @@ use: /thinking low | medium | high`
3502
3920
  ]);
3503
3921
  });
3504
3922
  } else {
3923
+ setHasUpdate(false);
3924
+ setLatestVersion(null);
3505
3925
  setEvents((e) => [
3506
3926
  ...e,
3507
3927
  { kind: "info", key: mkKey(), text: "no update available" }
@@ -3526,7 +3946,7 @@ use: /thinking low | medium | high`
3526
3946
  {
3527
3947
  kind: "info",
3528
3948
  key: mkKey(),
3529
- text: "commands:\n /mode edit|plan|auto switch mode (or shift+tab to cycle)\n /plan /auto /edit shortcuts for /mode\n /thinking low|med|high set reasoning effort (quality vs speed)\n /theme NAME dark, light, high-contrast\n /resume pick a past conversation\n /compact summarize old turns to free context\n /init scan this repo and write a KIMI.md for future agents\n /reasoning toggle show/hide model reasoning\n /clear clear current conversation\n /cost /model /update /logout /help /exit\nkeys: ctrl-c interrupt/exit \xB7 ctrl-r toggle reasoning \xB7 ctrl-o toggle verbose output \xB7 shift+tab cycle mode \xB7 \u2191/\u2193 history"
3949
+ text: "commands:\n /mode edit|plan|auto switch mode (or shift+tab to cycle)\n /plan /auto /edit shortcuts for /mode\n /thinking low|med|high set reasoning effort (quality vs speed)\n /theme interactive theme picker (or ctrl+t)\n /theme NAME set theme by name\n /resume pick a past conversation\n /compact summarize old turns to free context\n /init scan this repo and write a KIMI.md for future agents\n /reasoning toggle show/hide model reasoning\n /clear clear current conversation\n /cost /model /update /logout /help /exit\nkeys: ctrl-c interrupt/exit \xB7 ctrl-r toggle reasoning \xB7 ctrl-o verbose \xB7 ctrl+t theme \xB7 shift+tab cycle mode \xB7 \u2191/\u2193 history"
3530
3950
  }
3531
3951
  ]);
3532
3952
  return true;
@@ -3543,7 +3963,7 @@ use: /thinking low | medium | high`
3543
3963
  if (trimmed.startsWith("/") && handleSlash(trimmed)) return;
3544
3964
  const display = displayText?.trim() || trimmed;
3545
3965
  setEvents((e) => [...e, { kind: "user", key: mkKey(), text: display }]);
3546
- messagesRef.current.push({ role: "user", content: trimmed });
3966
+ messagesRef.current.push({ role: "user", content: sanitizeString(trimmed) });
3547
3967
  setBusy(true);
3548
3968
  setTurnStartedAt(Date.now());
3549
3969
  const controller = new AbortController();
@@ -3708,7 +4128,7 @@ use: /thinking low | medium | high`
3708
4128
  }
3709
4129
  }, [usage]);
3710
4130
  if (!cfg) {
3711
- return /* @__PURE__ */ jsx12(
4131
+ return /* @__PURE__ */ jsx13(
3712
4132
  Onboarding,
3713
4133
  {
3714
4134
  onDone: (newCfg) => {
@@ -3722,12 +4142,15 @@ use: /thinking low | medium | high`
3722
4142
  );
3723
4143
  }
3724
4144
  if (resumeSessions !== null) {
3725
- return /* @__PURE__ */ jsx12(Box11, { flexDirection: "column", children: /* @__PURE__ */ jsx12(ResumePicker, { sessions: resumeSessions, onPick: handleResumePick, theme }) });
4145
+ return /* @__PURE__ */ jsx13(Box12, { flexDirection: "column", children: /* @__PURE__ */ jsx13(ResumePicker, { sessions: resumeSessions, onPick: handleResumePick, theme }) });
4146
+ }
4147
+ if (showThemePicker) {
4148
+ return /* @__PURE__ */ jsx13(Box12, { flexDirection: "column", children: /* @__PURE__ */ jsx13(ThemePicker, { themes: themeList(), current: theme, onPick: handleThemePick }) });
3726
4149
  }
3727
4150
  const hasConversation = events.some((e) => e.kind === "user" || e.kind === "assistant");
3728
- return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
3729
- !hasConversation && events.length === 0 ? /* @__PURE__ */ jsx12(Welcome, { theme }) : /* @__PURE__ */ jsx12(ChatView, { events, showReasoning, theme, verbose }),
3730
- perm ? /* @__PURE__ */ jsx12(
4151
+ return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
4152
+ !hasConversation && events.length === 0 ? /* @__PURE__ */ jsx13(Welcome, { theme }) : /* @__PURE__ */ jsx13(ChatView, { events, showReasoning, theme, verbose }),
4153
+ perm ? /* @__PURE__ */ jsx13(
3731
4154
  PermissionModal,
3732
4155
  {
3733
4156
  tool: perm.tool,
@@ -3738,8 +4161,8 @@ use: /thinking low | medium | high`
3738
4161
  setPerm(null);
3739
4162
  }
3740
4163
  }
3741
- ) : /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginTop: 1, children: [
3742
- tasks.length > 0 && /* @__PURE__ */ jsx12(
4164
+ ) : /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginTop: 1, children: [
4165
+ tasks.length > 0 && /* @__PURE__ */ jsx13(
3743
4166
  TaskList,
3744
4167
  {
3745
4168
  tasks,
@@ -3748,11 +4171,11 @@ use: /thinking low | medium | high`
3748
4171
  tokensDelta: Math.max(0, (usage?.prompt_tokens ?? 0) - tasksStartTokens)
3749
4172
  }
3750
4173
  ),
3751
- queue.length > 0 && /* @__PURE__ */ jsx12(Box11, { flexDirection: "column", marginBottom: 1, children: queue.map((q, i) => /* @__PURE__ */ jsxs11(Text12, { color: theme.queue.color, dimColor: theme.queue.dim, children: [
4174
+ queue.length > 0 && /* @__PURE__ */ jsx13(Box12, { flexDirection: "column", marginBottom: 1, children: queue.map((q, i) => /* @__PURE__ */ jsxs12(Text13, { color: theme.queue.color, dimColor: theme.queue.dim, children: [
3752
4175
  "\u23F3 ",
3753
4176
  q.display
3754
4177
  ] }, `queue_${i}`)) }),
3755
- /* @__PURE__ */ jsx12(
4178
+ /* @__PURE__ */ jsx13(
3756
4179
  StatusBar,
3757
4180
  {
3758
4181
  model: cfg.model,
@@ -3762,12 +4185,14 @@ use: /thinking low | medium | high`
3762
4185
  theme,
3763
4186
  mode,
3764
4187
  effort,
3765
- contextLimit: CONTEXT_LIMIT
4188
+ contextLimit: CONTEXT_LIMIT,
4189
+ hasUpdate,
4190
+ latestVersion
3766
4191
  }
3767
4192
  ),
3768
- /* @__PURE__ */ jsxs11(Box11, { marginTop: 1, children: [
3769
- /* @__PURE__ */ jsx12(Text12, { color: theme.accent, children: "\u203A " }),
3770
- /* @__PURE__ */ jsx12(
4193
+ /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, children: [
4194
+ /* @__PURE__ */ jsx13(Text13, { color: theme.accent, children: "\u203A " }),
4195
+ /* @__PURE__ */ jsx13(
3771
4196
  CustomTextInput,
3772
4197
  {
3773
4198
  value: input,
@@ -3815,8 +4240,8 @@ use: /thinking low | medium | high`
3815
4240
  ] })
3816
4241
  ] });
3817
4242
  }
3818
- async function renderApp(cfg) {
3819
- const instance = render(/* @__PURE__ */ jsx12(App, { initialCfg: cfg }));
4243
+ async function renderApp(cfg, updateResult) {
4244
+ const instance = render(/* @__PURE__ */ jsx13(App, { initialCfg: cfg, initialUpdateResult: updateResult }));
3820
4245
  await instance.waitUntilExit();
3821
4246
  }
3822
4247
  var CONTEXT_LIMIT, AUTO_COMPACT_SUGGEST_PCT, nextAssistantId, nextKey, mkKey, EFFORT_DESCRIPTIONS;
@@ -3827,10 +4252,12 @@ var init_app = __esm({
3827
4252
  init_system_prompt();
3828
4253
  init_compact();
3829
4254
  init_executor();
4255
+ init_messages();
3830
4256
  init_chat();
3831
4257
  init_status();
3832
4258
  init_permission();
3833
4259
  init_resume_picker();
4260
+ init_theme_picker();
3834
4261
  init_task_list();
3835
4262
  init_text_input();
3836
4263
  init_update_check();
@@ -3858,6 +4285,7 @@ init_config();
3858
4285
  init_loop();
3859
4286
  init_system_prompt();
3860
4287
  init_executor();
4288
+ init_update_check();
3861
4289
  import { Command } from "commander";
3862
4290
  import { readFileSync as readFileSync2 } from "fs";
3863
4291
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -3876,6 +4304,7 @@ program.name("kimiflare").description("Terminal coding agent powered by Kimi-K2.
3876
4304
  var opts = program.opts();
3877
4305
  async function main() {
3878
4306
  const cfg = await loadConfig();
4307
+ const updateResult = await checkForUpdate();
3879
4308
  if (opts.print !== void 0) {
3880
4309
  if (!cfg) {
3881
4310
  console.error(
@@ -3889,7 +4318,8 @@ async function main() {
3889
4318
  model,
3890
4319
  prompt: opts.print,
3891
4320
  allowAll: !!opts.dangerouslyAllowAll,
3892
- showReasoning: !!opts.reasoning
4321
+ showReasoning: !!opts.reasoning,
4322
+ updateResult
3893
4323
  });
3894
4324
  return;
3895
4325
  }
@@ -3902,12 +4332,21 @@ async function main() {
3902
4332
  const { renderApp: renderApp2 } = await Promise.resolve().then(() => (init_app(), app_exports));
3903
4333
  if (cfg) {
3904
4334
  const model = opts.model ?? cfg.model ?? DEFAULT_MODEL;
3905
- await renderApp2({ ...cfg, model });
4335
+ await renderApp2({ ...cfg, model }, updateResult);
3906
4336
  } else {
3907
- await renderApp2(null);
4337
+ await renderApp2(null, updateResult);
3908
4338
  }
3909
4339
  }
3910
4340
  async function runPrintMode(opts2) {
4341
+ if (opts2.updateResult.hasUpdate) {
4342
+ const git = await isGitRepo();
4343
+ process.stderr.write(
4344
+ `\x1B[33mkimiflare update available: ${opts2.updateResult.localVersion} \u2192 ${opts2.updateResult.latestVersion}\x1B[0m
4345
+ \x1B[33m ${git ? "git pull && npm install && npm run build" : "npm update -g kimiflare"} then restart\x1B[0m
4346
+
4347
+ `
4348
+ );
4349
+ }
3911
4350
  const cwd = process.cwd();
3912
4351
  const executor = new ToolExecutor(ALL_TOOLS);
3913
4352
  const messages = [