kimiflare 0.7.1 → 0.8.1

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
@@ -1172,6 +1172,121 @@ var init_executor = __esm({
1172
1172
  }
1173
1173
  });
1174
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
+ const hasUpdate2 = isNewer(localVersion, cached.latestVersion);
1256
+ return { hasUpdate: hasUpdate2, localVersion, latestVersion: cached.latestVersion };
1257
+ }
1258
+ }
1259
+ const latestVersion = await fetchLatestVersion();
1260
+ if (!latestVersion) {
1261
+ return { hasUpdate: false, localVersion, latestVersion: null };
1262
+ }
1263
+ const hasUpdate = isNewer(localVersion, latestVersion);
1264
+ await writeCache({ checkedAt: Date.now(), latestVersion });
1265
+ return { hasUpdate, localVersion, latestVersion };
1266
+ }
1267
+ async function isGitRepo() {
1268
+ let dir = dirname2(fileURLToPath(import.meta.url));
1269
+ while (true) {
1270
+ try {
1271
+ await access(join3(dir, ".git"));
1272
+ return true;
1273
+ } catch {
1274
+ }
1275
+ const parent = dirname2(dir);
1276
+ if (parent === dir) break;
1277
+ dir = parent;
1278
+ }
1279
+ return false;
1280
+ }
1281
+ var CACHE_TTL_MS, NPM_REGISTRY;
1282
+ var init_update_check = __esm({
1283
+ "src/util/update-check.ts"() {
1284
+ "use strict";
1285
+ CACHE_TTL_MS = 60 * 60 * 1e3;
1286
+ NPM_REGISTRY = "https://registry.npmjs.org/kimiflare/latest";
1287
+ }
1288
+ });
1289
+
1175
1290
  // src/agent/compact.ts
1176
1291
  function indexOfNthUserFromEnd(messages, n) {
1177
1292
  let seen = 0;
@@ -1584,7 +1699,7 @@ import { useEffect, useState } from "react";
1584
1699
  import { Box as Box5, Text as Text5 } from "ink";
1585
1700
  import Spinner3 from "ink-spinner";
1586
1701
  import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1587
- function StatusBar({ model, usage, thinking, turnStartedAt, theme, mode, effort, contextLimit }) {
1702
+ function StatusBar({ model, usage, thinking, turnStartedAt, theme, mode, effort, contextLimit, hasUpdate, latestVersion }) {
1588
1703
  const [now, setNow] = useState(Date.now());
1589
1704
  const modeColor = mode === "plan" ? theme.modeBadge.plan : mode === "auto" ? theme.modeBadge.auto : theme.modeBadge.edit;
1590
1705
  const warn = usage && usage.prompt_tokens / contextLimit >= 0.8;
@@ -1618,6 +1733,12 @@ function StatusBar({ model, usage, thinking, turnStartedAt, theme, mode, effort,
1618
1733
  warn ? /* @__PURE__ */ jsxs5(Text5, { color: theme.warn, bold: true, children: [
1619
1734
  " \xB7 ",
1620
1735
  "/compact recommended"
1736
+ ] }) : null,
1737
+ hasUpdate ? /* @__PURE__ */ jsxs5(Text5, { color: theme.warn, bold: true, children: [
1738
+ " \xB7 ",
1739
+ "update available",
1740
+ latestVersion ? ` \u2192 ${latestVersion}` : "",
1741
+ " \xB7 run /update"
1621
1742
  ] }) : null
1622
1743
  ] })
1623
1744
  ] });
@@ -1691,10 +1812,16 @@ var init_permission = __esm({
1691
1812
  });
1692
1813
 
1693
1814
  // src/ui/resume-picker.tsx
1694
- import { Box as Box7, Text as Text7 } from "ink";
1815
+ import { useState as useState2 } from "react";
1816
+ import { Box as Box7, Text as Text7, useWindowSize } from "ink";
1695
1817
  import SelectInput2 from "ink-select-input";
1696
1818
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1697
1819
  function ResumePicker({ sessions, onPick, theme }) {
1820
+ const { rows } = useWindowSize();
1821
+ const [page, setPage] = useState2(0);
1822
+ const pageSize = Math.max(MIN_PAGE_SIZE, rows - HEADER_ROWS - FOOTER_ROWS);
1823
+ const totalPages = Math.max(1, Math.ceil(sessions.length / pageSize));
1824
+ const safePage = Math.min(page, totalPages - 1);
1698
1825
  if (sessions.length === 0) {
1699
1826
  return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
1700
1827
  /* @__PURE__ */ jsx7(Text7, { color: theme.accent, bold: true, children: "Resume a session" }),
@@ -1708,20 +1835,39 @@ function ResumePicker({ sessions, onPick, theme }) {
1708
1835
  ) })
1709
1836
  ] });
1710
1837
  }
1711
- const items = sessions.map((s) => ({
1838
+ const start = safePage * pageSize;
1839
+ const end = Math.min(start + pageSize, sessions.length);
1840
+ const pageSessions = sessions.slice(start, end);
1841
+ const items = pageSessions.map((s) => ({
1712
1842
  label: `${formatDate(s.updatedAt)} \xB7 ${s.messageCount} msgs \xB7 ${s.firstPrompt}`,
1713
1843
  value: s.id
1714
1844
  }));
1845
+ if (safePage > 0) {
1846
+ items.push({ label: "\u2190 previous page", value: "__prev__" });
1847
+ }
1848
+ if (safePage < totalPages - 1) {
1849
+ items.push({ label: "\u2192 next page", value: "__next__" });
1850
+ }
1715
1851
  items.push({ label: "(cancel)", value: "__cancel__" });
1716
1852
  return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
1717
1853
  /* @__PURE__ */ jsx7(Text7, { color: theme.accent, bold: true, children: "Resume a session" }),
1718
- /* @__PURE__ */ jsx7(Text7, { color: theme.info.color, dimColor: theme.info.dim, children: "Arrow keys to select, Enter to confirm." }),
1854
+ /* @__PURE__ */ jsxs7(Text7, { color: theme.info.color, dimColor: theme.info.dim, children: [
1855
+ "Arrow keys to select, Enter to confirm. Page ",
1856
+ safePage + 1,
1857
+ " of ",
1858
+ totalPages,
1859
+ " (",
1860
+ sessions.length,
1861
+ " total)"
1862
+ ] }),
1719
1863
  /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(
1720
1864
  SelectInput2,
1721
1865
  {
1722
1866
  items,
1723
1867
  onSelect: (item) => {
1724
1868
  if (item.value === "__cancel__") return onPick(null);
1869
+ if (item.value === "__prev__") return setPage((p) => Math.max(0, p - 1));
1870
+ if (item.value === "__next__") return setPage((p) => Math.min(totalPages - 1, p + 1));
1725
1871
  const picked = sessions.find((s) => s.id === item.value) ?? null;
1726
1872
  onPick(picked);
1727
1873
  }
@@ -1742,19 +1888,67 @@ function formatDate(iso) {
1742
1888
  return iso;
1743
1889
  }
1744
1890
  }
1891
+ var HEADER_ROWS, FOOTER_ROWS, MIN_PAGE_SIZE;
1745
1892
  var init_resume_picker = __esm({
1746
1893
  "src/ui/resume-picker.tsx"() {
1747
1894
  "use strict";
1895
+ HEADER_ROWS = 5;
1896
+ FOOTER_ROWS = 2;
1897
+ MIN_PAGE_SIZE = 5;
1748
1898
  }
1749
1899
  });
1750
1900
 
1751
- // src/ui/task-list.tsx
1752
- import { useEffect as useEffect2, useState as useState2 } from "react";
1901
+ // src/ui/theme-picker.tsx
1753
1902
  import { Box as Box8, Text as Text8 } from "ink";
1754
- import Spinner4 from "ink-spinner";
1903
+ import SelectInput3 from "ink-select-input";
1755
1904
  import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1905
+ function ThemePicker({ themes, current, onPick }) {
1906
+ const items = themes.map((t) => ({
1907
+ label: t.label,
1908
+ value: t.name,
1909
+ key: t.name
1910
+ }));
1911
+ items.push({ label: "(cancel)", value: "__cancel__", key: "__cancel__" });
1912
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", borderStyle: "round", borderColor: current.accent, paddingX: 1, children: [
1913
+ /* @__PURE__ */ jsx8(Text8, { color: current.accent, bold: true, children: "Pick a theme" }),
1914
+ /* @__PURE__ */ jsx8(Text8, { color: current.info.color, dimColor: current.info.dim, children: "Arrow keys to preview, Enter to confirm." }),
1915
+ /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(
1916
+ SelectInput3,
1917
+ {
1918
+ items,
1919
+ onHighlight: (item) => {
1920
+ if (item.value !== "__cancel__") {
1921
+ const highlighted = themes.find((t) => t.name === item.value);
1922
+ if (highlighted) onPick(highlighted);
1923
+ }
1924
+ },
1925
+ onSelect: (item) => {
1926
+ if (item.value === "__cancel__") return onPick(null);
1927
+ const picked = themes.find((t) => t.name === item.value) ?? null;
1928
+ onPick(picked);
1929
+ },
1930
+ itemComponent: ({ label, isSelected }) => {
1931
+ const theme = themes.find((t) => t.label === label);
1932
+ const color = theme?.accent ?? current.accent;
1933
+ return /* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsx8(Text8, { color, bold: isSelected, dimColor: !isSelected, children: label }) });
1934
+ }
1935
+ }
1936
+ ) })
1937
+ ] });
1938
+ }
1939
+ var init_theme_picker = __esm({
1940
+ "src/ui/theme-picker.tsx"() {
1941
+ "use strict";
1942
+ }
1943
+ });
1944
+
1945
+ // src/ui/task-list.tsx
1946
+ import { useEffect as useEffect2, useState as useState3 } from "react";
1947
+ import { Box as Box9, Text as Text9 } from "ink";
1948
+ import Spinner4 from "ink-spinner";
1949
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1756
1950
  function TaskList({ tasks, theme, startedAt, tokensDelta }) {
1757
- const [now, setNow] = useState2(Date.now());
1951
+ const [now, setNow] = useState3(Date.now());
1758
1952
  useEffect2(() => {
1759
1953
  if (startedAt === null) return;
1760
1954
  const allDone2 = tasks.length > 0 && tasks.every((t) => t.status === "completed");
@@ -1772,18 +1966,18 @@ function TaskList({ tasks, theme, startedAt, tokensDelta }) {
1772
1966
  const headerStats = [elapsed, tokensDelta > 0 ? `\u2191 ${formatTokens(tokensDelta)} tokens` : null].filter(Boolean).join(" \xB7 ");
1773
1967
  const visibleTasks = tasks.slice(0, MAX_VISIBLE);
1774
1968
  const hiddenPending = Math.max(0, tasks.length - visibleTasks.length);
1775
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginBottom: 1, children: [
1776
- /* @__PURE__ */ jsxs8(Box8, { children: [
1777
- /* @__PURE__ */ jsx8(Text8, { color: allDone ? "green" : theme.accent, bold: true, children: header }),
1778
- headerStats && /* @__PURE__ */ jsxs8(Text8, { color: theme.info.color, dimColor: theme.info.dim, children: [
1969
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginBottom: 1, children: [
1970
+ /* @__PURE__ */ jsxs9(Box9, { children: [
1971
+ /* @__PURE__ */ jsx9(Text9, { color: allDone ? "green" : theme.accent, bold: true, children: header }),
1972
+ headerStats && /* @__PURE__ */ jsxs9(Text9, { color: theme.info.color, dimColor: theme.info.dim, children: [
1779
1973
  " ",
1780
1974
  "(",
1781
1975
  headerStats,
1782
1976
  ")"
1783
1977
  ] })
1784
1978
  ] }),
1785
- visibleTasks.map((t) => /* @__PURE__ */ jsx8(TaskRow, { task: t, theme }, t.id)),
1786
- hiddenPending > 0 && /* @__PURE__ */ jsxs8(Text8, { color: theme.info.color, dimColor: theme.info.dim, children: [
1979
+ visibleTasks.map((t) => /* @__PURE__ */ jsx9(TaskRow, { task: t, theme }, t.id)),
1980
+ hiddenPending > 0 && /* @__PURE__ */ jsxs9(Text9, { color: theme.info.color, dimColor: theme.info.dim, children: [
1787
1981
  " ",
1788
1982
  "\u2026 +",
1789
1983
  hiddenPending,
@@ -1793,21 +1987,21 @@ function TaskList({ tasks, theme, startedAt, tokensDelta }) {
1793
1987
  }
1794
1988
  function TaskRow({ task, theme }) {
1795
1989
  if (task.status === "completed") {
1796
- return /* @__PURE__ */ jsxs8(Text8, { color: theme.info.color, dimColor: theme.info.dim, children: [
1990
+ return /* @__PURE__ */ jsxs9(Text9, { color: theme.info.color, dimColor: theme.info.dim, children: [
1797
1991
  " ",
1798
1992
  "\u2713 ",
1799
- /* @__PURE__ */ jsx8(Text8, { strikethrough: true, children: task.title })
1993
+ /* @__PURE__ */ jsx9(Text9, { strikethrough: true, children: task.title })
1800
1994
  ] });
1801
1995
  }
1802
1996
  if (task.status === "in_progress") {
1803
- return /* @__PURE__ */ jsxs8(Text8, { color: theme.accent, bold: true, children: [
1997
+ return /* @__PURE__ */ jsxs9(Text9, { color: theme.accent, bold: true, children: [
1804
1998
  " ",
1805
- /* @__PURE__ */ jsx8(Spinner4, { type: "dots" }),
1999
+ /* @__PURE__ */ jsx9(Spinner4, { type: "dots" }),
1806
2000
  " ",
1807
2001
  task.title
1808
2002
  ] });
1809
2003
  }
1810
- return /* @__PURE__ */ jsxs8(Text8, { color: theme.info.color, dimColor: theme.info.dim, children: [
2004
+ return /* @__PURE__ */ jsxs9(Text9, { color: theme.info.color, dimColor: theme.info.dim, children: [
1811
2005
  " ",
1812
2006
  "\u2610 ",
1813
2007
  task.title
@@ -2353,9 +2547,9 @@ var init_source = __esm({
2353
2547
  });
2354
2548
 
2355
2549
  // src/ui/text-input.tsx
2356
- import { useState as useState3, useEffect as useEffect3, useRef } from "react";
2357
- import { Text as Text9, useInput } from "ink";
2358
- import { jsx as jsx9 } from "react/jsx-runtime";
2550
+ import { useState as useState4, useEffect as useEffect3, useRef } from "react";
2551
+ import { Text as Text10, useInput } from "ink";
2552
+ import { jsx as jsx10 } from "react/jsx-runtime";
2359
2553
  function shouldTreatAsPaste(input) {
2360
2554
  if (input.length >= PASTE_CHAR_THRESHOLD) return true;
2361
2555
  const newlines = (input.match(/\n/g) ?? []).length;
@@ -2388,7 +2582,7 @@ function CustomTextInput({
2388
2582
  mask,
2389
2583
  enablePaste = false
2390
2584
  }) {
2391
- const [cursorOffset, setCursorOffset] = useState3(value.length);
2585
+ const [cursorOffset, setCursorOffset] = useState4(value.length);
2392
2586
  const pastesRef = useRef(/* @__PURE__ */ new Map());
2393
2587
  useEffect3(() => {
2394
2588
  if (!focus) return;
@@ -2532,7 +2726,7 @@ function CustomTextInput({
2532
2726
  } else if (cursorOffset === displayValue.length) {
2533
2727
  renderedValue += source_default.inverse(" ");
2534
2728
  }
2535
- return /* @__PURE__ */ jsx9(Text9, { children: renderedValue });
2729
+ return /* @__PURE__ */ jsx10(Text10, { children: renderedValue });
2536
2730
  }
2537
2731
  function findPasteTokenEndingAt(value, pos, pastes) {
2538
2732
  if (pos <= 0 || value[pos - 1] !== "]") return -1;
@@ -2553,128 +2747,16 @@ var init_text_input = __esm({
2553
2747
  }
2554
2748
  });
2555
2749
 
2556
- // src/util/update-check.ts
2557
- import { readFile as readFile6, writeFile as writeFile4, mkdir as mkdir3, access } from "fs/promises";
2558
- import { homedir as homedir4 } from "os";
2559
- import { join as join3, dirname as dirname2 } from "path";
2560
- import { fileURLToPath } from "url";
2561
- function cachePath() {
2562
- const xdg = process.env.XDG_CONFIG_HOME || join3(homedir4(), ".config");
2563
- return join3(xdg, "kimiflare", "update-check.json");
2564
- }
2565
- async function findPackageJson(startDir) {
2566
- let dir = startDir;
2567
- while (true) {
2568
- const candidate = join3(dir, "package.json");
2569
- try {
2570
- const raw = await readFile6(candidate, "utf8");
2571
- const parsed = JSON.parse(raw);
2572
- if (parsed.name === "kimiflare" && parsed.version) {
2573
- return { path: candidate, version: parsed.version };
2574
- }
2575
- } catch {
2576
- }
2577
- const parent = dirname2(dir);
2578
- if (parent === dir) break;
2579
- dir = parent;
2580
- }
2581
- return null;
2582
- }
2583
- async function readLocalVersion() {
2584
- const here = dirname2(fileURLToPath(import.meta.url));
2585
- const found = await findPackageJson(here);
2586
- return found?.version ?? null;
2587
- }
2588
- async function readCache() {
2589
- try {
2590
- const raw = await readFile6(cachePath(), "utf8");
2591
- const parsed = JSON.parse(raw);
2592
- if (Date.now() - parsed.checkedAt < CACHE_TTL_MS) {
2593
- return parsed;
2594
- }
2595
- } catch {
2596
- }
2597
- return null;
2598
- }
2599
- async function writeCache(entry) {
2600
- const p = cachePath();
2601
- await mkdir3(dirname2(p), { recursive: true });
2602
- await writeFile4(p, JSON.stringify(entry), "utf8");
2603
- }
2604
- async function fetchLatestVersion() {
2605
- try {
2606
- const res = await fetch(NPM_REGISTRY, {
2607
- headers: { "User-Agent": "kimiflare-update-checker", Accept: "application/json" }
2608
- });
2609
- if (!res.ok) return null;
2610
- const data = await res.json();
2611
- return data.version ?? null;
2612
- } catch {
2613
- return null;
2614
- }
2615
- }
2616
- function stripV(v) {
2617
- return v.startsWith("v") ? v.slice(1) : v;
2618
- }
2619
- function isNewer(local, remote) {
2620
- const a = stripV(local).split(".").map(Number);
2621
- const b = stripV(remote).split(".").map(Number);
2622
- for (let i = 0; i < Math.max(a.length, b.length); i++) {
2623
- const av = a[i] ?? 0;
2624
- const bv = b[i] ?? 0;
2625
- if (av < bv) return true;
2626
- if (av > bv) return false;
2627
- }
2628
- return false;
2629
- }
2630
- async function checkForUpdate() {
2631
- const localVersion = await readLocalVersion();
2632
- if (!localVersion) return { hasUpdate: false, localVersion: null, latestVersion: null };
2633
- const cached = await readCache();
2634
- if (cached) {
2635
- return { hasUpdate: cached.hasUpdate, localVersion, latestVersion: cached.latestVersion };
2636
- }
2637
- const latestVersion = await fetchLatestVersion();
2638
- if (!latestVersion) {
2639
- return { hasUpdate: false, localVersion, latestVersion: null };
2640
- }
2641
- const hasUpdate = isNewer(localVersion, latestVersion);
2642
- await writeCache({ checkedAt: Date.now(), latestVersion, hasUpdate });
2643
- return { hasUpdate, localVersion, latestVersion };
2644
- }
2645
- async function isGitRepo() {
2646
- let dir = dirname2(fileURLToPath(import.meta.url));
2647
- while (true) {
2648
- try {
2649
- await access(join3(dir, ".git"));
2650
- return true;
2651
- } catch {
2652
- }
2653
- const parent = dirname2(dir);
2654
- if (parent === dir) break;
2655
- dir = parent;
2656
- }
2657
- return false;
2658
- }
2659
- var CACHE_TTL_MS, NPM_REGISTRY;
2660
- var init_update_check = __esm({
2661
- "src/util/update-check.ts"() {
2662
- "use strict";
2663
- CACHE_TTL_MS = 60 * 60 * 1e3;
2664
- NPM_REGISTRY = "https://registry.npmjs.org/kimiflare/latest";
2665
- }
2666
- });
2667
-
2668
2750
  // src/ui/onboarding.tsx
2669
- import { useState as useState4 } from "react";
2670
- import { Box as Box9, Text as Text10 } from "ink";
2671
- import { Fragment, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
2751
+ import { useState as useState5 } from "react";
2752
+ import { Box as Box10, Text as Text11 } from "ink";
2753
+ import { Fragment, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
2672
2754
  function Onboarding({ onDone }) {
2673
- const [step, setStep] = useState4("accountId");
2674
- const [accountId, setAccountId] = useState4("");
2675
- const [apiToken, setApiToken] = useState4("");
2676
- const [model, setModel] = useState4(DEFAULT_MODEL);
2677
- const [savedPath, setSavedPath] = useState4(null);
2755
+ const [step, setStep] = useState5("accountId");
2756
+ const [accountId, setAccountId] = useState5("");
2757
+ const [apiToken, setApiToken] = useState5("");
2758
+ const [model, setModel] = useState5(DEFAULT_MODEL);
2759
+ const [savedPath, setSavedPath] = useState5(null);
2678
2760
  const stepIndex = STEPS.indexOf(step) + 1;
2679
2761
  const handleAccountIdSubmit = (value) => {
2680
2762
  const trimmed = value.trim();
@@ -2703,26 +2785,26 @@ function Onboarding({ onDone }) {
2703
2785
  setSavedPath(`error: ${e.message}`);
2704
2786
  }
2705
2787
  };
2706
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", paddingY: 1, children: [
2707
- /* @__PURE__ */ jsxs9(Box9, { marginBottom: 1, children: [
2708
- /* @__PURE__ */ jsx10(Text10, { bold: true, color: "cyan", children: "kimiflare" }),
2709
- /* @__PURE__ */ jsxs9(Text10, { color: "gray", dimColor: true, children: [
2788
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", paddingY: 1, children: [
2789
+ /* @__PURE__ */ jsxs10(Box10, { marginBottom: 1, children: [
2790
+ /* @__PURE__ */ jsx11(Text11, { bold: true, color: "cyan", children: "kimiflare" }),
2791
+ /* @__PURE__ */ jsxs10(Text11, { color: "gray", dimColor: true, children: [
2710
2792
  " ",
2711
2793
  "Terminal coding agent"
2712
2794
  ] })
2713
2795
  ] }),
2714
- /* @__PURE__ */ jsxs9(Text10, { color: "gray", dimColor: true, children: [
2796
+ /* @__PURE__ */ jsxs10(Text11, { color: "gray", dimColor: true, children: [
2715
2797
  "Step ",
2716
2798
  stepIndex,
2717
2799
  " of ",
2718
2800
  STEPS.length
2719
2801
  ] }),
2720
- /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexDirection: "column", children: [
2721
- step === "accountId" && /* @__PURE__ */ jsxs9(Fragment, { children: [
2722
- /* @__PURE__ */ jsx10(Text10, { children: "Enter your Cloudflare Account ID" }),
2723
- /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, children: [
2724
- /* @__PURE__ */ jsx10(Text10, { color: "cyan", children: "\u203A " }),
2725
- /* @__PURE__ */ jsx10(
2802
+ /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, flexDirection: "column", children: [
2803
+ step === "accountId" && /* @__PURE__ */ jsxs10(Fragment, { children: [
2804
+ /* @__PURE__ */ jsx11(Text11, { children: "Enter your Cloudflare Account ID" }),
2805
+ /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, children: [
2806
+ /* @__PURE__ */ jsx11(Text11, { color: "cyan", children: "\u203A " }),
2807
+ /* @__PURE__ */ jsx11(
2726
2808
  CustomTextInput,
2727
2809
  {
2728
2810
  value: accountId,
@@ -2732,12 +2814,12 @@ function Onboarding({ onDone }) {
2732
2814
  )
2733
2815
  ] })
2734
2816
  ] }),
2735
- step === "apiToken" && /* @__PURE__ */ jsxs9(Fragment, { children: [
2736
- /* @__PURE__ */ jsx10(Text10, { children: "Enter your Cloudflare API Token" }),
2737
- /* @__PURE__ */ jsx10(Text10, { color: "gray", dimColor: true, children: "Create one at https://dash.cloudflare.com/profile/api-tokens" }),
2738
- /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, children: [
2739
- /* @__PURE__ */ jsx10(Text10, { color: "cyan", children: "\u203A " }),
2740
- /* @__PURE__ */ jsx10(
2817
+ step === "apiToken" && /* @__PURE__ */ jsxs10(Fragment, { children: [
2818
+ /* @__PURE__ */ jsx11(Text11, { children: "Enter your Cloudflare API Token" }),
2819
+ /* @__PURE__ */ jsx11(Text11, { color: "gray", dimColor: true, children: "Create one at https://dash.cloudflare.com/profile/api-tokens" }),
2820
+ /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, children: [
2821
+ /* @__PURE__ */ jsx11(Text11, { color: "cyan", children: "\u203A " }),
2822
+ /* @__PURE__ */ jsx11(
2741
2823
  CustomTextInput,
2742
2824
  {
2743
2825
  value: apiToken,
@@ -2748,15 +2830,15 @@ function Onboarding({ onDone }) {
2748
2830
  )
2749
2831
  ] })
2750
2832
  ] }),
2751
- step === "model" && /* @__PURE__ */ jsxs9(Fragment, { children: [
2752
- /* @__PURE__ */ jsx10(Text10, { children: "Model ID (press Enter for default)" }),
2753
- /* @__PURE__ */ jsxs9(Text10, { color: "gray", dimColor: true, children: [
2833
+ step === "model" && /* @__PURE__ */ jsxs10(Fragment, { children: [
2834
+ /* @__PURE__ */ jsx11(Text11, { children: "Model ID (press Enter for default)" }),
2835
+ /* @__PURE__ */ jsxs10(Text11, { color: "gray", dimColor: true, children: [
2754
2836
  "default: ",
2755
2837
  DEFAULT_MODEL
2756
2838
  ] }),
2757
- /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, children: [
2758
- /* @__PURE__ */ jsx10(Text10, { color: "cyan", children: "\u203A " }),
2759
- /* @__PURE__ */ jsx10(
2839
+ /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, children: [
2840
+ /* @__PURE__ */ jsx11(Text11, { color: "cyan", children: "\u203A " }),
2841
+ /* @__PURE__ */ jsx11(
2760
2842
  CustomTextInput,
2761
2843
  {
2762
2844
  value: model,
@@ -2766,10 +2848,10 @@ function Onboarding({ onDone }) {
2766
2848
  )
2767
2849
  ] })
2768
2850
  ] }),
2769
- step === "confirm" && /* @__PURE__ */ jsxs9(Fragment, { children: [
2770
- /* @__PURE__ */ jsx10(Text10, { children: "Ready to save configuration" }),
2771
- /* @__PURE__ */ jsxs9(
2772
- Box9,
2851
+ step === "confirm" && /* @__PURE__ */ jsxs10(Fragment, { children: [
2852
+ /* @__PURE__ */ jsx11(Text11, { children: "Ready to save configuration" }),
2853
+ /* @__PURE__ */ jsxs10(
2854
+ Box10,
2773
2855
  {
2774
2856
  flexDirection: "column",
2775
2857
  marginTop: 1,
@@ -2778,25 +2860,25 @@ function Onboarding({ onDone }) {
2778
2860
  borderColor: "gray",
2779
2861
  paddingX: 1,
2780
2862
  children: [
2781
- /* @__PURE__ */ jsxs9(Text10, { color: "gray", children: [
2863
+ /* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
2782
2864
  "Account ID: ",
2783
2865
  accountId
2784
2866
  ] }),
2785
- /* @__PURE__ */ jsxs9(Text10, { color: "gray", children: [
2867
+ /* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
2786
2868
  "API Token: ",
2787
2869
  "\u2022".repeat(apiToken.length)
2788
2870
  ] }),
2789
- /* @__PURE__ */ jsxs9(Text10, { color: "gray", children: [
2871
+ /* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
2790
2872
  "Model: ",
2791
2873
  model
2792
2874
  ] })
2793
2875
  ]
2794
2876
  }
2795
2877
  ),
2796
- /* @__PURE__ */ jsx10(Text10, { children: "Press Enter to confirm, or Ctrl+C to cancel" }),
2797
- /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, children: [
2798
- /* @__PURE__ */ jsx10(Text10, { color: "cyan", children: "\u203A " }),
2799
- /* @__PURE__ */ jsx10(
2878
+ /* @__PURE__ */ jsx11(Text11, { children: "Press Enter to confirm, or Ctrl+C to cancel" }),
2879
+ /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, children: [
2880
+ /* @__PURE__ */ jsx11(Text11, { color: "cyan", children: "\u203A " }),
2881
+ /* @__PURE__ */ jsx11(
2800
2882
  CustomTextInput,
2801
2883
  {
2802
2884
  value: "",
@@ -2807,7 +2889,7 @@ function Onboarding({ onDone }) {
2807
2889
  )
2808
2890
  ] })
2809
2891
  ] }),
2810
- savedPath && /* @__PURE__ */ jsxs9(Text10, { color: "green", children: [
2892
+ savedPath && /* @__PURE__ */ jsxs10(Text11, { color: "green", children: [
2811
2893
  "Config saved to ",
2812
2894
  savedPath
2813
2895
  ] })
@@ -2825,26 +2907,26 @@ var init_onboarding = __esm({
2825
2907
  });
2826
2908
 
2827
2909
  // src/ui/welcome.tsx
2828
- import { Box as Box10, Text as Text11 } from "ink";
2829
- import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
2910
+ import { Box as Box11, Text as Text12 } from "ink";
2911
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
2830
2912
  function Welcome({ theme }) {
2831
- return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", marginBottom: 1, children: [
2832
- /* @__PURE__ */ jsxs10(Box10, { marginBottom: 1, children: [
2833
- /* @__PURE__ */ jsx11(Text11, { bold: true, color: theme.accent, children: "kimiflare" }),
2834
- /* @__PURE__ */ jsxs10(Text11, { color: theme.info.color, dimColor: theme.info.dim, children: [
2913
+ return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginBottom: 1, children: [
2914
+ /* @__PURE__ */ jsxs11(Box11, { marginBottom: 1, children: [
2915
+ /* @__PURE__ */ jsx12(Text12, { bold: true, color: theme.accent, children: "kimiflare" }),
2916
+ /* @__PURE__ */ jsxs11(Text12, { color: theme.info.color, dimColor: theme.info.dim, children: [
2835
2917
  " ",
2836
2918
  "Ready when you are."
2837
2919
  ] })
2838
2920
  ] }),
2839
- /* @__PURE__ */ jsx11(Box10, { flexDirection: "column", children: SUGGESTIONS.map((s, i) => /* @__PURE__ */ jsxs10(Box10, { children: [
2840
- /* @__PURE__ */ jsxs10(Text11, { color: theme.info.color, dimColor: theme.info.dim, children: [
2921
+ /* @__PURE__ */ jsx12(Box11, { flexDirection: "column", children: SUGGESTIONS.map((s, i) => /* @__PURE__ */ jsxs11(Box11, { children: [
2922
+ /* @__PURE__ */ jsxs11(Text12, { color: theme.info.color, dimColor: theme.info.dim, children: [
2841
2923
  " ",
2842
2924
  "\u203A",
2843
2925
  " "
2844
2926
  ] }),
2845
- /* @__PURE__ */ jsx11(Text11, { color: theme.user, children: s })
2927
+ /* @__PURE__ */ jsx12(Text12, { color: theme.user, children: s })
2846
2928
  ] }, i)) }),
2847
- /* @__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" }) })
2929
+ /* @__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" }) })
2848
2930
  ] });
2849
2931
  }
2850
2932
  var SUGGESTIONS;
@@ -2867,7 +2949,10 @@ function resolveTheme(name) {
2867
2949
  function themeNames() {
2868
2950
  return Object.keys(THEMES);
2869
2951
  }
2870
- var dark, light, highContrast, THEMES, DEFAULT_THEME_NAME;
2952
+ function themeList() {
2953
+ return Object.values(THEMES);
2954
+ }
2955
+ var dark, light, highContrast, dracula, nord, oneDark, monokai, solarizedDark, solarizedLight, tokyoNight, gruvboxDark, gruvboxLight, catppuccinMocha, rosePine, THEMES, DEFAULT_THEME_NAME;
2871
2956
  var init_theme = __esm({
2872
2957
  "src/ui/theme.ts"() {
2873
2958
  "use strict";
@@ -2919,10 +3004,197 @@ var init_theme = __esm({
2919
3004
  accent: "cyanBright",
2920
3005
  modeBadge: { plan: "blueBright", auto: "greenBright", edit: "cyanBright" }
2921
3006
  };
3007
+ dracula = {
3008
+ name: "dracula",
3009
+ label: "dracula (purple & cyan, popular dark)",
3010
+ user: "cyanBright",
3011
+ assistant: void 0,
3012
+ reasoning: { color: "magenta", dim: true },
3013
+ info: { color: "gray", dim: true },
3014
+ error: "redBright",
3015
+ warn: "yellowBright",
3016
+ tool: "magentaBright",
3017
+ spinner: "cyanBright",
3018
+ permission: "yellowBright",
3019
+ queue: { color: "gray", dim: true },
3020
+ accent: "magentaBright",
3021
+ modeBadge: { plan: "blueBright", auto: "greenBright", edit: "magentaBright" }
3022
+ };
3023
+ nord = {
3024
+ name: "nord",
3025
+ label: "nord (arctic blue & frost, calm dark)",
3026
+ user: "cyan",
3027
+ assistant: void 0,
3028
+ reasoning: { color: "blue", dim: true },
3029
+ info: { color: "blue", dim: true },
3030
+ error: "red",
3031
+ warn: "yellow",
3032
+ tool: "cyan",
3033
+ spinner: "cyan",
3034
+ permission: "yellow",
3035
+ queue: { color: "blue", dim: true },
3036
+ accent: "cyan",
3037
+ modeBadge: { plan: "blue", auto: "green", edit: "cyan" }
3038
+ };
3039
+ oneDark = {
3040
+ name: "one-dark",
3041
+ label: "one-dark (Atom's classic dark)",
3042
+ user: "cyan",
3043
+ assistant: void 0,
3044
+ reasoning: { color: "gray", dim: true },
3045
+ info: { color: "gray", dim: true },
3046
+ error: "red",
3047
+ warn: "yellow",
3048
+ tool: "blue",
3049
+ spinner: "cyan",
3050
+ permission: "yellow",
3051
+ queue: { color: "gray", dim: true },
3052
+ accent: "blue",
3053
+ modeBadge: { plan: "blue", auto: "green", edit: "cyan" }
3054
+ };
3055
+ monokai = {
3056
+ name: "monokai",
3057
+ label: "monokai (vibrant magenta & yellow)",
3058
+ user: "magentaBright",
3059
+ assistant: void 0,
3060
+ reasoning: { color: "gray", dim: true },
3061
+ info: { color: "gray", dim: true },
3062
+ error: "redBright",
3063
+ warn: "yellowBright",
3064
+ tool: "cyanBright",
3065
+ spinner: "yellowBright",
3066
+ permission: "yellowBright",
3067
+ queue: { color: "gray", dim: true },
3068
+ accent: "magentaBright",
3069
+ modeBadge: { plan: "blueBright", auto: "greenBright", edit: "magentaBright" }
3070
+ };
3071
+ solarizedDark = {
3072
+ name: "solarized-dark",
3073
+ label: "solarized-dark (muted blue & yellow)",
3074
+ user: "cyan",
3075
+ assistant: void 0,
3076
+ reasoning: { color: "blue", dim: true },
3077
+ info: { color: "blue", dim: true },
3078
+ error: "red",
3079
+ warn: "yellow",
3080
+ tool: "cyan",
3081
+ spinner: "yellow",
3082
+ permission: "yellow",
3083
+ queue: { color: "blue", dim: true },
3084
+ accent: "cyan",
3085
+ modeBadge: { plan: "blue", auto: "green", edit: "cyan" }
3086
+ };
3087
+ solarizedLight = {
3088
+ name: "solarized-light",
3089
+ label: "solarized-light (light beige & cyan)",
3090
+ user: "blue",
3091
+ assistant: void 0,
3092
+ reasoning: { color: "cyan", dim: false },
3093
+ info: { color: "cyan", dim: false },
3094
+ error: "red",
3095
+ warn: "yellow",
3096
+ tool: "blue",
3097
+ spinner: "blue",
3098
+ permission: "yellow",
3099
+ queue: { color: "cyan", dim: false },
3100
+ accent: "blue",
3101
+ modeBadge: { plan: "blue", auto: "green", edit: "blue" }
3102
+ };
3103
+ tokyoNight = {
3104
+ name: "tokyo-night",
3105
+ label: "tokyo-night (deep blue & purple)",
3106
+ user: "cyanBright",
3107
+ assistant: void 0,
3108
+ reasoning: { color: "blue", dim: true },
3109
+ info: { color: "blue", dim: true },
3110
+ error: "redBright",
3111
+ warn: "yellow",
3112
+ tool: "magentaBright",
3113
+ spinner: "cyanBright",
3114
+ permission: "yellow",
3115
+ queue: { color: "blue", dim: true },
3116
+ accent: "magentaBright",
3117
+ modeBadge: { plan: "blueBright", auto: "greenBright", edit: "magentaBright" }
3118
+ };
3119
+ gruvboxDark = {
3120
+ name: "gruvbox-dark",
3121
+ label: "gruvbox-dark (warm retro dark)",
3122
+ user: "yellow",
3123
+ assistant: void 0,
3124
+ reasoning: { color: "gray", dim: true },
3125
+ info: { color: "gray", dim: true },
3126
+ error: "red",
3127
+ warn: "yellowBright",
3128
+ tool: "cyan",
3129
+ spinner: "yellow",
3130
+ permission: "yellowBright",
3131
+ queue: { color: "gray", dim: true },
3132
+ accent: "yellow",
3133
+ modeBadge: { plan: "blue", auto: "green", edit: "yellow" }
3134
+ };
3135
+ gruvboxLight = {
3136
+ name: "gruvbox-light",
3137
+ label: "gruvbox-light (warm retro light)",
3138
+ user: "blue",
3139
+ assistant: void 0,
3140
+ reasoning: { color: "blackBright", dim: false },
3141
+ info: { color: "blackBright", dim: false },
3142
+ error: "red",
3143
+ warn: "yellow",
3144
+ tool: "cyan",
3145
+ spinner: "blue",
3146
+ permission: "yellow",
3147
+ queue: { color: "blackBright", dim: false },
3148
+ accent: "blue",
3149
+ modeBadge: { plan: "blue", auto: "green", edit: "blue" }
3150
+ };
3151
+ catppuccinMocha = {
3152
+ name: "catppuccin-mocha",
3153
+ label: "catppuccin-mocha (pastel pink & lavender)",
3154
+ user: "magentaBright",
3155
+ assistant: void 0,
3156
+ reasoning: { color: "gray", dim: true },
3157
+ info: { color: "gray", dim: true },
3158
+ error: "redBright",
3159
+ warn: "yellow",
3160
+ tool: "cyanBright",
3161
+ spinner: "cyanBright",
3162
+ permission: "yellow",
3163
+ queue: { color: "gray", dim: true },
3164
+ accent: "magentaBright",
3165
+ modeBadge: { plan: "blueBright", auto: "greenBright", edit: "magentaBright" }
3166
+ };
3167
+ rosePine = {
3168
+ name: "rose-pine",
3169
+ label: "rose-pine (soft rose & foam)",
3170
+ user: "magenta",
3171
+ assistant: void 0,
3172
+ reasoning: { color: "gray", dim: true },
3173
+ info: { color: "gray", dim: true },
3174
+ error: "red",
3175
+ warn: "yellow",
3176
+ tool: "cyan",
3177
+ spinner: "magenta",
3178
+ permission: "yellow",
3179
+ queue: { color: "gray", dim: true },
3180
+ accent: "magenta",
3181
+ modeBadge: { plan: "blue", auto: "green", edit: "magenta" }
3182
+ };
2922
3183
  THEMES = {
2923
3184
  dark,
2924
3185
  light,
2925
- "high-contrast": highContrast
3186
+ "high-contrast": highContrast,
3187
+ dracula,
3188
+ nord,
3189
+ "one-dark": oneDark,
3190
+ monokai,
3191
+ "solarized-dark": solarizedDark,
3192
+ "solarized-light": solarizedLight,
3193
+ "tokyo-night": tokyoNight,
3194
+ "gruvbox-dark": gruvboxDark,
3195
+ "gruvbox-light": gruvboxLight,
3196
+ "catppuccin-mocha": catppuccinMocha,
3197
+ "rose-pine": rosePine
2926
3198
  };
2927
3199
  DEFAULT_THEME_NAME = "dark";
2928
3200
  }
@@ -2997,36 +3269,40 @@ var app_exports = {};
2997
3269
  __export(app_exports, {
2998
3270
  renderApp: () => renderApp
2999
3271
  });
3000
- import { useState as useState5, useRef as useRef2, useEffect as useEffect4, useCallback } from "react";
3001
- import { Box as Box11, Text as Text12, useApp, useInput as useInput2, render } from "ink";
3272
+ import { useState as useState6, useRef as useRef2, useEffect as useEffect4, useCallback } from "react";
3273
+ import { Box as Box12, Text as Text13, useApp, useInput as useInput2, render } from "ink";
3002
3274
  import { existsSync } from "fs";
3003
3275
  import { join as join5 } from "path";
3004
3276
  import { unlink } from "fs/promises";
3005
- import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
3006
- function App({ initialCfg }) {
3277
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
3278
+ function App({ initialCfg, initialUpdateResult }) {
3007
3279
  const { exit } = useApp();
3008
- const [cfg, setCfg] = useState5(initialCfg);
3009
- const [events, setEvents] = useState5([]);
3010
- const [input, setInput] = useState5("");
3011
- const [busy, setBusy] = useState5(false);
3012
- const [usage, setUsage] = useState5(null);
3013
- const [showReasoning, setShowReasoning] = useState5(false);
3014
- const [perm, setPerm] = useState5(null);
3015
- const [queue, setQueue] = useState5([]);
3016
- const [history, setHistory] = useState5([]);
3017
- const [historyIndex, setHistoryIndex] = useState5(-1);
3018
- const [draftInput, setDraftInput] = useState5("");
3019
- const [mode, setMode] = useState5("edit");
3020
- const [effort, setEffort] = useState5(
3280
+ const [cfg, setCfg] = useState6(initialCfg);
3281
+ const [events, setEvents] = useState6([]);
3282
+ const [input, setInput] = useState6("");
3283
+ const [busy, setBusy] = useState6(false);
3284
+ const [usage, setUsage] = useState6(null);
3285
+ const [showReasoning, setShowReasoning] = useState6(false);
3286
+ const [perm, setPerm] = useState6(null);
3287
+ const [queue, setQueue] = useState6([]);
3288
+ const [history, setHistory] = useState6([]);
3289
+ const [historyIndex, setHistoryIndex] = useState6(-1);
3290
+ const [draftInput, setDraftInput] = useState6("");
3291
+ const [mode, setMode] = useState6("edit");
3292
+ const [effort, setEffort] = useState6(
3021
3293
  initialCfg?.reasoningEffort ?? DEFAULT_REASONING_EFFORT
3022
3294
  );
3023
- const [theme, setTheme] = useState5(resolveTheme(initialCfg?.theme));
3024
- const [resumeSessions, setResumeSessions] = useState5(null);
3025
- const [tasks, setTasks] = useState5([]);
3026
- const [tasksStartedAt, setTasksStartedAt] = useState5(null);
3027
- const [tasksStartTokens, setTasksStartTokens] = useState5(0);
3028
- const [turnStartedAt, setTurnStartedAt] = useState5(null);
3029
- const [verbose, setVerbose] = useState5(false);
3295
+ const [theme, setTheme] = useState6(resolveTheme(initialCfg?.theme));
3296
+ const [resumeSessions, setResumeSessions] = useState6(null);
3297
+ const [showThemePicker, setShowThemePicker] = useState6(false);
3298
+ const [originalTheme, setOriginalTheme] = useState6(null);
3299
+ const [tasks, setTasks] = useState6([]);
3300
+ const [tasksStartedAt, setTasksStartedAt] = useState6(null);
3301
+ const [tasksStartTokens, setTasksStartTokens] = useState6(0);
3302
+ const [turnStartedAt, setTurnStartedAt] = useState6(null);
3303
+ const [verbose, setVerbose] = useState6(false);
3304
+ const [hasUpdate, setHasUpdate] = useState6(initialUpdateResult?.hasUpdate ?? false);
3305
+ const [latestVersion, setLatestVersion] = useState6(initialUpdateResult?.latestVersion ?? null);
3030
3306
  const messagesRef = useRef2([
3031
3307
  {
3032
3308
  role: "system",
@@ -3047,12 +3323,42 @@ function App({ initialCfg }) {
3047
3323
  const tasksRef = useRef2([]);
3048
3324
  const usageRef = useRef2(null);
3049
3325
  const updateCheckedRef = useRef2(false);
3326
+ const updateNudgedRef = useRef2(false);
3050
3327
  const compactSuggestedRef = useRef2(false);
3051
3328
  useEffect4(() => {
3052
3329
  if (!cfg || updateCheckedRef.current) return;
3053
3330
  updateCheckedRef.current = true;
3331
+ if (initialUpdateResult) {
3332
+ if (initialUpdateResult.hasUpdate && !updateNudgedRef.current) {
3333
+ updateNudgedRef.current = true;
3334
+ setHasUpdate(true);
3335
+ setLatestVersion(initialUpdateResult.latestVersion);
3336
+ setEvents((e) => [
3337
+ ...e,
3338
+ {
3339
+ kind: "info",
3340
+ key: mkKey(),
3341
+ text: `update available: ${initialUpdateResult.localVersion} \u2192 ${initialUpdateResult.latestVersion}`
3342
+ }
3343
+ ]);
3344
+ void isGitRepo().then((git) => {
3345
+ setEvents((e) => [
3346
+ ...e,
3347
+ {
3348
+ kind: "info",
3349
+ key: mkKey(),
3350
+ text: git ? "run: git pull && npm install && npm run build then restart kimiflare" : "run: npm update -g kimiflare then restart"
3351
+ }
3352
+ ]);
3353
+ });
3354
+ }
3355
+ return;
3356
+ }
3054
3357
  void checkForUpdate().then((result) => {
3055
- if (result.hasUpdate) {
3358
+ if (result.hasUpdate && !updateNudgedRef.current) {
3359
+ updateNudgedRef.current = true;
3360
+ setHasUpdate(true);
3361
+ setLatestVersion(result.latestVersion);
3056
3362
  setEvents((e) => [
3057
3363
  ...e,
3058
3364
  {
@@ -3073,7 +3379,7 @@ function App({ initialCfg }) {
3073
3379
  });
3074
3380
  }
3075
3381
  });
3076
- }, [cfg]);
3382
+ }, [cfg, initialUpdateResult]);
3077
3383
  useEffect4(() => {
3078
3384
  modeRef.current = mode;
3079
3385
  messagesRef.current[0] = {
@@ -3092,6 +3398,39 @@ function App({ initialCfg }) {
3092
3398
  useEffect4(() => {
3093
3399
  effortRef.current = effort;
3094
3400
  }, [effort]);
3401
+ useEffect4(() => {
3402
+ if (!cfg) return;
3403
+ const id = setInterval(() => {
3404
+ void checkForUpdate().then((result) => {
3405
+ if (result.hasUpdate) {
3406
+ setHasUpdate(true);
3407
+ setLatestVersion(result.latestVersion);
3408
+ if (!updateNudgedRef.current) {
3409
+ updateNudgedRef.current = true;
3410
+ setEvents((e) => [
3411
+ ...e,
3412
+ {
3413
+ kind: "info",
3414
+ key: mkKey(),
3415
+ text: `update available: ${result.localVersion} \u2192 ${result.latestVersion}`
3416
+ }
3417
+ ]);
3418
+ void isGitRepo().then((git) => {
3419
+ setEvents((e) => [
3420
+ ...e,
3421
+ {
3422
+ kind: "info",
3423
+ key: mkKey(),
3424
+ text: git ? "run: git pull && npm install && npm run build then restart kimiflare" : "run: npm update -g kimiflare then restart"
3425
+ }
3426
+ ]);
3427
+ });
3428
+ }
3429
+ }
3430
+ });
3431
+ }, 30 * 60 * 1e3);
3432
+ return () => clearInterval(id);
3433
+ }, [cfg]);
3095
3434
  const saveSessionSafe = useCallback(async () => {
3096
3435
  if (!cfg) return;
3097
3436
  if (!sessionIdRef.current) {
@@ -3134,6 +3473,11 @@ function App({ initialCfg }) {
3134
3473
  setVerbose((v) => !v);
3135
3474
  return;
3136
3475
  }
3476
+ if (key.ctrl && inputChar === "t") {
3477
+ setOriginalTheme(theme);
3478
+ setShowThemePicker(true);
3479
+ return;
3480
+ }
3137
3481
  });
3138
3482
  const updateAssistant = useCallback(
3139
3483
  (id, patch) => {
@@ -3204,7 +3548,7 @@ function App({ initialCfg }) {
3204
3548
  }
3205
3549
  }, [cfg, busy, saveSessionSafe]);
3206
3550
  const openResumePicker = useCallback(async () => {
3207
- const sessions = await listSessions(30);
3551
+ const sessions = await listSessions(200);
3208
3552
  setResumeSessions(sessions);
3209
3553
  }, []);
3210
3554
  const runInit = useCallback(async () => {
@@ -3372,6 +3716,27 @@ function App({ initialCfg }) {
3372
3716
  },
3373
3717
  []
3374
3718
  );
3719
+ const handleThemePick = useCallback(
3720
+ (picked) => {
3721
+ if (!picked) {
3722
+ if (originalTheme) setTheme(originalTheme);
3723
+ setShowThemePicker(false);
3724
+ setOriginalTheme(null);
3725
+ return;
3726
+ }
3727
+ setTheme(picked);
3728
+ setCfg((c) => c ? { ...c, theme: picked.name } : c);
3729
+ if (cfg) void saveConfig({ ...cfg, theme: picked.name }).catch(() => {
3730
+ });
3731
+ setShowThemePicker(false);
3732
+ setOriginalTheme(null);
3733
+ setEvents((e) => [
3734
+ ...e,
3735
+ { kind: "info", key: mkKey(), text: `theme: ${picked.label}` }
3736
+ ]);
3737
+ },
3738
+ [cfg, originalTheme]
3739
+ );
3375
3740
  const handleSlash = useCallback(
3376
3741
  (cmd) => {
3377
3742
  const raw = cmd.trim();
@@ -3391,6 +3756,7 @@ function App({ initialCfg }) {
3391
3756
  setTasksStartedAt(null);
3392
3757
  setTasksStartTokens(0);
3393
3758
  compactSuggestedRef.current = false;
3759
+ updateNudgedRef.current = false;
3394
3760
  return true;
3395
3761
  }
3396
3762
  if (c === "/reasoning") {
@@ -3457,14 +3823,8 @@ use: /thinking low | medium | high`
3457
3823
  }
3458
3824
  if (c === "/theme") {
3459
3825
  if (!arg) {
3460
- setEvents((e) => [
3461
- ...e,
3462
- {
3463
- kind: "info",
3464
- key: mkKey(),
3465
- text: `current: ${theme.name} \xB7 available: ${themeNames().join(", ")}`
3466
- }
3467
- ]);
3826
+ setOriginalTheme(theme);
3827
+ setShowThemePicker(true);
3468
3828
  return true;
3469
3829
  }
3470
3830
  const next = resolveTheme(arg);
@@ -3535,8 +3895,10 @@ use: /thinking low | medium | high`
3535
3895
  return true;
3536
3896
  }
3537
3897
  if (c === "/update") {
3538
- void checkForUpdate().then((result) => {
3898
+ void checkForUpdate(true).then((result) => {
3539
3899
  if (result.hasUpdate) {
3900
+ setHasUpdate(true);
3901
+ setLatestVersion(result.latestVersion);
3540
3902
  setEvents((e) => [
3541
3903
  ...e,
3542
3904
  {
@@ -3556,6 +3918,8 @@ use: /thinking low | medium | high`
3556
3918
  ]);
3557
3919
  });
3558
3920
  } else {
3921
+ setHasUpdate(false);
3922
+ setLatestVersion(null);
3559
3923
  setEvents((e) => [
3560
3924
  ...e,
3561
3925
  { kind: "info", key: mkKey(), text: "no update available" }
@@ -3580,7 +3944,7 @@ use: /thinking low | medium | high`
3580
3944
  {
3581
3945
  kind: "info",
3582
3946
  key: mkKey(),
3583
- 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"
3947
+ 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"
3584
3948
  }
3585
3949
  ]);
3586
3950
  return true;
@@ -3762,7 +4126,7 @@ use: /thinking low | medium | high`
3762
4126
  }
3763
4127
  }, [usage]);
3764
4128
  if (!cfg) {
3765
- return /* @__PURE__ */ jsx12(
4129
+ return /* @__PURE__ */ jsx13(
3766
4130
  Onboarding,
3767
4131
  {
3768
4132
  onDone: (newCfg) => {
@@ -3776,12 +4140,15 @@ use: /thinking low | medium | high`
3776
4140
  );
3777
4141
  }
3778
4142
  if (resumeSessions !== null) {
3779
- return /* @__PURE__ */ jsx12(Box11, { flexDirection: "column", children: /* @__PURE__ */ jsx12(ResumePicker, { sessions: resumeSessions, onPick: handleResumePick, theme }) });
4143
+ return /* @__PURE__ */ jsx13(Box12, { flexDirection: "column", children: /* @__PURE__ */ jsx13(ResumePicker, { sessions: resumeSessions, onPick: handleResumePick, theme }) });
4144
+ }
4145
+ if (showThemePicker) {
4146
+ return /* @__PURE__ */ jsx13(Box12, { flexDirection: "column", children: /* @__PURE__ */ jsx13(ThemePicker, { themes: themeList(), current: theme, onPick: handleThemePick }) });
3780
4147
  }
3781
4148
  const hasConversation = events.some((e) => e.kind === "user" || e.kind === "assistant");
3782
- return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
3783
- !hasConversation && events.length === 0 ? /* @__PURE__ */ jsx12(Welcome, { theme }) : /* @__PURE__ */ jsx12(ChatView, { events, showReasoning, theme, verbose }),
3784
- perm ? /* @__PURE__ */ jsx12(
4149
+ return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
4150
+ !hasConversation && events.length === 0 ? /* @__PURE__ */ jsx13(Welcome, { theme }) : /* @__PURE__ */ jsx13(ChatView, { events, showReasoning, theme, verbose }),
4151
+ perm ? /* @__PURE__ */ jsx13(
3785
4152
  PermissionModal,
3786
4153
  {
3787
4154
  tool: perm.tool,
@@ -3792,8 +4159,8 @@ use: /thinking low | medium | high`
3792
4159
  setPerm(null);
3793
4160
  }
3794
4161
  }
3795
- ) : /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginTop: 1, children: [
3796
- tasks.length > 0 && /* @__PURE__ */ jsx12(
4162
+ ) : /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginTop: 1, children: [
4163
+ tasks.length > 0 && /* @__PURE__ */ jsx13(
3797
4164
  TaskList,
3798
4165
  {
3799
4166
  tasks,
@@ -3802,11 +4169,11 @@ use: /thinking low | medium | high`
3802
4169
  tokensDelta: Math.max(0, (usage?.prompt_tokens ?? 0) - tasksStartTokens)
3803
4170
  }
3804
4171
  ),
3805
- 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: [
4172
+ 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: [
3806
4173
  "\u23F3 ",
3807
4174
  q.display
3808
4175
  ] }, `queue_${i}`)) }),
3809
- /* @__PURE__ */ jsx12(
4176
+ /* @__PURE__ */ jsx13(
3810
4177
  StatusBar,
3811
4178
  {
3812
4179
  model: cfg.model,
@@ -3816,12 +4183,14 @@ use: /thinking low | medium | high`
3816
4183
  theme,
3817
4184
  mode,
3818
4185
  effort,
3819
- contextLimit: CONTEXT_LIMIT
4186
+ contextLimit: CONTEXT_LIMIT,
4187
+ hasUpdate,
4188
+ latestVersion
3820
4189
  }
3821
4190
  ),
3822
- /* @__PURE__ */ jsxs11(Box11, { marginTop: 1, children: [
3823
- /* @__PURE__ */ jsx12(Text12, { color: theme.accent, children: "\u203A " }),
3824
- /* @__PURE__ */ jsx12(
4191
+ /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, children: [
4192
+ /* @__PURE__ */ jsx13(Text13, { color: theme.accent, children: "\u203A " }),
4193
+ /* @__PURE__ */ jsx13(
3825
4194
  CustomTextInput,
3826
4195
  {
3827
4196
  value: input,
@@ -3869,8 +4238,8 @@ use: /thinking low | medium | high`
3869
4238
  ] })
3870
4239
  ] });
3871
4240
  }
3872
- async function renderApp(cfg) {
3873
- const instance = render(/* @__PURE__ */ jsx12(App, { initialCfg: cfg }));
4241
+ async function renderApp(cfg, updateResult) {
4242
+ const instance = render(/* @__PURE__ */ jsx13(App, { initialCfg: cfg, initialUpdateResult: updateResult }));
3874
4243
  await instance.waitUntilExit();
3875
4244
  }
3876
4245
  var CONTEXT_LIMIT, AUTO_COMPACT_SUGGEST_PCT, nextAssistantId, nextKey, mkKey, EFFORT_DESCRIPTIONS;
@@ -3886,6 +4255,7 @@ var init_app = __esm({
3886
4255
  init_status();
3887
4256
  init_permission();
3888
4257
  init_resume_picker();
4258
+ init_theme_picker();
3889
4259
  init_task_list();
3890
4260
  init_text_input();
3891
4261
  init_update_check();
@@ -3913,6 +4283,7 @@ init_config();
3913
4283
  init_loop();
3914
4284
  init_system_prompt();
3915
4285
  init_executor();
4286
+ init_update_check();
3916
4287
  import { Command } from "commander";
3917
4288
  import { readFileSync as readFileSync2 } from "fs";
3918
4289
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -3931,6 +4302,7 @@ program.name("kimiflare").description("Terminal coding agent powered by Kimi-K2.
3931
4302
  var opts = program.opts();
3932
4303
  async function main() {
3933
4304
  const cfg = await loadConfig();
4305
+ const updateResult = await checkForUpdate();
3934
4306
  if (opts.print !== void 0) {
3935
4307
  if (!cfg) {
3936
4308
  console.error(
@@ -3944,7 +4316,8 @@ async function main() {
3944
4316
  model,
3945
4317
  prompt: opts.print,
3946
4318
  allowAll: !!opts.dangerouslyAllowAll,
3947
- showReasoning: !!opts.reasoning
4319
+ showReasoning: !!opts.reasoning,
4320
+ updateResult
3948
4321
  });
3949
4322
  return;
3950
4323
  }
@@ -3957,12 +4330,21 @@ async function main() {
3957
4330
  const { renderApp: renderApp2 } = await Promise.resolve().then(() => (init_app(), app_exports));
3958
4331
  if (cfg) {
3959
4332
  const model = opts.model ?? cfg.model ?? DEFAULT_MODEL;
3960
- await renderApp2({ ...cfg, model });
4333
+ await renderApp2({ ...cfg, model }, updateResult);
3961
4334
  } else {
3962
- await renderApp2(null);
4335
+ await renderApp2(null, updateResult);
3963
4336
  }
3964
4337
  }
3965
4338
  async function runPrintMode(opts2) {
4339
+ if (opts2.updateResult.hasUpdate) {
4340
+ const git = await isGitRepo();
4341
+ process.stderr.write(
4342
+ `\x1B[33mkimiflare update available: ${opts2.updateResult.localVersion} \u2192 ${opts2.updateResult.latestVersion}\x1B[0m
4343
+ \x1B[33m ${git ? "git pull && npm install && npm run build" : "npm update -g kimiflare"} then restart\x1B[0m
4344
+
4345
+ `
4346
+ );
4347
+ }
3966
4348
  const cwd = process.cwd();
3967
4349
  const executor = new ToolExecutor(ALL_TOOLS);
3968
4350
  const messages = [