kimiflare 0.2.2 → 0.3.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
@@ -80,10 +80,10 @@ async function* readSSE(stream, signal) {
80
80
  if (done) break;
81
81
  buffer += decoder.decode(value, { stream: true });
82
82
  buffer = buffer.replace(/\r\n/g, "\n");
83
- let sep;
84
- while ((sep = buffer.indexOf("\n\n")) !== -1) {
85
- const event = buffer.slice(0, sep);
86
- buffer = buffer.slice(sep + 2);
83
+ let sep2;
84
+ while ((sep2 = buffer.indexOf("\n\n")) !== -1) {
85
+ const event = buffer.slice(0, sep2);
86
+ buffer = buffer.slice(sep2 + 2);
87
87
  const data = extractData(event);
88
88
  if (data !== null) yield data;
89
89
  }
@@ -407,7 +407,21 @@ var init_mode = __esm({
407
407
 
408
408
  // src/agent/system-prompt.ts
409
409
  import { platform, release, homedir as homedir2 } from "os";
410
- import { basename } from "path";
410
+ import { basename, join as join2 } from "path";
411
+ import { readFileSync, statSync } from "fs";
412
+ function loadContextFile(cwd) {
413
+ for (const name of CONTEXT_FILENAMES) {
414
+ const path = join2(cwd, name);
415
+ try {
416
+ const s = statSync(path);
417
+ if (!s.isFile() || s.size > MAX_CONTEXT_BYTES) continue;
418
+ const content = readFileSync(path, "utf8");
419
+ return { name, path, content, lineCount: content.split("\n").length };
420
+ } catch {
421
+ }
422
+ }
423
+ return null;
424
+ }
411
425
  function buildSystemPrompt(opts2) {
412
426
  const now = opts2.now ?? /* @__PURE__ */ new Date();
413
427
  const date = now.toISOString().slice(0, 10);
@@ -416,7 +430,7 @@ function buildSystemPrompt(opts2) {
416
430
  const perm = t.needsPermission ? " [needs user permission]" : "";
417
431
  return `- \`${t.name}\`${perm}: ${t.description.split("\n")[0]}`;
418
432
  }).join("\n");
419
- return `You are kimiflare, an interactive coding assistant running in the user's terminal. You act on the user's local filesystem through the tools listed below. You are powered by the ${opts2.model} model on Cloudflare Workers AI.
433
+ const base = `You are kimiflare, an interactive coding assistant running in the user's terminal. You act on the user's local filesystem through the tools listed below. You are powered by the ${opts2.model} model on Cloudflare Workers AI.
420
434
 
421
435
  Environment:
422
436
  - Working directory: ${opts2.cwd}
@@ -437,17 +451,27 @@ How to work:
437
451
  - If a tool returns an error, read it carefully and adjust; do not retry the same call blindly.
438
452
  - You have a 262k-token context window. Read as much of a file as needed rather than guessing.
439
453
  - If a request is ambiguous, ask one focused question instead of making large assumptions.
440
- - When you finish a task, stop. Do not add a closing summary.${opts2.mode ? systemPromptForMode(opts2.mode) : ""}`;
454
+ - When you finish a task, stop. Do not add a closing summary.`;
455
+ const ctx = loadContextFile(opts2.cwd);
456
+ const contextBlock = ctx ? `
457
+
458
+ Project context from ${ctx.name} (${ctx.lineCount} lines, treat as authoritative):
459
+ ${ctx.content.trim()}` : "";
460
+ const modeBlock = opts2.mode ? systemPromptForMode(opts2.mode) : "";
461
+ return base + contextBlock + modeBlock;
441
462
  }
463
+ var CONTEXT_FILENAMES, MAX_CONTEXT_BYTES;
442
464
  var init_system_prompt = __esm({
443
465
  "src/agent/system-prompt.ts"() {
444
466
  "use strict";
445
467
  init_mode();
468
+ CONTEXT_FILENAMES = ["KIMI.md", "KIMIFLARE.md", "AGENT.md"];
469
+ MAX_CONTEXT_BYTES = 20 * 1024;
446
470
  }
447
471
  });
448
472
 
449
473
  // src/util/paths.ts
450
- import { resolve, isAbsolute } from "path";
474
+ import { resolve, isAbsolute, relative, sep } from "path";
451
475
  import { homedir as homedir3 } from "os";
452
476
  function resolvePath(cwd, input) {
453
477
  if (input.startsWith("~/") || input === "~") {
@@ -460,6 +484,26 @@ function truncate(s, n) {
460
484
  return s.slice(0, n) + `
461
485
  ... [truncated, ${s.length - n} chars omitted]`;
462
486
  }
487
+ function collapsePath(input, cwd, maxLen = 40) {
488
+ if (!input) return input;
489
+ let abs;
490
+ try {
491
+ abs = resolvePath(cwd, input);
492
+ } catch {
493
+ return input;
494
+ }
495
+ const rel = relative(cwd, abs);
496
+ if (rel && !rel.startsWith("..") && !isAbsolute(rel)) {
497
+ return rel === "" ? "." : rel;
498
+ }
499
+ if (input.length <= maxLen) return input;
500
+ const parts = input.split(sep).filter(Boolean);
501
+ if (parts.length <= 2) return input;
502
+ return `\u2026/${parts.slice(-2).join(sep)}`;
503
+ }
504
+ function collapsePathsInText(s, cwd, maxLen = 40) {
505
+ return s.replace(/([~/][^\s"',)}\]]+)/g, (match) => collapsePath(match, cwd, maxLen));
506
+ }
463
507
  var init_paths = __esm({
464
508
  "src/util/paths.ts"() {
465
509
  "use strict";
@@ -488,7 +532,7 @@ var init_read = __esm({
488
532
  additionalProperties: false
489
533
  },
490
534
  needsPermission: false,
491
- render: ({ path }) => ({ title: `read ${path}` }),
535
+ render: ({ path }) => ({ title: `read ${collapsePath(path, process.cwd())}` }),
492
536
  async run(args, ctx) {
493
537
  const abs = resolvePath(ctx.cwd, args.path);
494
538
  const st = await stat(abs);
@@ -526,7 +570,7 @@ var init_write = __esm({
526
570
  },
527
571
  needsPermission: true,
528
572
  render: (args) => ({
529
- title: `write ${args.path} (${args.content.length} chars)`,
573
+ title: `write ${collapsePath(args.path, process.cwd())} (${args.content.length} chars)`,
530
574
  diff: { path: args.path, before: "", after: args.content }
531
575
  }),
532
576
  async run(args, ctx) {
@@ -579,7 +623,7 @@ var init_edit = __esm({
579
623
  },
580
624
  needsPermission: true,
581
625
  render: (args) => ({
582
- title: `edit ${args.path}${args.replace_all ? " (replace_all)" : ""}`,
626
+ title: `edit ${collapsePath(args.path, process.cwd())}${args.replace_all ? " (replace_all)" : ""}`,
583
627
  diff: { path: args.path, before: args.old_string, after: args.new_string }
584
628
  }),
585
629
  async run(args, ctx) {
@@ -602,6 +646,49 @@ var init_edit = __esm({
602
646
 
603
647
  // src/tools/bash.ts
604
648
  import { spawn } from "child_process";
649
+ function formatBashTitle(raw) {
650
+ let cmd = (raw ?? "").trim();
651
+ const m = cmd.match(/^cd\s+([^\s&;]+)\s*(?:&&|;)\s*(.*)$/);
652
+ if (m) cmd = m[2].trim();
653
+ return `$ ${cmd}`.slice(0, 120);
654
+ }
655
+ function runBash(args, ctx) {
656
+ const timeout = Math.min(Math.max(1e3, args.timeout_ms ?? DEFAULT_TIMEOUT), MAX_TIMEOUT);
657
+ return new Promise((resolve2, reject) => {
658
+ const child = spawn("bash", ["-lc", args.command], {
659
+ cwd: ctx.cwd,
660
+ signal: ctx.signal
661
+ });
662
+ let stdout = "";
663
+ let stderr = "";
664
+ let killedByTimeout = false;
665
+ const timer = setTimeout(() => {
666
+ killedByTimeout = true;
667
+ child.kill("SIGKILL");
668
+ }, timeout);
669
+ child.stdout.on("data", (d) => {
670
+ stdout += d.toString("utf8");
671
+ });
672
+ child.stderr.on("data", (d) => {
673
+ stderr += d.toString("utf8");
674
+ });
675
+ child.on("error", (e) => {
676
+ clearTimeout(timer);
677
+ reject(e);
678
+ });
679
+ child.on("close", (code, signal) => {
680
+ clearTimeout(timer);
681
+ const header = killedByTimeout ? `(timed out after ${timeout}ms)` : `exit=${code ?? "?"}${signal ? ` signal=${signal}` : ""}`;
682
+ const parts = [header];
683
+ if (stdout) parts.push(`--- stdout ---
684
+ ${stdout.trimEnd()}`);
685
+ if (stderr) parts.push(`--- stderr ---
686
+ ${stderr.trimEnd()}`);
687
+ if (!stdout && !stderr) parts.push("(no output)");
688
+ resolve2(truncate(parts.join("\n"), OUTPUT_CAP));
689
+ });
690
+ });
691
+ }
605
692
  var DEFAULT_TIMEOUT, MAX_TIMEOUT, OUTPUT_CAP, bashTool;
606
693
  var init_bash = __esm({
607
694
  "src/tools/bash.ts"() {
@@ -628,44 +715,8 @@ var init_bash = __esm({
628
715
  additionalProperties: false
629
716
  },
630
717
  needsPermission: true,
631
- render: (args) => ({ title: `bash: ${args.command}`.slice(0, 120) }),
632
- run(args, ctx) {
633
- const timeout = Math.min(Math.max(1e3, args.timeout_ms ?? DEFAULT_TIMEOUT), MAX_TIMEOUT);
634
- return new Promise((resolve2, reject) => {
635
- const child = spawn("bash", ["-lc", args.command], {
636
- cwd: ctx.cwd,
637
- signal: ctx.signal
638
- });
639
- let stdout = "";
640
- let stderr = "";
641
- let killedByTimeout = false;
642
- const timer = setTimeout(() => {
643
- killedByTimeout = true;
644
- child.kill("SIGKILL");
645
- }, timeout);
646
- child.stdout.on("data", (d) => {
647
- stdout += d.toString("utf8");
648
- });
649
- child.stderr.on("data", (d) => {
650
- stderr += d.toString("utf8");
651
- });
652
- child.on("error", (e) => {
653
- clearTimeout(timer);
654
- reject(e);
655
- });
656
- child.on("close", (code, signal) => {
657
- clearTimeout(timer);
658
- const header = killedByTimeout ? `(timed out after ${timeout}ms)` : `exit=${code ?? "?"}${signal ? ` signal=${signal}` : ""}`;
659
- const parts = [header];
660
- if (stdout) parts.push(`--- stdout ---
661
- ${stdout.trimEnd()}`);
662
- if (stderr) parts.push(`--- stderr ---
663
- ${stderr.trimEnd()}`);
664
- if (!stdout && !stderr) parts.push("(no output)");
665
- resolve2(truncate(parts.join("\n"), OUTPUT_CAP));
666
- });
667
- });
668
- }
718
+ render: (args) => ({ title: formatBashTitle(args.command) }),
719
+ run: (args, ctx) => runBash(args, ctx)
669
720
  };
670
721
  }
671
722
  });
@@ -690,7 +741,7 @@ var init_glob = __esm({
690
741
  additionalProperties: false
691
742
  },
692
743
  needsPermission: false,
693
- render: (args) => ({ title: `glob ${args.pattern}${args.path ? ` in ${args.path}` : ""}` }),
744
+ render: (args) => ({ title: `glob ${args.pattern}${args.path ? ` in ${collapsePath(args.path, process.cwd())}` : ""}` }),
694
745
  async run(args, ctx) {
695
746
  const root = args.path ? resolvePath(ctx.cwd, args.path) : ctx.cwd;
696
747
  const entries = await fg(args.pattern, {
@@ -1124,27 +1175,40 @@ import { createTwoFilesPatch } from "diff";
1124
1175
  import { jsx, jsxs } from "react/jsx-runtime";
1125
1176
  function DiffView({ path, before, after, maxLines = 40 }) {
1126
1177
  const patch = createTwoFilesPatch(path, path, before, after, "", "", { context: 2 });
1127
- const lines = patch.split("\n").slice(4);
1128
- const truncated = lines.length > maxLines ? lines.slice(0, maxLines) : lines;
1178
+ const raw = patch.split("\n").slice(4);
1179
+ const lines = raw.filter((l) => {
1180
+ if (l.startsWith("--- ") || l.startsWith("+++ ")) return false;
1181
+ if (l.startsWith("\")) return false;
1182
+ return true;
1183
+ });
1184
+ const diffStats = countChanges(lines);
1185
+ const hideHeader = diffStats.changed <= 3 && diffStats.context <= 3 && diffStats.hunks <= 1;
1186
+ const filtered = hideHeader ? lines.filter((l) => !l.startsWith("@@")) : lines;
1187
+ const truncated = filtered.length > maxLines ? filtered.slice(0, maxLines) : filtered;
1129
1188
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
1130
1189
  truncated.map((line, i) => /* @__PURE__ */ jsx(DiffLine, { line }, i)),
1131
- lines.length > maxLines && /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
1190
+ filtered.length > maxLines && /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
1132
1191
  "... (",
1133
- lines.length - maxLines,
1192
+ filtered.length - maxLines,
1134
1193
  " more lines)"
1135
1194
  ] })
1136
1195
  ] });
1137
1196
  }
1138
- function DiffLine({ line }) {
1139
- if (line.startsWith("+") && !line.startsWith("+++")) {
1140
- return /* @__PURE__ */ jsx(Text, { color: "green", children: line });
1141
- }
1142
- if (line.startsWith("-") && !line.startsWith("---")) {
1143
- return /* @__PURE__ */ jsx(Text, { color: "red", children: line });
1144
- }
1145
- if (line.startsWith("@@")) {
1146
- return /* @__PURE__ */ jsx(Text, { color: "cyan", children: line });
1197
+ function countChanges(lines) {
1198
+ let changed = 0;
1199
+ let context = 0;
1200
+ let hunks = 0;
1201
+ for (const l of lines) {
1202
+ if (l.startsWith("@@")) hunks++;
1203
+ else if (l.startsWith("+") || l.startsWith("-")) changed++;
1204
+ else if (l.trim().length > 0) context++;
1147
1205
  }
1206
+ return { changed, context, hunks };
1207
+ }
1208
+ function DiffLine({ line }) {
1209
+ if (line.startsWith("+")) return /* @__PURE__ */ jsx(Text, { color: "green", children: line });
1210
+ if (line.startsWith("-")) return /* @__PURE__ */ jsx(Text, { color: "red", children: line });
1211
+ if (line.startsWith("@@")) return /* @__PURE__ */ jsx(Text, { color: "cyan", children: line });
1148
1212
  return /* @__PURE__ */ jsx(Text, { color: "gray", children: line });
1149
1213
  }
1150
1214
  var init_diff_view = __esm({
@@ -1185,7 +1249,8 @@ function ToolView({ evt, verbose }) {
1185
1249
  ] });
1186
1250
  }
1187
1251
  function compactArgs(raw) {
1188
- const s = raw.replace(/\s+/g, " ");
1252
+ const collapsed = collapsePathsInText(raw, process.cwd());
1253
+ const s = collapsed.replace(/\s+/g, " ");
1189
1254
  return s.length <= 80 ? s : s.slice(0, 80) + "\u2026";
1190
1255
  }
1191
1256
  function firstLine(s) {
@@ -1196,38 +1261,198 @@ var init_tool_view = __esm({
1196
1261
  "src/ui/tool-view.tsx"() {
1197
1262
  "use strict";
1198
1263
  init_diff_view();
1264
+ init_paths();
1199
1265
  }
1200
1266
  });
1201
1267
 
1202
- // src/ui/chat.tsx
1268
+ // src/ui/markdown.tsx
1203
1269
  import { Box as Box3, Text as Text3 } from "ink";
1204
1270
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1271
+ function MD({ text, theme }) {
1272
+ const blocks = parseBlocks(text);
1273
+ return /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", children: blocks.map((b, i) => /* @__PURE__ */ jsx3(Block, { block: b, theme }, i)) });
1274
+ }
1275
+ function parseBlocks(src) {
1276
+ const out = [];
1277
+ const lines = src.split("\n");
1278
+ let i = 0;
1279
+ while (i < lines.length) {
1280
+ const line = lines[i];
1281
+ if (line.trim() === "") {
1282
+ out.push({ kind: "blank" });
1283
+ i++;
1284
+ continue;
1285
+ }
1286
+ const fence = line.match(/^```(\w*)\s*$/);
1287
+ if (fence) {
1288
+ const lang = fence[1] || void 0;
1289
+ const start = i + 1;
1290
+ let end = start;
1291
+ while (end < lines.length && !/^```\s*$/.test(lines[end])) end++;
1292
+ out.push({ kind: "code", lang, text: lines.slice(start, end).join("\n") });
1293
+ i = end + 1;
1294
+ continue;
1295
+ }
1296
+ const heading = line.match(/^(#{1,3})\s+(.*)$/);
1297
+ if (heading) {
1298
+ out.push({ kind: "heading", level: heading[1].length, text: heading[2] });
1299
+ i++;
1300
+ continue;
1301
+ }
1302
+ if (/^\s*>\s?/.test(line)) {
1303
+ const quoteLines = [];
1304
+ while (i < lines.length && /^\s*>\s?/.test(lines[i])) {
1305
+ quoteLines.push(lines[i].replace(/^\s*>\s?/, ""));
1306
+ i++;
1307
+ }
1308
+ out.push({ kind: "quote", text: quoteLines.join("\n") });
1309
+ continue;
1310
+ }
1311
+ if (/^\s*[-*]\s+/.test(line)) {
1312
+ const items = [];
1313
+ while (i < lines.length && /^\s*[-*]\s+/.test(lines[i])) {
1314
+ items.push(lines[i].replace(/^\s*[-*]\s+/, ""));
1315
+ i++;
1316
+ }
1317
+ out.push({ kind: "bullet", items });
1318
+ continue;
1319
+ }
1320
+ const paraLines = [line];
1321
+ i++;
1322
+ while (i < lines.length && lines[i].trim() !== "" && !/^(#{1,3})\s+/.test(lines[i]) && !/^\s*[-*]\s+/.test(lines[i]) && !/^\s*>\s?/.test(lines[i]) && !/^```/.test(lines[i])) {
1323
+ paraLines.push(lines[i]);
1324
+ i++;
1325
+ }
1326
+ out.push({ kind: "paragraph", text: paraLines.join("\n") });
1327
+ }
1328
+ return out;
1329
+ }
1330
+ function Block({ block, theme }) {
1331
+ if (block.kind === "blank") return /* @__PURE__ */ jsx3(Text3, { children: " " });
1332
+ if (block.kind === "heading") {
1333
+ return /* @__PURE__ */ jsx3(Box3, { marginTop: block.level === 1 ? 1 : 0, children: /* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.accent, children: renderInline(block.text, theme) }) });
1334
+ }
1335
+ if (block.kind === "bullet") {
1336
+ return /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", children: block.items.map((item, i) => /* @__PURE__ */ jsxs3(Box3, { children: [
1337
+ /* @__PURE__ */ jsx3(Text3, { color: theme.accent, children: " \u2022 " }),
1338
+ /* @__PURE__ */ jsx3(Text3, { children: renderInline(item, theme) })
1339
+ ] }, i)) });
1340
+ }
1341
+ if (block.kind === "quote") {
1342
+ return /* @__PURE__ */ jsx3(Box3, { marginLeft: 2, children: /* @__PURE__ */ jsx3(Text3, { color: theme.info.color, dimColor: theme.info.dim, italic: true, children: renderInline(block.text, theme) }) });
1343
+ }
1344
+ if (block.kind === "code") {
1345
+ return /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", marginLeft: 2, children: block.text.split("\n").map((l, i) => /* @__PURE__ */ jsx3(Text3, { color: theme.tool, children: l }, i)) });
1346
+ }
1347
+ return /* @__PURE__ */ jsx3(Text3, { children: renderInline(block.text, theme) });
1348
+ }
1349
+ function renderInline(src, theme) {
1350
+ const segments = parseInline(src);
1351
+ return segments.map((seg, i) => {
1352
+ if (seg.kind === "bold") return /* @__PURE__ */ jsx3(Text3, { bold: true, children: seg.text }, i);
1353
+ if (seg.kind === "italic") return /* @__PURE__ */ jsx3(Text3, { italic: true, children: seg.text }, i);
1354
+ if (seg.kind === "code") return /* @__PURE__ */ jsx3(Text3, { color: theme.tool, children: seg.text }, i);
1355
+ return /* @__PURE__ */ jsx3(Text3, { children: seg.text }, i);
1356
+ });
1357
+ }
1358
+ function parseInline(src) {
1359
+ const out = [];
1360
+ let i = 0;
1361
+ let buf = "";
1362
+ const flush = () => {
1363
+ if (buf) {
1364
+ out.push({ kind: "plain", text: buf });
1365
+ buf = "";
1366
+ }
1367
+ };
1368
+ while (i < src.length) {
1369
+ const ch = src[i];
1370
+ if (ch === "`") {
1371
+ const end = src.indexOf("`", i + 1);
1372
+ if (end > i) {
1373
+ flush();
1374
+ out.push({ kind: "code", text: src.slice(i + 1, end) });
1375
+ i = end + 1;
1376
+ continue;
1377
+ }
1378
+ }
1379
+ if (ch === "*" && src[i + 1] === "*") {
1380
+ const end = src.indexOf("**", i + 2);
1381
+ if (end > i + 1) {
1382
+ flush();
1383
+ out.push({ kind: "bold", text: src.slice(i + 2, end) });
1384
+ i = end + 2;
1385
+ continue;
1386
+ }
1387
+ }
1388
+ if (ch === "_" && src[i + 1] === "_") {
1389
+ const end = src.indexOf("__", i + 2);
1390
+ if (end > i + 1) {
1391
+ flush();
1392
+ out.push({ kind: "bold", text: src.slice(i + 2, end) });
1393
+ i = end + 2;
1394
+ continue;
1395
+ }
1396
+ }
1397
+ if (ch === "*" && src[i + 1] !== "*") {
1398
+ const end = src.indexOf("*", i + 1);
1399
+ if (end > i) {
1400
+ flush();
1401
+ out.push({ kind: "italic", text: src.slice(i + 1, end) });
1402
+ i = end + 1;
1403
+ continue;
1404
+ }
1405
+ }
1406
+ if (ch === "_" && !/\w/.test(src[i - 1] ?? "") && src[i + 1] !== "_") {
1407
+ const end = src.indexOf("_", i + 1);
1408
+ if (end > i && !/\w/.test(src[end + 1] ?? "")) {
1409
+ flush();
1410
+ out.push({ kind: "italic", text: src.slice(i + 1, end) });
1411
+ i = end + 1;
1412
+ continue;
1413
+ }
1414
+ }
1415
+ buf += ch;
1416
+ i++;
1417
+ }
1418
+ flush();
1419
+ return out;
1420
+ }
1421
+ var init_markdown = __esm({
1422
+ "src/ui/markdown.tsx"() {
1423
+ "use strict";
1424
+ }
1425
+ });
1426
+
1427
+ // src/ui/chat.tsx
1428
+ import { Box as Box4, Text as Text4 } from "ink";
1429
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1205
1430
  function ChatView({ events, showReasoning, theme, verbose }) {
1206
- return /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", children: events.map((e) => /* @__PURE__ */ jsx3(EventView, { evt: e, showReasoning, theme, verbose }, e.key)) });
1431
+ return /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", children: events.map((e) => /* @__PURE__ */ jsx4(EventView, { evt: e, showReasoning, theme, verbose }, e.key)) });
1207
1432
  }
1208
1433
  function EventView({ evt, showReasoning, theme, verbose }) {
1209
1434
  if (evt.kind === "user") {
1210
- return /* @__PURE__ */ jsxs3(Box3, { marginY: 0, children: [
1211
- /* @__PURE__ */ jsx3(Text3, { color: theme.user, children: "\u203A " }),
1212
- /* @__PURE__ */ jsx3(Text3, { children: evt.text })
1435
+ return /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
1436
+ /* @__PURE__ */ jsx4(Text4, { color: theme.user, children: "\u203A " }),
1437
+ /* @__PURE__ */ jsx4(Text4, { children: evt.text })
1213
1438
  ] });
1214
1439
  }
1215
1440
  if (evt.kind === "assistant") {
1216
- return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginY: 0, children: [
1217
- showReasoning && evt.reasoning ? /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", marginLeft: 2, children: /* @__PURE__ */ jsxs3(Text3, { color: theme.reasoning.color, dimColor: theme.reasoning.dim, children: [
1441
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginTop: 1, paddingLeft: 1, children: [
1442
+ showReasoning && evt.reasoning ? /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", marginLeft: 2, children: /* @__PURE__ */ jsxs4(Text4, { color: theme.reasoning.color, dimColor: theme.reasoning.dim, children: [
1218
1443
  "\u2727 thinking: ",
1219
1444
  evt.reasoning.length > 400 ? evt.reasoning.slice(0, 400) + "\u2026" : evt.reasoning
1220
1445
  ] }) }) : null,
1221
- evt.text ? theme.assistant ? /* @__PURE__ */ jsx3(Text3, { color: theme.assistant, children: evt.text }) : /* @__PURE__ */ jsx3(Text3, { children: evt.text }) : null
1446
+ evt.text ? /* @__PURE__ */ jsx4(MD, { text: evt.text, theme }) : null
1222
1447
  ] });
1223
1448
  }
1224
1449
  if (evt.kind === "tool") {
1225
- return /* @__PURE__ */ jsx3(ToolView, { evt, verbose });
1450
+ return /* @__PURE__ */ jsx4(ToolView, { evt, verbose });
1226
1451
  }
1227
1452
  if (evt.kind === "info") {
1228
- return /* @__PURE__ */ jsx3(Text3, { color: theme.info.color, dimColor: theme.info.dim, children: evt.text });
1453
+ return /* @__PURE__ */ jsx4(Text4, { color: theme.info.color, dimColor: theme.info.dim, children: evt.text });
1229
1454
  }
1230
- return /* @__PURE__ */ jsxs3(Text3, { color: theme.error, children: [
1455
+ return /* @__PURE__ */ jsxs4(Text4, { color: theme.error, children: [
1231
1456
  "! ",
1232
1457
  evt.text
1233
1458
  ] });
@@ -1236,12 +1461,13 @@ var init_chat = __esm({
1236
1461
  "src/ui/chat.tsx"() {
1237
1462
  "use strict";
1238
1463
  init_tool_view();
1464
+ init_markdown();
1239
1465
  }
1240
1466
  });
1241
1467
 
1242
1468
  // src/ui/status.tsx
1243
- import { Box as Box4, Text as Text4 } from "ink";
1244
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1469
+ import { Box as Box5, Text as Text5 } from "ink";
1470
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1245
1471
  function StatusBar({ model, usage, thinking, hint, theme, mode, effort, contextLimit }) {
1246
1472
  const parts = [`model: ${shortModel(model)}`, `effort: ${effort}`];
1247
1473
  if (usage) {
@@ -1260,15 +1486,15 @@ function StatusBar({ model, usage, thinking, hint, theme, mode, effort, contextL
1260
1486
  if (hint) parts.push(hint);
1261
1487
  const modeColor = mode === "plan" ? theme.modeBadge.plan : mode === "auto" ? theme.modeBadge.auto : theme.modeBadge.edit;
1262
1488
  const warn = usage && usage.prompt_tokens / contextLimit >= 0.8;
1263
- return /* @__PURE__ */ jsxs4(Box4, { children: [
1264
- /* @__PURE__ */ jsxs4(Text4, { color: modeColor, bold: true, children: [
1489
+ return /* @__PURE__ */ jsxs5(Box5, { children: [
1490
+ /* @__PURE__ */ jsxs5(Text5, { color: modeColor, bold: true, children: [
1265
1491
  "[",
1266
1492
  mode,
1267
1493
  "]"
1268
1494
  ] }),
1269
- /* @__PURE__ */ jsx4(Text4, { children: " " }),
1270
- /* @__PURE__ */ jsx4(Text4, { color: theme.info.color, dimColor: theme.info.dim, children: parts.join(" \xB7 ") }),
1271
- warn ? /* @__PURE__ */ jsxs4(Text4, { color: theme.warn, bold: true, children: [
1495
+ /* @__PURE__ */ jsx5(Text5, { children: " " }),
1496
+ /* @__PURE__ */ jsx5(Text5, { color: theme.info.color, dimColor: theme.info.dim, children: parts.join(" \xB7 ") }),
1497
+ warn ? /* @__PURE__ */ jsxs5(Text5, { color: theme.warn, bold: true, children: [
1272
1498
  " \xB7 ",
1273
1499
  "/compact recommended"
1274
1500
  ] }) : null
@@ -1289,9 +1515,9 @@ var init_status = __esm({
1289
1515
  });
1290
1516
 
1291
1517
  // src/ui/permission.tsx
1292
- import { Box as Box5, Text as Text5 } from "ink";
1518
+ import { Box as Box6, Text as Text6 } from "ink";
1293
1519
  import SelectInput from "ink-select-input";
1294
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1520
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1295
1521
  function PermissionModal({ tool, args, onDecide, theme }) {
1296
1522
  const render2 = tool.render?.(args);
1297
1523
  const items = [
@@ -1299,21 +1525,21 @@ function PermissionModal({ tool, args, onDecide, theme }) {
1299
1525
  { label: "Allow for this session", value: "allow_session" },
1300
1526
  { label: "Deny", value: "deny" }
1301
1527
  ];
1302
- return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", borderStyle: "round", borderColor: theme.permission, paddingX: 1, children: [
1303
- /* @__PURE__ */ jsx5(Text5, { color: theme.permission, bold: true, children: "Permission requested" }),
1304
- /* @__PURE__ */ jsxs5(Text5, { children: [
1528
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", borderStyle: "round", borderColor: theme.permission, paddingX: 1, children: [
1529
+ /* @__PURE__ */ jsx6(Text6, { color: theme.permission, bold: true, children: "Permission requested" }),
1530
+ /* @__PURE__ */ jsxs6(Text6, { children: [
1305
1531
  "tool: ",
1306
- /* @__PURE__ */ jsx5(Text5, { color: theme.tool, children: tool.name })
1532
+ /* @__PURE__ */ jsx6(Text6, { color: theme.tool, children: tool.name })
1307
1533
  ] }),
1308
- render2?.title ? /* @__PURE__ */ jsxs5(Text5, { children: [
1534
+ render2?.title ? /* @__PURE__ */ jsxs6(Text6, { children: [
1309
1535
  "action: ",
1310
1536
  render2.title
1311
1537
  ] }) : null,
1312
- render2?.diff ? /* @__PURE__ */ jsx5(Box5, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx5(DiffView, { ...render2.diff }) }) : /* @__PURE__ */ jsxs5(Text5, { color: theme.info.color, dimColor: theme.info.dim, children: [
1538
+ render2?.diff ? /* @__PURE__ */ jsx6(Box6, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx6(DiffView, { ...render2.diff }) }) : /* @__PURE__ */ jsxs6(Text6, { color: theme.info.color, dimColor: theme.info.dim, children: [
1313
1539
  "args: ",
1314
1540
  JSON.stringify(args)
1315
1541
  ] }),
1316
- /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(SelectInput, { items, onSelect: (item) => onDecide(item.value) }) })
1542
+ /* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(SelectInput, { items, onSelect: (item) => onDecide(item.value) }) })
1317
1543
  ] });
1318
1544
  }
1319
1545
  var init_permission = __esm({
@@ -1324,15 +1550,15 @@ var init_permission = __esm({
1324
1550
  });
1325
1551
 
1326
1552
  // src/ui/resume-picker.tsx
1327
- import { Box as Box6, Text as Text6 } from "ink";
1553
+ import { Box as Box7, Text as Text7 } from "ink";
1328
1554
  import SelectInput2 from "ink-select-input";
1329
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1555
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1330
1556
  function ResumePicker({ sessions, onPick, theme }) {
1331
1557
  if (sessions.length === 0) {
1332
- return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
1333
- /* @__PURE__ */ jsx6(Text6, { color: theme.accent, bold: true, children: "Resume a session" }),
1334
- /* @__PURE__ */ jsx6(Text6, { color: theme.info.color, dimColor: theme.info.dim, children: "No saved sessions yet. Press Enter to dismiss." }),
1335
- /* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(
1558
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
1559
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accent, bold: true, children: "Resume a session" }),
1560
+ /* @__PURE__ */ jsx7(Text7, { color: theme.info.color, dimColor: theme.info.dim, children: "No saved sessions yet. Press Enter to dismiss." }),
1561
+ /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(
1336
1562
  SelectInput2,
1337
1563
  {
1338
1564
  items: [{ label: "(back)", value: "__cancel__" }],
@@ -1346,10 +1572,10 @@ function ResumePicker({ sessions, onPick, theme }) {
1346
1572
  value: s.id
1347
1573
  }));
1348
1574
  items.push({ label: "(cancel)", value: "__cancel__" });
1349
- return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
1350
- /* @__PURE__ */ jsx6(Text6, { color: theme.accent, bold: true, children: "Resume a session" }),
1351
- /* @__PURE__ */ jsx6(Text6, { color: theme.info.color, dimColor: theme.info.dim, children: "Arrow keys to select, Enter to confirm." }),
1352
- /* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(
1575
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
1576
+ /* @__PURE__ */ jsx7(Text7, { color: theme.accent, bold: true, children: "Resume a session" }),
1577
+ /* @__PURE__ */ jsx7(Text7, { color: theme.info.color, dimColor: theme.info.dim, children: "Arrow keys to select, Enter to confirm." }),
1578
+ /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(
1353
1579
  SelectInput2,
1354
1580
  {
1355
1581
  items,
@@ -1383,8 +1609,8 @@ var init_resume_picker = __esm({
1383
1609
 
1384
1610
  // src/ui/task-list.tsx
1385
1611
  import { useEffect, useState } from "react";
1386
- import { Box as Box7, Text as Text7 } from "ink";
1387
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1612
+ import { Box as Box8, Text as Text8 } from "ink";
1613
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1388
1614
  function TaskList({ tasks, theme, startedAt, tokensDelta }) {
1389
1615
  const [now, setNow] = useState(Date.now());
1390
1616
  useEffect(() => {
@@ -1404,22 +1630,22 @@ function TaskList({ tasks, theme, startedAt, tokensDelta }) {
1404
1630
  const headerStats = [elapsed, tokensDelta > 0 ? `\u2191 ${formatTokens(tokensDelta)} tokens` : null].filter(Boolean).join(" \xB7 ");
1405
1631
  const visibleTasks = tasks.slice(0, MAX_VISIBLE);
1406
1632
  const hiddenPending = Math.max(0, tasks.length - visibleTasks.length);
1407
- return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", marginBottom: 1, children: [
1408
- /* @__PURE__ */ jsxs7(Box7, { children: [
1409
- /* @__PURE__ */ jsxs7(Text7, { color: allDone ? "green" : theme.accent, bold: true, children: [
1633
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginBottom: 1, children: [
1634
+ /* @__PURE__ */ jsxs8(Box8, { children: [
1635
+ /* @__PURE__ */ jsxs8(Text8, { color: allDone ? "green" : theme.accent, bold: true, children: [
1410
1636
  allDone ? "\u2713" : "\u25B8",
1411
1637
  " ",
1412
1638
  header
1413
1639
  ] }),
1414
- headerStats && /* @__PURE__ */ jsxs7(Text7, { color: theme.info.color, dimColor: theme.info.dim, children: [
1640
+ headerStats && /* @__PURE__ */ jsxs8(Text8, { color: theme.info.color, dimColor: theme.info.dim, children: [
1415
1641
  " ",
1416
1642
  "(",
1417
1643
  headerStats,
1418
1644
  ")"
1419
1645
  ] })
1420
1646
  ] }),
1421
- visibleTasks.map((t) => /* @__PURE__ */ jsx7(TaskRow, { task: t, theme }, t.id)),
1422
- hiddenPending > 0 && /* @__PURE__ */ jsxs7(Text7, { color: theme.info.color, dimColor: theme.info.dim, children: [
1647
+ visibleTasks.map((t) => /* @__PURE__ */ jsx8(TaskRow, { task: t, theme }, t.id)),
1648
+ hiddenPending > 0 && /* @__PURE__ */ jsxs8(Text8, { color: theme.info.color, dimColor: theme.info.dim, children: [
1423
1649
  " ",
1424
1650
  "\u2026 +",
1425
1651
  hiddenPending,
@@ -1429,20 +1655,20 @@ function TaskList({ tasks, theme, startedAt, tokensDelta }) {
1429
1655
  }
1430
1656
  function TaskRow({ task, theme }) {
1431
1657
  if (task.status === "completed") {
1432
- return /* @__PURE__ */ jsxs7(Text7, { color: theme.info.color, dimColor: theme.info.dim, children: [
1658
+ return /* @__PURE__ */ jsxs8(Text8, { color: theme.info.color, dimColor: theme.info.dim, children: [
1433
1659
  " ",
1434
1660
  "\u2713 ",
1435
- /* @__PURE__ */ jsx7(Text7, { strikethrough: true, children: task.title })
1661
+ /* @__PURE__ */ jsx8(Text8, { strikethrough: true, children: task.title })
1436
1662
  ] });
1437
1663
  }
1438
1664
  if (task.status === "in_progress") {
1439
- return /* @__PURE__ */ jsxs7(Text7, { color: theme.accent, bold: true, children: [
1665
+ return /* @__PURE__ */ jsxs8(Text8, { color: theme.accent, bold: true, children: [
1440
1666
  " ",
1441
1667
  "\u25A0 ",
1442
1668
  task.title
1443
1669
  ] });
1444
1670
  }
1445
- return /* @__PURE__ */ jsxs7(Text7, { color: theme.info.color, dimColor: theme.info.dim, children: [
1671
+ return /* @__PURE__ */ jsxs8(Text8, { color: theme.info.color, dimColor: theme.info.dim, children: [
1446
1672
  " ",
1447
1673
  "\u2610 ",
1448
1674
  task.title
@@ -1989,8 +2215,8 @@ var init_source = __esm({
1989
2215
 
1990
2216
  // src/ui/text-input.tsx
1991
2217
  import { useState as useState2, useEffect as useEffect2, useRef } from "react";
1992
- import { Text as Text8, useInput } from "ink";
1993
- import { jsx as jsx8 } from "react/jsx-runtime";
2218
+ import { Text as Text9, useInput } from "ink";
2219
+ import { jsx as jsx9 } from "react/jsx-runtime";
1994
2220
  function shouldTreatAsPaste(input) {
1995
2221
  if (input.length >= PASTE_CHAR_THRESHOLD) return true;
1996
2222
  const newlines = (input.match(/\n/g) ?? []).length;
@@ -2167,7 +2393,7 @@ function CustomTextInput({
2167
2393
  } else if (cursorOffset === displayValue.length) {
2168
2394
  renderedValue += source_default.inverse(" ");
2169
2395
  }
2170
- return /* @__PURE__ */ jsx8(Text8, { children: renderedValue });
2396
+ return /* @__PURE__ */ jsx9(Text9, { children: renderedValue });
2171
2397
  }
2172
2398
  function findPasteTokenEndingAt(value, pos, pastes) {
2173
2399
  if (pos <= 0 || value[pos - 1] !== "]") return -1;
@@ -2191,15 +2417,15 @@ var init_text_input = __esm({
2191
2417
  // src/util/update-check.ts
2192
2418
  import { readFile as readFile6, writeFile as writeFile4, mkdir as mkdir3, access } from "fs/promises";
2193
2419
  import { homedir as homedir4 } from "os";
2194
- import { join as join2, dirname as dirname2 } from "path";
2420
+ import { join as join3, dirname as dirname2 } from "path";
2195
2421
  import { fileURLToPath } from "url";
2196
2422
  function cachePath() {
2197
- const xdg = process.env.XDG_CONFIG_HOME || join2(homedir4(), ".config");
2198
- return join2(xdg, "kimiflare", "update-check.json");
2423
+ const xdg = process.env.XDG_CONFIG_HOME || join3(homedir4(), ".config");
2424
+ return join3(xdg, "kimiflare", "update-check.json");
2199
2425
  }
2200
2426
  function localPackageJsonPath() {
2201
2427
  const here = dirname2(fileURLToPath(import.meta.url));
2202
- return join2(here, "..", "..", "package.json");
2428
+ return join3(here, "..", "..", "package.json");
2203
2429
  }
2204
2430
  async function readLocalVersion() {
2205
2431
  try {
@@ -2270,7 +2496,7 @@ async function checkForUpdate() {
2270
2496
  async function isGitRepo() {
2271
2497
  try {
2272
2498
  const here = dirname2(fileURLToPath(import.meta.url));
2273
- await access(join2(here, "..", "..", ".git"));
2499
+ await access(join3(here, "..", "..", ".git"));
2274
2500
  return true;
2275
2501
  } catch {
2276
2502
  return false;
@@ -2287,8 +2513,8 @@ var init_update_check = __esm({
2287
2513
 
2288
2514
  // src/ui/onboarding.tsx
2289
2515
  import { useState as useState3 } from "react";
2290
- import { Box as Box8, Text as Text9 } from "ink";
2291
- import { Fragment, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
2516
+ import { Box as Box9, Text as Text10 } from "ink";
2517
+ import { Fragment, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
2292
2518
  function Onboarding({ onDone }) {
2293
2519
  const [step, setStep] = useState3("accountId");
2294
2520
  const [accountId, setAccountId] = useState3("");
@@ -2322,15 +2548,15 @@ function Onboarding({ onDone }) {
2322
2548
  setSavedPath(`error: ${e.message}`);
2323
2549
  }
2324
2550
  };
2325
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
2326
- /* @__PURE__ */ jsx9(Text9, { bold: true, color: "cyan", children: "Welcome to kimiflare!" }),
2327
- /* @__PURE__ */ jsx9(Text9, { color: "gray", dimColor: true, children: "Terminal coding agent powered by Kimi-K2.6 on Cloudflare Workers AI." }),
2328
- /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
2329
- step === "accountId" && /* @__PURE__ */ jsxs8(Fragment, { children: [
2330
- /* @__PURE__ */ jsx9(Text9, { children: "Enter your Cloudflare Account ID:" }),
2331
- /* @__PURE__ */ jsxs8(Box8, { children: [
2332
- /* @__PURE__ */ jsx9(Text9, { color: "cyan", children: "\u203A " }),
2333
- /* @__PURE__ */ jsx9(
2551
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
2552
+ /* @__PURE__ */ jsx10(Text10, { bold: true, color: "cyan", children: "Welcome to kimiflare!" }),
2553
+ /* @__PURE__ */ jsx10(Text10, { color: "gray", dimColor: true, children: "Terminal coding agent powered by Kimi-K2.6 on Cloudflare Workers AI." }),
2554
+ /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexDirection: "column", children: [
2555
+ step === "accountId" && /* @__PURE__ */ jsxs9(Fragment, { children: [
2556
+ /* @__PURE__ */ jsx10(Text10, { children: "Enter your Cloudflare Account ID:" }),
2557
+ /* @__PURE__ */ jsxs9(Box9, { children: [
2558
+ /* @__PURE__ */ jsx10(Text10, { color: "cyan", children: "\u203A " }),
2559
+ /* @__PURE__ */ jsx10(
2334
2560
  CustomTextInput,
2335
2561
  {
2336
2562
  value: accountId,
@@ -2340,12 +2566,12 @@ function Onboarding({ onDone }) {
2340
2566
  )
2341
2567
  ] })
2342
2568
  ] }),
2343
- step === "apiToken" && /* @__PURE__ */ jsxs8(Fragment, { children: [
2344
- /* @__PURE__ */ jsx9(Text9, { children: "Enter your Cloudflare API Token:" }),
2345
- /* @__PURE__ */ jsx9(Text9, { color: "gray", dimColor: true, children: "Create one at https://dash.cloudflare.com/profile/api-tokens" }),
2346
- /* @__PURE__ */ jsxs8(Box8, { children: [
2347
- /* @__PURE__ */ jsx9(Text9, { color: "cyan", children: "\u203A " }),
2348
- /* @__PURE__ */ jsx9(
2569
+ step === "apiToken" && /* @__PURE__ */ jsxs9(Fragment, { children: [
2570
+ /* @__PURE__ */ jsx10(Text10, { children: "Enter your Cloudflare API Token:" }),
2571
+ /* @__PURE__ */ jsx10(Text10, { color: "gray", dimColor: true, children: "Create one at https://dash.cloudflare.com/profile/api-tokens" }),
2572
+ /* @__PURE__ */ jsxs9(Box9, { children: [
2573
+ /* @__PURE__ */ jsx10(Text10, { color: "cyan", children: "\u203A " }),
2574
+ /* @__PURE__ */ jsx10(
2349
2575
  CustomTextInput,
2350
2576
  {
2351
2577
  value: apiToken,
@@ -2356,15 +2582,15 @@ function Onboarding({ onDone }) {
2356
2582
  )
2357
2583
  ] })
2358
2584
  ] }),
2359
- step === "model" && /* @__PURE__ */ jsxs8(Fragment, { children: [
2360
- /* @__PURE__ */ jsx9(Text9, { children: "Model ID (press Enter for default):" }),
2361
- /* @__PURE__ */ jsxs8(Text9, { color: "gray", dimColor: true, children: [
2585
+ step === "model" && /* @__PURE__ */ jsxs9(Fragment, { children: [
2586
+ /* @__PURE__ */ jsx10(Text10, { children: "Model ID (press Enter for default):" }),
2587
+ /* @__PURE__ */ jsxs9(Text10, { color: "gray", dimColor: true, children: [
2362
2588
  "default: ",
2363
2589
  DEFAULT_MODEL
2364
2590
  ] }),
2365
- /* @__PURE__ */ jsxs8(Box8, { children: [
2366
- /* @__PURE__ */ jsx9(Text9, { color: "cyan", children: "\u203A " }),
2367
- /* @__PURE__ */ jsx9(
2591
+ /* @__PURE__ */ jsxs9(Box9, { children: [
2592
+ /* @__PURE__ */ jsx10(Text10, { color: "cyan", children: "\u203A " }),
2593
+ /* @__PURE__ */ jsx10(
2368
2594
  CustomTextInput,
2369
2595
  {
2370
2596
  value: model,
@@ -2374,26 +2600,26 @@ function Onboarding({ onDone }) {
2374
2600
  )
2375
2601
  ] })
2376
2602
  ] }),
2377
- step === "confirm" && /* @__PURE__ */ jsxs8(Fragment, { children: [
2378
- /* @__PURE__ */ jsx9(Text9, { children: "Ready to save configuration:" }),
2379
- /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginLeft: 2, children: [
2380
- /* @__PURE__ */ jsxs8(Text9, { color: "gray", children: [
2603
+ step === "confirm" && /* @__PURE__ */ jsxs9(Fragment, { children: [
2604
+ /* @__PURE__ */ jsx10(Text10, { children: "Ready to save configuration:" }),
2605
+ /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginLeft: 2, children: [
2606
+ /* @__PURE__ */ jsxs9(Text10, { color: "gray", children: [
2381
2607
  "Account ID: ",
2382
2608
  accountId
2383
2609
  ] }),
2384
- /* @__PURE__ */ jsxs8(Text9, { color: "gray", children: [
2610
+ /* @__PURE__ */ jsxs9(Text10, { color: "gray", children: [
2385
2611
  "API Token: ",
2386
2612
  "\u2022".repeat(apiToken.length)
2387
2613
  ] }),
2388
- /* @__PURE__ */ jsxs8(Text9, { color: "gray", children: [
2614
+ /* @__PURE__ */ jsxs9(Text10, { color: "gray", children: [
2389
2615
  "Model: ",
2390
2616
  model
2391
2617
  ] })
2392
2618
  ] }),
2393
- /* @__PURE__ */ jsx9(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text9, { children: "Press Enter to confirm, or Ctrl+C to cancel" }) }),
2394
- /* @__PURE__ */ jsxs8(Box8, { children: [
2395
- /* @__PURE__ */ jsx9(Text9, { color: "cyan", children: "\u203A " }),
2396
- /* @__PURE__ */ jsx9(
2619
+ /* @__PURE__ */ jsx10(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text10, { children: "Press Enter to confirm, or Ctrl+C to cancel" }) }),
2620
+ /* @__PURE__ */ jsxs9(Box9, { children: [
2621
+ /* @__PURE__ */ jsx10(Text10, { color: "cyan", children: "\u203A " }),
2622
+ /* @__PURE__ */ jsx10(
2397
2623
  CustomTextInput,
2398
2624
  {
2399
2625
  value: "",
@@ -2404,7 +2630,7 @@ function Onboarding({ onDone }) {
2404
2630
  )
2405
2631
  ] })
2406
2632
  ] }),
2407
- savedPath && /* @__PURE__ */ jsxs8(Text9, { color: "green", children: [
2633
+ savedPath && /* @__PURE__ */ jsxs9(Text10, { color: "green", children: [
2408
2634
  "Config saved to ",
2409
2635
  savedPath
2410
2636
  ] })
@@ -2491,10 +2717,10 @@ var init_theme = __esm({
2491
2717
  // src/sessions.ts
2492
2718
  import { readFile as readFile7, writeFile as writeFile5, mkdir as mkdir4, readdir, stat as stat2 } from "fs/promises";
2493
2719
  import { homedir as homedir5 } from "os";
2494
- import { join as join3 } from "path";
2720
+ import { join as join4 } from "path";
2495
2721
  function sessionsDir() {
2496
- const xdg = process.env.XDG_DATA_HOME || join3(homedir5(), ".local", "share");
2497
- return join3(xdg, "kimiflare", "sessions");
2722
+ const xdg = process.env.XDG_DATA_HOME || join4(homedir5(), ".local", "share");
2723
+ return join4(xdg, "kimiflare", "sessions");
2498
2724
  }
2499
2725
  function sanitize(text) {
2500
2726
  return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
@@ -2507,7 +2733,7 @@ function makeSessionId(firstPrompt) {
2507
2733
  async function saveSession(file) {
2508
2734
  const dir = sessionsDir();
2509
2735
  await mkdir4(dir, { recursive: true });
2510
- const path = join3(dir, `${file.id}.json`);
2736
+ const path = join4(dir, `${file.id}.json`);
2511
2737
  await writeFile5(path, JSON.stringify(file, null, 2), "utf8");
2512
2738
  return path;
2513
2739
  }
@@ -2522,7 +2748,7 @@ async function listSessions(limit = 30) {
2522
2748
  const summaries = [];
2523
2749
  for (const name of entries) {
2524
2750
  if (!name.endsWith(".json")) continue;
2525
- const path = join3(dir, name);
2751
+ const path = join4(dir, name);
2526
2752
  try {
2527
2753
  const [s, raw] = await Promise.all([stat2(path), readFile7(path, "utf8")]);
2528
2754
  const parsed = JSON.parse(raw);
@@ -2558,16 +2784,29 @@ __export(app_exports, {
2558
2784
  renderApp: () => renderApp
2559
2785
  });
2560
2786
  import { useState as useState4, useRef as useRef2, useEffect as useEffect3, useCallback } from "react";
2561
- import { Box as Box9, Text as Text10, useApp, useInput as useInput2, render } from "ink";
2787
+ import { Box as Box10, Text as Text11, useApp, useInput as useInput2, render } from "ink";
2562
2788
  import Spinner2 from "ink-spinner";
2789
+ import { existsSync } from "fs";
2790
+ import { join as join5 } from "path";
2563
2791
  import { unlink } from "fs/promises";
2564
- import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
2792
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
2565
2793
  function App({ initialCfg }) {
2566
2794
  const { exit } = useApp();
2567
2795
  const [cfg, setCfg] = useState4(initialCfg);
2568
- const [events, setEvents] = useState4([
2569
- { kind: "info", key: mkKey(), text: "kimiflare \xB7 /help for commands \xB7 ctrl-c to exit \xB7 shift+tab to cycle modes" }
2570
- ]);
2796
+ const [events, setEvents] = useState4(() => {
2797
+ const initial = [
2798
+ { kind: "info", key: mkKey(), text: "kimiflare \xB7 /help for commands \xB7 ctrl-c to exit \xB7 shift+tab to cycle modes" }
2799
+ ];
2800
+ const ctxFile = loadContextFile(process.cwd());
2801
+ if (ctxFile) {
2802
+ initial.push({
2803
+ kind: "info",
2804
+ key: mkKey(),
2805
+ text: `loaded project context from ${ctxFile.name} (${ctxFile.lineCount} lines)`
2806
+ });
2807
+ }
2808
+ return initial;
2809
+ });
2571
2810
  const [input, setInput] = useState4("");
2572
2811
  const [busy, setBusy] = useState4(false);
2573
2812
  const [usage, setUsage] = useState4(null);
@@ -2605,6 +2844,8 @@ function App({ initialCfg }) {
2605
2844
  const sessionIdRef = useRef2(null);
2606
2845
  const modeRef = useRef2(mode);
2607
2846
  const effortRef = useRef2(effort);
2847
+ const tasksRef = useRef2([]);
2848
+ const usageRef = useRef2(null);
2608
2849
  useEffect3(() => {
2609
2850
  modeRef.current = mode;
2610
2851
  messagesRef.current[0] = {
@@ -2751,6 +2992,141 @@ function App({ initialCfg }) {
2751
2992
  const sessions = await listSessions(30);
2752
2993
  setResumeSessions(sessions);
2753
2994
  }, []);
2995
+ const runInit = useCallback(async () => {
2996
+ if (!cfg) return;
2997
+ if (busy) {
2998
+ setEvents((e) => [...e, { kind: "info", key: mkKey(), text: "can't /init while model is running" }]);
2999
+ return;
3000
+ }
3001
+ const cwd = process.cwd();
3002
+ for (const name of ["KIMI.md", "KIMIFLARE.md", "AGENT.md"]) {
3003
+ if (existsSync(join5(cwd, name))) {
3004
+ setEvents((e) => [
3005
+ ...e,
3006
+ {
3007
+ kind: "info",
3008
+ key: mkKey(),
3009
+ text: `${name} already exists at ${join5(cwd, name)} \u2014 delete it first if you want to regenerate`
3010
+ }
3011
+ ]);
3012
+ return;
3013
+ }
3014
+ }
3015
+ const prompt = [
3016
+ "Generate a KIMI.md at the repository root so future agents have project context.",
3017
+ "",
3018
+ "First, use the `glob`, `read`, and `grep` tools to understand the project: read `package.json`, the top-level `README.md` if present, the tsconfig / build config, and skim the top-level source directory structure.",
3019
+ "",
3020
+ "Then call the `write` tool to create `KIMI.md` at the repo root with these sections, terse (aim \u2264 100 lines total):",
3021
+ "",
3022
+ "- **Project** \u2014 one-line description + primary language/runtime.",
3023
+ "- **Build / test / run** \u2014 exact shell commands an agent should use.",
3024
+ "- **Layout** \u2014 key directories and what lives in each.",
3025
+ "- **Conventions** \u2014 naming, import style, file structure, commit style, anything surprising.",
3026
+ "- **Do / Don't** \u2014 quirks or rules future agents should know.",
3027
+ "",
3028
+ "Do not call `tasks_set` for this. Just read what you need, then write the file."
3029
+ ].join("\n");
3030
+ setEvents((e) => [...e, { kind: "user", key: mkKey(), text: "/init" }]);
3031
+ messagesRef.current.push({ role: "user", content: prompt });
3032
+ setBusy(true);
3033
+ const controller = new AbortController();
3034
+ activeControllerRef.current = controller;
3035
+ try {
3036
+ await runAgentTurn({
3037
+ accountId: cfg.accountId,
3038
+ apiToken: cfg.apiToken,
3039
+ model: cfg.model,
3040
+ messages: messagesRef.current,
3041
+ tools: ALL_TOOLS,
3042
+ executor: executorRef.current,
3043
+ cwd,
3044
+ signal: controller.signal,
3045
+ reasoningEffort: effortRef.current,
3046
+ callbacks: {
3047
+ onAssistantStart: () => {
3048
+ const id = nextAssistantId++;
3049
+ activeAsstIdRef.current = id;
3050
+ setEvents((e) => [
3051
+ ...e,
3052
+ { kind: "assistant", key: `asst_${id}`, id, text: "", reasoning: "", streaming: true }
3053
+ ]);
3054
+ },
3055
+ onReasoningDelta: (d) => {
3056
+ const id = activeAsstIdRef.current;
3057
+ if (id !== null) updateAssistant(id, (e) => ({ reasoning: e.reasoning + d }));
3058
+ },
3059
+ onTextDelta: (d) => {
3060
+ const id = activeAsstIdRef.current;
3061
+ if (id !== null) updateAssistant(id, (e) => ({ text: e.text + d }));
3062
+ },
3063
+ onAssistantFinal: () => {
3064
+ const id = activeAsstIdRef.current;
3065
+ if (id !== null) updateAssistant(id, () => ({ streaming: false }));
3066
+ },
3067
+ onToolCallFinalized: (call) => {
3068
+ const spec = executorRef.current.list().find((t) => t.name === call.function.name);
3069
+ let renderMeta;
3070
+ try {
3071
+ const args = call.function.arguments ? JSON.parse(call.function.arguments) : {};
3072
+ renderMeta = spec?.render?.(args);
3073
+ } catch {
3074
+ }
3075
+ setEvents((e) => [
3076
+ ...e,
3077
+ {
3078
+ kind: "tool",
3079
+ key: `tool_${call.id}`,
3080
+ id: call.id,
3081
+ name: call.function.name,
3082
+ args: call.function.arguments,
3083
+ status: "running",
3084
+ render: renderMeta,
3085
+ expanded: false
3086
+ }
3087
+ ]);
3088
+ },
3089
+ onToolResult: (r) => {
3090
+ updateTool(r.tool_call_id, { status: r.ok ? "done" : "error", result: r.content });
3091
+ },
3092
+ onUsage: (u) => {
3093
+ usageRef.current = u;
3094
+ setUsage(u);
3095
+ },
3096
+ askPermission: (req) => new Promise((resolve2) => {
3097
+ if (modeRef.current === "auto") return resolve2("allow");
3098
+ setPerm({ tool: req.tool, args: req.args, resolve: resolve2 });
3099
+ })
3100
+ }
3101
+ });
3102
+ if (existsSync(join5(cwd, "KIMI.md"))) {
3103
+ messagesRef.current[0] = {
3104
+ role: "system",
3105
+ content: buildSystemPrompt({
3106
+ cwd,
3107
+ tools: ALL_TOOLS,
3108
+ model: cfg.model,
3109
+ mode: modeRef.current
3110
+ })
3111
+ };
3112
+ setEvents((e) => [
3113
+ ...e,
3114
+ { kind: "info", key: mkKey(), text: "KIMI.md generated; context loaded for future turns" }
3115
+ ]);
3116
+ }
3117
+ } catch (e) {
3118
+ if (e.name !== "AbortError") {
3119
+ setEvents((es) => [
3120
+ ...es,
3121
+ { kind: "error", key: mkKey(), text: `init failed: ${e.message}` }
3122
+ ]);
3123
+ }
3124
+ } finally {
3125
+ setBusy(false);
3126
+ activeAsstIdRef.current = null;
3127
+ activeControllerRef.current = null;
3128
+ }
3129
+ }, [cfg, busy, updateAssistant, updateTool]);
2754
3130
  const handleResumePick = useCallback(
2755
3131
  async (picked) => {
2756
3132
  setResumeSessions(null);
@@ -2935,6 +3311,10 @@ use: /thinking low | medium | high`
2935
3311
  void runCompact();
2936
3312
  return true;
2937
3313
  }
3314
+ if (c === "/init") {
3315
+ void runInit();
3316
+ return true;
3317
+ }
2938
3318
  if (c === "/update") {
2939
3319
  if (updateInfo?.hasUpdate) {
2940
3320
  setEvents((e) => [
@@ -2990,14 +3370,14 @@ use: /thinking low | medium | high`
2990
3370
  {
2991
3371
  kind: "info",
2992
3372
  key: mkKey(),
2993
- 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 /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"
3373
+ 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"
2994
3374
  }
2995
3375
  ]);
2996
3376
  return true;
2997
3377
  }
2998
3378
  return false;
2999
3379
  },
3000
- [cfg, exit, usage, updateInfo, effort, theme, mode, openResumePicker, runCompact]
3380
+ [cfg, exit, usage, updateInfo, effort, theme, mode, openResumePicker, runCompact, runInit]
3001
3381
  );
3002
3382
  const processMessage = useCallback(
3003
3383
  async (text, displayText) => {
@@ -3071,19 +3451,22 @@ use: /thinking low | medium | high`
3071
3451
  result: r.content
3072
3452
  });
3073
3453
  },
3074
- onUsage: (u) => setUsage(u),
3454
+ onUsage: (u) => {
3455
+ usageRef.current = u;
3456
+ setUsage(u);
3457
+ },
3075
3458
  onTasks: (nextTasks) => {
3076
- setTasks((prev) => {
3077
- if (prev.length === 0 && nextTasks.length > 0) {
3078
- setTasksStartedAt(Date.now());
3079
- setTasksStartTokens(usage?.prompt_tokens ?? 0);
3080
- }
3081
- if (nextTasks.length === 0) {
3082
- setTasksStartedAt(null);
3083
- setTasksStartTokens(0);
3084
- }
3085
- return nextTasks;
3086
- });
3459
+ const prevEmpty = tasksRef.current.length === 0;
3460
+ tasksRef.current = nextTasks;
3461
+ setTasks(nextTasks);
3462
+ if (prevEmpty && nextTasks.length > 0) {
3463
+ setTasksStartedAt(Date.now());
3464
+ setTasksStartTokens(usageRef.current?.prompt_tokens ?? 0);
3465
+ }
3466
+ if (nextTasks.length === 0) {
3467
+ setTasksStartedAt(null);
3468
+ setTasksStartTokens(0);
3469
+ }
3087
3470
  },
3088
3471
  askPermission: (req) => new Promise((resolve2) => {
3089
3472
  if (modeRef.current === "auto") {
@@ -3183,7 +3566,7 @@ use: /thinking low | medium | high`
3183
3566
  }
3184
3567
  }, [usage]);
3185
3568
  if (!cfg) {
3186
- return /* @__PURE__ */ jsx10(
3569
+ return /* @__PURE__ */ jsx11(
3187
3570
  Onboarding,
3188
3571
  {
3189
3572
  onDone: (newCfg) => {
@@ -3197,11 +3580,11 @@ use: /thinking low | medium | high`
3197
3580
  );
3198
3581
  }
3199
3582
  if (resumeSessions !== null) {
3200
- return /* @__PURE__ */ jsx10(Box9, { flexDirection: "column", children: /* @__PURE__ */ jsx10(ResumePicker, { sessions: resumeSessions, onPick: handleResumePick, theme }) });
3583
+ return /* @__PURE__ */ jsx11(Box10, { flexDirection: "column", children: /* @__PURE__ */ jsx11(ResumePicker, { sessions: resumeSessions, onPick: handleResumePick, theme }) });
3201
3584
  }
3202
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
3203
- /* @__PURE__ */ jsx10(ChatView, { events, showReasoning, theme, verbose }),
3204
- perm ? /* @__PURE__ */ jsx10(
3585
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
3586
+ /* @__PURE__ */ jsx11(ChatView, { events, showReasoning, theme, verbose }),
3587
+ perm ? /* @__PURE__ */ jsx11(
3205
3588
  PermissionModal,
3206
3589
  {
3207
3590
  tool: perm.tool,
@@ -3212,8 +3595,8 @@ use: /thinking low | medium | high`
3212
3595
  setPerm(null);
3213
3596
  }
3214
3597
  }
3215
- ) : /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginTop: 1, children: [
3216
- tasks.length > 0 && /* @__PURE__ */ jsx10(
3598
+ ) : /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", marginTop: 1, children: [
3599
+ tasks.length > 0 && /* @__PURE__ */ jsx11(
3217
3600
  TaskList,
3218
3601
  {
3219
3602
  tasks,
@@ -3222,11 +3605,11 @@ use: /thinking low | medium | high`
3222
3605
  tokensDelta: Math.max(0, (usage?.prompt_tokens ?? 0) - tasksStartTokens)
3223
3606
  }
3224
3607
  ),
3225
- queue.length > 0 && /* @__PURE__ */ jsx10(Box9, { flexDirection: "column", marginBottom: 1, children: queue.map((q, i) => /* @__PURE__ */ jsxs9(Text10, { color: theme.queue.color, dimColor: theme.queue.dim, children: [
3608
+ queue.length > 0 && /* @__PURE__ */ jsx11(Box10, { flexDirection: "column", marginBottom: 1, children: queue.map((q, i) => /* @__PURE__ */ jsxs10(Text11, { color: theme.queue.color, dimColor: theme.queue.dim, children: [
3226
3609
  "\u23F3 ",
3227
3610
  q.display
3228
3611
  ] }, `queue_${i}`)) }),
3229
- /* @__PURE__ */ jsx10(
3612
+ /* @__PURE__ */ jsx11(
3230
3613
  StatusBar,
3231
3614
  {
3232
3615
  model: cfg.model,
@@ -3239,13 +3622,13 @@ use: /thinking low | medium | high`
3239
3622
  contextLimit: CONTEXT_LIMIT
3240
3623
  }
3241
3624
  ),
3242
- busy && /* @__PURE__ */ jsxs9(Box9, { children: [
3243
- /* @__PURE__ */ jsx10(Text10, { color: theme.spinner, children: /* @__PURE__ */ jsx10(Spinner2, { type: "dots" }) }),
3244
- /* @__PURE__ */ jsx10(Text10, { color: theme.info.color, dimColor: theme.info.dim, children: " working\u2026" })
3625
+ busy && /* @__PURE__ */ jsxs10(Box10, { children: [
3626
+ /* @__PURE__ */ jsx11(Text11, { color: theme.spinner, children: /* @__PURE__ */ jsx11(Spinner2, { type: "dots" }) }),
3627
+ /* @__PURE__ */ jsx11(Text11, { color: theme.info.color, dimColor: theme.info.dim, children: " working\u2026" })
3245
3628
  ] }),
3246
- /* @__PURE__ */ jsxs9(Box9, { children: [
3247
- /* @__PURE__ */ jsx10(Text10, { color: theme.user, children: busy ? "\u23F3 " : "\u203A " }),
3248
- /* @__PURE__ */ jsx10(
3629
+ /* @__PURE__ */ jsxs10(Box10, { children: [
3630
+ /* @__PURE__ */ jsx11(Text11, { color: theme.user, children: busy ? "\u23F3 " : "\u203A " }),
3631
+ /* @__PURE__ */ jsx11(
3249
3632
  CustomTextInput,
3250
3633
  {
3251
3634
  value: input,
@@ -3294,7 +3677,7 @@ use: /thinking low | medium | high`
3294
3677
  ] });
3295
3678
  }
3296
3679
  async function renderApp(cfg) {
3297
- const instance = render(/* @__PURE__ */ jsx10(App, { initialCfg: cfg }));
3680
+ const instance = render(/* @__PURE__ */ jsx11(App, { initialCfg: cfg }));
3298
3681
  await instance.waitUntilExit();
3299
3682
  }
3300
3683
  var CONTEXT_LIMIT, AUTO_COMPACT_SUGGEST_PCT, nextAssistantId, nextKey, mkKey, EFFORT_DESCRIPTIONS;
@@ -3310,6 +3693,7 @@ var init_app = __esm({
3310
3693
  init_permission();
3311
3694
  init_resume_picker();
3312
3695
  init_task_list();
3696
+ init_system_prompt();
3313
3697
  init_text_input();
3314
3698
  init_update_check();
3315
3699
  init_onboarding();