kimiflare 0.7.1 → 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
@@ -1172,6 +1172,120 @@ 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
+ 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
+
1175
1289
  // src/agent/compact.ts
1176
1290
  function indexOfNthUserFromEnd(messages, n) {
1177
1291
  let seen = 0;
@@ -1584,7 +1698,7 @@ import { useEffect, useState } from "react";
1584
1698
  import { Box as Box5, Text as Text5 } from "ink";
1585
1699
  import Spinner3 from "ink-spinner";
1586
1700
  import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1587
- function StatusBar({ model, usage, thinking, turnStartedAt, theme, mode, effort, contextLimit }) {
1701
+ function StatusBar({ model, usage, thinking, turnStartedAt, theme, mode, effort, contextLimit, hasUpdate, latestVersion }) {
1588
1702
  const [now, setNow] = useState(Date.now());
1589
1703
  const modeColor = mode === "plan" ? theme.modeBadge.plan : mode === "auto" ? theme.modeBadge.auto : theme.modeBadge.edit;
1590
1704
  const warn = usage && usage.prompt_tokens / contextLimit >= 0.8;
@@ -1618,6 +1732,12 @@ function StatusBar({ model, usage, thinking, turnStartedAt, theme, mode, effort,
1618
1732
  warn ? /* @__PURE__ */ jsxs5(Text5, { color: theme.warn, bold: true, children: [
1619
1733
  " \xB7 ",
1620
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"
1621
1741
  ] }) : null
1622
1742
  ] })
1623
1743
  ] });
@@ -1691,10 +1811,16 @@ var init_permission = __esm({
1691
1811
  });
1692
1812
 
1693
1813
  // src/ui/resume-picker.tsx
1694
- 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";
1695
1816
  import SelectInput2 from "ink-select-input";
1696
1817
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1697
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);
1698
1824
  if (sessions.length === 0) {
1699
1825
  return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
1700
1826
  /* @__PURE__ */ jsx7(Text7, { color: theme.accent, bold: true, children: "Resume a session" }),
@@ -1708,20 +1834,39 @@ function ResumePicker({ sessions, onPick, theme }) {
1708
1834
  ) })
1709
1835
  ] });
1710
1836
  }
1711
- 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) => ({
1712
1841
  label: `${formatDate(s.updatedAt)} \xB7 ${s.messageCount} msgs \xB7 ${s.firstPrompt}`,
1713
1842
  value: s.id
1714
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
+ }
1715
1850
  items.push({ label: "(cancel)", value: "__cancel__" });
1716
1851
  return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
1717
1852
  /* @__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." }),
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
+ ] }),
1719
1862
  /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(
1720
1863
  SelectInput2,
1721
1864
  {
1722
1865
  items,
1723
1866
  onSelect: (item) => {
1724
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));
1725
1870
  const picked = sessions.find((s) => s.id === item.value) ?? null;
1726
1871
  onPick(picked);
1727
1872
  }
@@ -1742,19 +1887,70 @@ function formatDate(iso) {
1742
1887
  return iso;
1743
1888
  }
1744
1889
  }
1890
+ var HEADER_ROWS, FOOTER_ROWS, MIN_PAGE_SIZE;
1745
1891
  var init_resume_picker = __esm({
1746
1892
  "src/ui/resume-picker.tsx"() {
1747
1893
  "use strict";
1894
+ HEADER_ROWS = 5;
1895
+ FOOTER_ROWS = 2;
1896
+ MIN_PAGE_SIZE = 5;
1748
1897
  }
1749
1898
  });
1750
1899
 
1751
- // src/ui/task-list.tsx
1752
- import { useEffect as useEffect2, useState as useState2 } from "react";
1900
+ // src/ui/theme-picker.tsx
1753
1901
  import { Box as Box8, Text as Text8 } from "ink";
1754
- import Spinner4 from "ink-spinner";
1902
+ import SelectInput3 from "ink-select-input";
1755
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";
1756
1952
  function TaskList({ tasks, theme, startedAt, tokensDelta }) {
1757
- const [now, setNow] = useState2(Date.now());
1953
+ const [now, setNow] = useState3(Date.now());
1758
1954
  useEffect2(() => {
1759
1955
  if (startedAt === null) return;
1760
1956
  const allDone2 = tasks.length > 0 && tasks.every((t) => t.status === "completed");
@@ -1772,18 +1968,18 @@ function TaskList({ tasks, theme, startedAt, tokensDelta }) {
1772
1968
  const headerStats = [elapsed, tokensDelta > 0 ? `\u2191 ${formatTokens(tokensDelta)} tokens` : null].filter(Boolean).join(" \xB7 ");
1773
1969
  const visibleTasks = tasks.slice(0, MAX_VISIBLE);
1774
1970
  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: [
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: [
1779
1975
  " ",
1780
1976
  "(",
1781
1977
  headerStats,
1782
1978
  ")"
1783
1979
  ] })
1784
1980
  ] }),
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: [
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: [
1787
1983
  " ",
1788
1984
  "\u2026 +",
1789
1985
  hiddenPending,
@@ -1793,21 +1989,21 @@ function TaskList({ tasks, theme, startedAt, tokensDelta }) {
1793
1989
  }
1794
1990
  function TaskRow({ task, theme }) {
1795
1991
  if (task.status === "completed") {
1796
- 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: [
1797
1993
  " ",
1798
1994
  "\u2713 ",
1799
- /* @__PURE__ */ jsx8(Text8, { strikethrough: true, children: task.title })
1995
+ /* @__PURE__ */ jsx9(Text9, { strikethrough: true, children: task.title })
1800
1996
  ] });
1801
1997
  }
1802
1998
  if (task.status === "in_progress") {
1803
- return /* @__PURE__ */ jsxs8(Text8, { color: theme.accent, bold: true, children: [
1999
+ return /* @__PURE__ */ jsxs9(Text9, { color: theme.accent, bold: true, children: [
1804
2000
  " ",
1805
- /* @__PURE__ */ jsx8(Spinner4, { type: "dots" }),
2001
+ /* @__PURE__ */ jsx9(Spinner4, { type: "dots" }),
1806
2002
  " ",
1807
2003
  task.title
1808
2004
  ] });
1809
2005
  }
1810
- 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: [
1811
2007
  " ",
1812
2008
  "\u2610 ",
1813
2009
  task.title
@@ -2353,9 +2549,9 @@ var init_source = __esm({
2353
2549
  });
2354
2550
 
2355
2551
  // 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";
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";
2359
2555
  function shouldTreatAsPaste(input) {
2360
2556
  if (input.length >= PASTE_CHAR_THRESHOLD) return true;
2361
2557
  const newlines = (input.match(/\n/g) ?? []).length;
@@ -2388,7 +2584,7 @@ function CustomTextInput({
2388
2584
  mask,
2389
2585
  enablePaste = false
2390
2586
  }) {
2391
- const [cursorOffset, setCursorOffset] = useState3(value.length);
2587
+ const [cursorOffset, setCursorOffset] = useState4(value.length);
2392
2588
  const pastesRef = useRef(/* @__PURE__ */ new Map());
2393
2589
  useEffect3(() => {
2394
2590
  if (!focus) return;
@@ -2532,7 +2728,7 @@ function CustomTextInput({
2532
2728
  } else if (cursorOffset === displayValue.length) {
2533
2729
  renderedValue += source_default.inverse(" ");
2534
2730
  }
2535
- return /* @__PURE__ */ jsx9(Text9, { children: renderedValue });
2731
+ return /* @__PURE__ */ jsx10(Text10, { children: renderedValue });
2536
2732
  }
2537
2733
  function findPasteTokenEndingAt(value, pos, pastes) {
2538
2734
  if (pos <= 0 || value[pos - 1] !== "]") return -1;
@@ -2553,128 +2749,16 @@ var init_text_input = __esm({
2553
2749
  }
2554
2750
  });
2555
2751
 
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
2752
  // 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";
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";
2672
2756
  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);
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);
2678
2762
  const stepIndex = STEPS.indexOf(step) + 1;
2679
2763
  const handleAccountIdSubmit = (value) => {
2680
2764
  const trimmed = value.trim();
@@ -2703,26 +2787,26 @@ function Onboarding({ onDone }) {
2703
2787
  setSavedPath(`error: ${e.message}`);
2704
2788
  }
2705
2789
  };
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: [
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: [
2710
2794
  " ",
2711
2795
  "Terminal coding agent"
2712
2796
  ] })
2713
2797
  ] }),
2714
- /* @__PURE__ */ jsxs9(Text10, { color: "gray", dimColor: true, children: [
2798
+ /* @__PURE__ */ jsxs10(Text11, { color: "gray", dimColor: true, children: [
2715
2799
  "Step ",
2716
2800
  stepIndex,
2717
2801
  " of ",
2718
2802
  STEPS.length
2719
2803
  ] }),
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(
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(
2726
2810
  CustomTextInput,
2727
2811
  {
2728
2812
  value: accountId,
@@ -2732,12 +2816,12 @@ function Onboarding({ onDone }) {
2732
2816
  )
2733
2817
  ] })
2734
2818
  ] }),
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(
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(
2741
2825
  CustomTextInput,
2742
2826
  {
2743
2827
  value: apiToken,
@@ -2748,15 +2832,15 @@ function Onboarding({ onDone }) {
2748
2832
  )
2749
2833
  ] })
2750
2834
  ] }),
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: [
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: [
2754
2838
  "default: ",
2755
2839
  DEFAULT_MODEL
2756
2840
  ] }),
2757
- /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, children: [
2758
- /* @__PURE__ */ jsx10(Text10, { color: "cyan", children: "\u203A " }),
2759
- /* @__PURE__ */ jsx10(
2841
+ /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, children: [
2842
+ /* @__PURE__ */ jsx11(Text11, { color: "cyan", children: "\u203A " }),
2843
+ /* @__PURE__ */ jsx11(
2760
2844
  CustomTextInput,
2761
2845
  {
2762
2846
  value: model,
@@ -2766,10 +2850,10 @@ function Onboarding({ onDone }) {
2766
2850
  )
2767
2851
  ] })
2768
2852
  ] }),
2769
- step === "confirm" && /* @__PURE__ */ jsxs9(Fragment, { children: [
2770
- /* @__PURE__ */ jsx10(Text10, { children: "Ready to save configuration" }),
2771
- /* @__PURE__ */ jsxs9(
2772
- Box9,
2853
+ step === "confirm" && /* @__PURE__ */ jsxs10(Fragment, { children: [
2854
+ /* @__PURE__ */ jsx11(Text11, { children: "Ready to save configuration" }),
2855
+ /* @__PURE__ */ jsxs10(
2856
+ Box10,
2773
2857
  {
2774
2858
  flexDirection: "column",
2775
2859
  marginTop: 1,
@@ -2778,25 +2862,25 @@ function Onboarding({ onDone }) {
2778
2862
  borderColor: "gray",
2779
2863
  paddingX: 1,
2780
2864
  children: [
2781
- /* @__PURE__ */ jsxs9(Text10, { color: "gray", children: [
2865
+ /* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
2782
2866
  "Account ID: ",
2783
2867
  accountId
2784
2868
  ] }),
2785
- /* @__PURE__ */ jsxs9(Text10, { color: "gray", children: [
2869
+ /* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
2786
2870
  "API Token: ",
2787
2871
  "\u2022".repeat(apiToken.length)
2788
2872
  ] }),
2789
- /* @__PURE__ */ jsxs9(Text10, { color: "gray", children: [
2873
+ /* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
2790
2874
  "Model: ",
2791
2875
  model
2792
2876
  ] })
2793
2877
  ]
2794
2878
  }
2795
2879
  ),
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(
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(
2800
2884
  CustomTextInput,
2801
2885
  {
2802
2886
  value: "",
@@ -2807,7 +2891,7 @@ function Onboarding({ onDone }) {
2807
2891
  )
2808
2892
  ] })
2809
2893
  ] }),
2810
- savedPath && /* @__PURE__ */ jsxs9(Text10, { color: "green", children: [
2894
+ savedPath && /* @__PURE__ */ jsxs10(Text11, { color: "green", children: [
2811
2895
  "Config saved to ",
2812
2896
  savedPath
2813
2897
  ] })
@@ -2825,26 +2909,26 @@ var init_onboarding = __esm({
2825
2909
  });
2826
2910
 
2827
2911
  // 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";
2912
+ import { Box as Box11, Text as Text12 } from "ink";
2913
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
2830
2914
  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: [
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: [
2835
2919
  " ",
2836
2920
  "Ready when you are."
2837
2921
  ] })
2838
2922
  ] }),
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: [
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: [
2841
2925
  " ",
2842
2926
  "\u203A",
2843
2927
  " "
2844
2928
  ] }),
2845
- /* @__PURE__ */ jsx11(Text11, { color: theme.user, children: s })
2929
+ /* @__PURE__ */ jsx12(Text12, { color: theme.user, children: s })
2846
2930
  ] }, 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" }) })
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" }) })
2848
2932
  ] });
2849
2933
  }
2850
2934
  var SUGGESTIONS;
@@ -2867,7 +2951,10 @@ function resolveTheme(name) {
2867
2951
  function themeNames() {
2868
2952
  return Object.keys(THEMES);
2869
2953
  }
2870
- 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;
2871
2958
  var init_theme = __esm({
2872
2959
  "src/ui/theme.ts"() {
2873
2960
  "use strict";
@@ -2919,10 +3006,197 @@ var init_theme = __esm({
2919
3006
  accent: "cyanBright",
2920
3007
  modeBadge: { plan: "blueBright", auto: "greenBright", edit: "cyanBright" }
2921
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
+ };
2922
3185
  THEMES = {
2923
3186
  dark,
2924
3187
  light,
2925
- "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
2926
3200
  };
2927
3201
  DEFAULT_THEME_NAME = "dark";
2928
3202
  }
@@ -2997,36 +3271,40 @@ var app_exports = {};
2997
3271
  __export(app_exports, {
2998
3272
  renderApp: () => renderApp
2999
3273
  });
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";
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";
3002
3276
  import { existsSync } from "fs";
3003
3277
  import { join as join5 } from "path";
3004
3278
  import { unlink } from "fs/promises";
3005
- import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
3006
- function App({ initialCfg }) {
3279
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
3280
+ function App({ initialCfg, initialUpdateResult }) {
3007
3281
  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(
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(
3021
3295
  initialCfg?.reasoningEffort ?? DEFAULT_REASONING_EFFORT
3022
3296
  );
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);
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);
3030
3308
  const messagesRef = useRef2([
3031
3309
  {
3032
3310
  role: "system",
@@ -3047,12 +3325,42 @@ function App({ initialCfg }) {
3047
3325
  const tasksRef = useRef2([]);
3048
3326
  const usageRef = useRef2(null);
3049
3327
  const updateCheckedRef = useRef2(false);
3328
+ const updateNudgedRef = useRef2(false);
3050
3329
  const compactSuggestedRef = useRef2(false);
3051
3330
  useEffect4(() => {
3052
3331
  if (!cfg || updateCheckedRef.current) return;
3053
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
+ }
3054
3359
  void checkForUpdate().then((result) => {
3055
- if (result.hasUpdate) {
3360
+ if (result.hasUpdate && !updateNudgedRef.current) {
3361
+ updateNudgedRef.current = true;
3362
+ setHasUpdate(true);
3363
+ setLatestVersion(result.latestVersion);
3056
3364
  setEvents((e) => [
3057
3365
  ...e,
3058
3366
  {
@@ -3073,7 +3381,7 @@ function App({ initialCfg }) {
3073
3381
  });
3074
3382
  }
3075
3383
  });
3076
- }, [cfg]);
3384
+ }, [cfg, initialUpdateResult]);
3077
3385
  useEffect4(() => {
3078
3386
  modeRef.current = mode;
3079
3387
  messagesRef.current[0] = {
@@ -3092,6 +3400,39 @@ function App({ initialCfg }) {
3092
3400
  useEffect4(() => {
3093
3401
  effortRef.current = effort;
3094
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]);
3095
3436
  const saveSessionSafe = useCallback(async () => {
3096
3437
  if (!cfg) return;
3097
3438
  if (!sessionIdRef.current) {
@@ -3134,6 +3475,11 @@ function App({ initialCfg }) {
3134
3475
  setVerbose((v) => !v);
3135
3476
  return;
3136
3477
  }
3478
+ if (key.ctrl && inputChar === "t") {
3479
+ setOriginalTheme(theme);
3480
+ setShowThemePicker(true);
3481
+ return;
3482
+ }
3137
3483
  });
3138
3484
  const updateAssistant = useCallback(
3139
3485
  (id, patch) => {
@@ -3204,7 +3550,7 @@ function App({ initialCfg }) {
3204
3550
  }
3205
3551
  }, [cfg, busy, saveSessionSafe]);
3206
3552
  const openResumePicker = useCallback(async () => {
3207
- const sessions = await listSessions(30);
3553
+ const sessions = await listSessions(200);
3208
3554
  setResumeSessions(sessions);
3209
3555
  }, []);
3210
3556
  const runInit = useCallback(async () => {
@@ -3372,6 +3718,27 @@ function App({ initialCfg }) {
3372
3718
  },
3373
3719
  []
3374
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
+ );
3375
3742
  const handleSlash = useCallback(
3376
3743
  (cmd) => {
3377
3744
  const raw = cmd.trim();
@@ -3391,6 +3758,7 @@ function App({ initialCfg }) {
3391
3758
  setTasksStartedAt(null);
3392
3759
  setTasksStartTokens(0);
3393
3760
  compactSuggestedRef.current = false;
3761
+ updateNudgedRef.current = false;
3394
3762
  return true;
3395
3763
  }
3396
3764
  if (c === "/reasoning") {
@@ -3457,14 +3825,8 @@ use: /thinking low | medium | high`
3457
3825
  }
3458
3826
  if (c === "/theme") {
3459
3827
  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
- ]);
3828
+ setOriginalTheme(theme);
3829
+ setShowThemePicker(true);
3468
3830
  return true;
3469
3831
  }
3470
3832
  const next = resolveTheme(arg);
@@ -3535,8 +3897,10 @@ use: /thinking low | medium | high`
3535
3897
  return true;
3536
3898
  }
3537
3899
  if (c === "/update") {
3538
- void checkForUpdate().then((result) => {
3900
+ void checkForUpdate(true).then((result) => {
3539
3901
  if (result.hasUpdate) {
3902
+ setHasUpdate(true);
3903
+ setLatestVersion(result.latestVersion);
3540
3904
  setEvents((e) => [
3541
3905
  ...e,
3542
3906
  {
@@ -3556,6 +3920,8 @@ use: /thinking low | medium | high`
3556
3920
  ]);
3557
3921
  });
3558
3922
  } else {
3923
+ setHasUpdate(false);
3924
+ setLatestVersion(null);
3559
3925
  setEvents((e) => [
3560
3926
  ...e,
3561
3927
  { kind: "info", key: mkKey(), text: "no update available" }
@@ -3580,7 +3946,7 @@ use: /thinking low | medium | high`
3580
3946
  {
3581
3947
  kind: "info",
3582
3948
  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"
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"
3584
3950
  }
3585
3951
  ]);
3586
3952
  return true;
@@ -3762,7 +4128,7 @@ use: /thinking low | medium | high`
3762
4128
  }
3763
4129
  }, [usage]);
3764
4130
  if (!cfg) {
3765
- return /* @__PURE__ */ jsx12(
4131
+ return /* @__PURE__ */ jsx13(
3766
4132
  Onboarding,
3767
4133
  {
3768
4134
  onDone: (newCfg) => {
@@ -3776,12 +4142,15 @@ use: /thinking low | medium | high`
3776
4142
  );
3777
4143
  }
3778
4144
  if (resumeSessions !== null) {
3779
- 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 }) });
3780
4149
  }
3781
4150
  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(
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(
3785
4154
  PermissionModal,
3786
4155
  {
3787
4156
  tool: perm.tool,
@@ -3792,8 +4161,8 @@ use: /thinking low | medium | high`
3792
4161
  setPerm(null);
3793
4162
  }
3794
4163
  }
3795
- ) : /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginTop: 1, children: [
3796
- tasks.length > 0 && /* @__PURE__ */ jsx12(
4164
+ ) : /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginTop: 1, children: [
4165
+ tasks.length > 0 && /* @__PURE__ */ jsx13(
3797
4166
  TaskList,
3798
4167
  {
3799
4168
  tasks,
@@ -3802,11 +4171,11 @@ use: /thinking low | medium | high`
3802
4171
  tokensDelta: Math.max(0, (usage?.prompt_tokens ?? 0) - tasksStartTokens)
3803
4172
  }
3804
4173
  ),
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: [
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: [
3806
4175
  "\u23F3 ",
3807
4176
  q.display
3808
4177
  ] }, `queue_${i}`)) }),
3809
- /* @__PURE__ */ jsx12(
4178
+ /* @__PURE__ */ jsx13(
3810
4179
  StatusBar,
3811
4180
  {
3812
4181
  model: cfg.model,
@@ -3816,12 +4185,14 @@ use: /thinking low | medium | high`
3816
4185
  theme,
3817
4186
  mode,
3818
4187
  effort,
3819
- contextLimit: CONTEXT_LIMIT
4188
+ contextLimit: CONTEXT_LIMIT,
4189
+ hasUpdate,
4190
+ latestVersion
3820
4191
  }
3821
4192
  ),
3822
- /* @__PURE__ */ jsxs11(Box11, { marginTop: 1, children: [
3823
- /* @__PURE__ */ jsx12(Text12, { color: theme.accent, children: "\u203A " }),
3824
- /* @__PURE__ */ jsx12(
4193
+ /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, children: [
4194
+ /* @__PURE__ */ jsx13(Text13, { color: theme.accent, children: "\u203A " }),
4195
+ /* @__PURE__ */ jsx13(
3825
4196
  CustomTextInput,
3826
4197
  {
3827
4198
  value: input,
@@ -3869,8 +4240,8 @@ use: /thinking low | medium | high`
3869
4240
  ] })
3870
4241
  ] });
3871
4242
  }
3872
- async function renderApp(cfg) {
3873
- 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 }));
3874
4245
  await instance.waitUntilExit();
3875
4246
  }
3876
4247
  var CONTEXT_LIMIT, AUTO_COMPACT_SUGGEST_PCT, nextAssistantId, nextKey, mkKey, EFFORT_DESCRIPTIONS;
@@ -3886,6 +4257,7 @@ var init_app = __esm({
3886
4257
  init_status();
3887
4258
  init_permission();
3888
4259
  init_resume_picker();
4260
+ init_theme_picker();
3889
4261
  init_task_list();
3890
4262
  init_text_input();
3891
4263
  init_update_check();
@@ -3913,6 +4285,7 @@ init_config();
3913
4285
  init_loop();
3914
4286
  init_system_prompt();
3915
4287
  init_executor();
4288
+ init_update_check();
3916
4289
  import { Command } from "commander";
3917
4290
  import { readFileSync as readFileSync2 } from "fs";
3918
4291
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -3931,6 +4304,7 @@ program.name("kimiflare").description("Terminal coding agent powered by Kimi-K2.
3931
4304
  var opts = program.opts();
3932
4305
  async function main() {
3933
4306
  const cfg = await loadConfig();
4307
+ const updateResult = await checkForUpdate();
3934
4308
  if (opts.print !== void 0) {
3935
4309
  if (!cfg) {
3936
4310
  console.error(
@@ -3944,7 +4318,8 @@ async function main() {
3944
4318
  model,
3945
4319
  prompt: opts.print,
3946
4320
  allowAll: !!opts.dangerouslyAllowAll,
3947
- showReasoning: !!opts.reasoning
4321
+ showReasoning: !!opts.reasoning,
4322
+ updateResult
3948
4323
  });
3949
4324
  return;
3950
4325
  }
@@ -3957,12 +4332,21 @@ async function main() {
3957
4332
  const { renderApp: renderApp2 } = await Promise.resolve().then(() => (init_app(), app_exports));
3958
4333
  if (cfg) {
3959
4334
  const model = opts.model ?? cfg.model ?? DEFAULT_MODEL;
3960
- await renderApp2({ ...cfg, model });
4335
+ await renderApp2({ ...cfg, model }, updateResult);
3961
4336
  } else {
3962
- await renderApp2(null);
4337
+ await renderApp2(null, updateResult);
3963
4338
  }
3964
4339
  }
3965
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
+ }
3966
4350
  const cwd = process.cwd();
3967
4351
  const executor = new ToolExecutor(ALL_TOOLS);
3968
4352
  const messages = [