dskcode 0.1.30 → 0.1.33

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
@@ -7,7 +7,7 @@ import {
7
7
  estimateTokens,
8
8
  getModelMeta,
9
9
  isSupportedModel
10
- } from "./chunk-UV4IWYHZ.js";
10
+ } from "./chunk-LWOBXEMJ.js";
11
11
 
12
12
  // src/cli/index.tsx
13
13
  import { Command } from "commander";
@@ -805,7 +805,7 @@ var LOGO_LINES = [
805
805
  ];
806
806
 
807
807
  // src/ui/ChatSession.tsx
808
- import { Box as Box9, Text as Text10, useInput, Static } from "ink";
808
+ import { Box as Box10, Text as Text11, useInput, Static } from "ink";
809
809
  import TextInput from "ink-text-input";
810
810
  import { useEffect as useEffect4, useState as useState4, useCallback as useCallback2, useMemo, useRef as useRef3 } from "react";
811
811
 
@@ -838,7 +838,7 @@ function useDoubleCtrlC(onExit) {
838
838
  }
839
839
 
840
840
  // src/ui/ChatSession.tsx
841
- import InkSpinner2 from "ink-spinner";
841
+ import InkSpinner3 from "ink-spinner";
842
842
 
843
843
  // src/ui/AssistantMessage.tsx
844
844
  import { Box as Box5, Text as Text6 } from "ink";
@@ -1175,21 +1175,20 @@ function formatArgsSummary(args) {
1175
1175
  return ` ${truncated}`;
1176
1176
  }
1177
1177
  }
1178
- function ToolCallBlock({ call, showPendingHint = true }) {
1178
+ function ToolCallBlock({ call }) {
1179
1179
  const argsDisplay = formatArgsSummary(call.arguments);
1180
1180
  return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginLeft: 3, marginTop: 1, children: [
1181
1181
  /* @__PURE__ */ jsxs3(Box3, { children: [
1182
- /* @__PURE__ */ jsxs3(Text4, { color: "#00ffff", bold: true, children: [
1182
+ /* @__PURE__ */ jsxs3(Text4, { dimColor: true, children: [
1183
1183
  "\u{1F4E6} ",
1184
1184
  call.name
1185
1185
  ] }),
1186
- /* @__PURE__ */ jsxs3(Text4, { color: "#555555", children: [
1186
+ /* @__PURE__ */ jsxs3(Text4, { dimColor: true, children: [
1187
1187
  " ",
1188
1188
  "\u2500".repeat(Math.max(1, 30 - call.name.length))
1189
1189
  ] })
1190
1190
  ] }),
1191
- /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", children: /* @__PURE__ */ jsx3(Text4, { color: "#888888", children: argsDisplay }) }),
1192
- showPendingHint && /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Text4, { color: "yellow", children: "\u23F3 \u7B49\u5F85\u6267\u884C" }) })
1191
+ /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", children: /* @__PURE__ */ jsx3(Text4, { dimColor: true, children: argsDisplay }) })
1193
1192
  ] });
1194
1193
  }
1195
1194
 
@@ -1278,6 +1277,226 @@ function formatUsageSummary(usage) {
1278
1277
 
1279
1278
  // src/ui/HighlightedText.tsx
1280
1279
  import { Box as Box4, Text as Text5 } from "ink";
1280
+
1281
+ // src/ui/table-layout.ts
1282
+ var TABLE_MIN_COL_WIDTH = 3;
1283
+ var DEFAULT_TERM_WIDTH = 80;
1284
+ var TABLE_MARGIN = 2;
1285
+ function cpWidth(cp2) {
1286
+ if (cp2 >= 4352 && cp2 <= 4447 || // Hangul Jamo
1287
+ cp2 === 9001 || cp2 === 9002 || cp2 >= 11904 && cp2 <= 42191 || // CJK Radicals ~ Yi
1288
+ cp2 >= 43360 && cp2 <= 43391 || // Hangul Extended
1289
+ cp2 >= 44032 && cp2 <= 55215 || // Hangul Syllables
1290
+ cp2 >= 63744 && cp2 <= 64255 || // CJK Compat
1291
+ cp2 >= 65040 && cp2 <= 65055 || // Vertical forms
1292
+ cp2 >= 65281 && cp2 <= 65376 || // Fullwidth ASCII / 标点
1293
+ cp2 >= 65504 && cp2 <= 65510 || // Fullwidth signs
1294
+ cp2 >= 126976 && cp2 <= 131071 || // Emoji / Symbols
1295
+ cp2 >= 12288 && cp2 <= 12351 || // CJK Symbols & Punctuation(含「、。·」等)
1296
+ cp2 >= 131072 && cp2 <= 196607) {
1297
+ return 2;
1298
+ }
1299
+ if (cp2 < 32 || cp2 >= 127 && cp2 < 160) return 0;
1300
+ return 1;
1301
+ }
1302
+ function visualWidth(text) {
1303
+ let w = 0;
1304
+ for (const ch of text) {
1305
+ const cp2 = ch.codePointAt(0);
1306
+ if (cp2 === void 0) continue;
1307
+ if (cp2 === 8205) continue;
1308
+ if (cp2 >= 65024 && cp2 <= 65039) continue;
1309
+ w += cpWidth(cp2);
1310
+ }
1311
+ return w;
1312
+ }
1313
+ function* iterateChars(text) {
1314
+ for (const ch of text) {
1315
+ const cp2 = ch.codePointAt(0);
1316
+ if (cp2 === void 0) continue;
1317
+ if (cp2 === 8205) continue;
1318
+ if (cp2 >= 65024 && cp2 <= 65039) continue;
1319
+ yield { ch, width: cpWidth(cp2) };
1320
+ }
1321
+ }
1322
+ function wrapByWidth(text, maxWidth) {
1323
+ if (maxWidth <= 0) return [text];
1324
+ const out = [];
1325
+ for (const para of text.split("\n")) {
1326
+ if (para.length === 0) {
1327
+ out.push("");
1328
+ continue;
1329
+ }
1330
+ const wrapped = wrapOneLine(para, maxWidth);
1331
+ for (const w of wrapped) out.push(w);
1332
+ }
1333
+ return out.length === 0 ? [""] : out;
1334
+ }
1335
+ function wrapOneLine(text, maxWidth) {
1336
+ if (visualWidth(text) <= maxWidth) return [text];
1337
+ const lines = [];
1338
+ let current = "";
1339
+ let currentW = 0;
1340
+ let breakCandidate = -1;
1341
+ let breakCandidateW = 0;
1342
+ for (const { ch, width } of iterateChars(text)) {
1343
+ if (ch === " " || ch === " ") {
1344
+ breakCandidate = current.length;
1345
+ breakCandidateW = currentW + width;
1346
+ current += ch;
1347
+ currentW += width;
1348
+ continue;
1349
+ }
1350
+ if (currentW + width > maxWidth) {
1351
+ if (breakCandidate > 0) {
1352
+ const split = current.slice(0, breakCandidate).replace(/[ \t]+$/, "");
1353
+ lines.push(split);
1354
+ const rest = current.slice(breakCandidate).replace(/^[ \t]+/, "");
1355
+ current = rest + ch;
1356
+ currentW = visualWidth(current);
1357
+ breakCandidate = -1;
1358
+ breakCandidateW = 0;
1359
+ } else {
1360
+ lines.push(current);
1361
+ current = ch;
1362
+ currentW = width;
1363
+ }
1364
+ } else {
1365
+ current += ch;
1366
+ currentW += width;
1367
+ }
1368
+ }
1369
+ if (current.length > 0) lines.push(current);
1370
+ return lines;
1371
+ }
1372
+ function wrapRowCells(cells, colWidths, alignments) {
1373
+ return cells.map((cell, ci) => {
1374
+ const w = colWidths[ci] ?? TABLE_MIN_COL_WIDTH;
1375
+ const lines = wrapByWidth(cell, w);
1376
+ return lines.map((l) => padToWidth(l, w, alignments[ci] ?? "left"));
1377
+ });
1378
+ }
1379
+ function padToWidth(text, targetWidth, align = "left") {
1380
+ const vw = visualWidth(text);
1381
+ const delta = targetWidth - vw;
1382
+ if (delta <= 0) return text;
1383
+ const leftPad = align === "right" ? delta : align === "center" ? Math.floor(delta / 2) : 0;
1384
+ const rightPad = delta - leftPad;
1385
+ return " ".repeat(leftPad) + text + " ".repeat(rightPad);
1386
+ }
1387
+ function parseTableCells(line) {
1388
+ const t = line.trim();
1389
+ if (!t.startsWith("|") || !t.endsWith("|")) return [];
1390
+ const inner = t.slice(1, t.length - 1);
1391
+ return inner.split("|").map((c) => c.trim());
1392
+ }
1393
+ function parseAlignments(sepLine) {
1394
+ return parseTableCells(sepLine).map((c) => {
1395
+ const l = c.startsWith(":");
1396
+ const r = c.endsWith(":");
1397
+ if (l && r) return "center";
1398
+ if (r) return "right";
1399
+ return "left";
1400
+ });
1401
+ }
1402
+ function layoutTable(text, options = {}) {
1403
+ const termWidth = options.termWidth ?? DEFAULT_TERM_WIDTH;
1404
+ const outerMargin = options.outerMargin ?? 0;
1405
+ const usable = Math.max(termWidth - outerMargin, 20);
1406
+ const rawLines = text.split("\n").filter((l) => l.trim().length > 0);
1407
+ if (rawLines.length < 2) {
1408
+ return { lines: rawLines, colWidths: [], totalWidth: 0 };
1409
+ }
1410
+ let sepIdx = -1;
1411
+ for (let k = 0; k < rawLines.length; k++) {
1412
+ const t = (rawLines[k] ?? "").trim();
1413
+ if (/^\|[-: |]+\|$/.test(t) && t.includes("-")) {
1414
+ sepIdx = k;
1415
+ break;
1416
+ }
1417
+ }
1418
+ if (sepIdx === -1) {
1419
+ return { lines: rawLines, colWidths: [], totalWidth: 0 };
1420
+ }
1421
+ const headerCells = parseTableCells(rawLines.slice(0, sepIdx).join(""));
1422
+ const alignments = parseAlignments(rawLines[sepIdx] ?? "");
1423
+ const dataRows = rawLines.slice(sepIdx + 1).map(parseTableCells);
1424
+ const colCount = headerCells.length;
1425
+ if (colCount === 0) {
1426
+ return { lines: rawLines, colWidths: [], totalWidth: 0 };
1427
+ }
1428
+ const colMaxWidths = [];
1429
+ for (let ci = 0; ci < colCount; ci++) {
1430
+ let max = visualWidth(headerCells[ci] ?? "");
1431
+ for (const row of dataRows) {
1432
+ const w = visualWidth(row[ci] ?? "");
1433
+ if (w > max) max = w;
1434
+ }
1435
+ colMaxWidths.push(Math.max(max, TABLE_MIN_COL_WIDTH));
1436
+ }
1437
+ const margin = TABLE_MARGIN;
1438
+ const totalMinWidth = colMaxWidths.reduce((s, w) => s + w + margin, 1);
1439
+ let colWidths;
1440
+ if (totalMinWidth <= usable) {
1441
+ colWidths = colMaxWidths;
1442
+ } else {
1443
+ const available = usable - 1 - margin * colCount;
1444
+ const sum = colMaxWidths.reduce((s, w) => s + w, 0);
1445
+ colWidths = colMaxWidths.map(
1446
+ (w) => Math.max(TABLE_MIN_COL_WIDTH, Math.floor(w / sum * available))
1447
+ );
1448
+ let total = colWidths.reduce((s, w) => s + w + margin, 1);
1449
+ while (total > usable && colWidths.length > 0) {
1450
+ const last = colWidths.length - 1;
1451
+ const cur = colWidths[last] ?? TABLE_MIN_COL_WIDTH;
1452
+ if (cur <= TABLE_MIN_COL_WIDTH) break;
1453
+ colWidths[last] = cur - 1;
1454
+ total = colWidths.reduce((s, w) => s + w + margin, 1);
1455
+ }
1456
+ }
1457
+ const H = "\u2500";
1458
+ const makeSep = (l, m, r) => l + colWidths.map((w) => H.repeat(w + margin)).join(m) + r;
1459
+ const topBorder = makeSep("\u250C", "\u252C", "\u2510");
1460
+ const midBorder = makeSep("\u251C", "\u253C", "\u2524");
1461
+ const botBorder = makeSep("\u2514", "\u2534", "\u2518");
1462
+ function buildRow(cells) {
1463
+ const wrapped = wrapRowCells(cells, colWidths, alignments);
1464
+ const rowHeight = Math.max(1, ...wrapped.map((lines2) => lines2.length));
1465
+ const lines = [];
1466
+ for (let li = 0; li < rowHeight; li++) {
1467
+ let row = "\u2502";
1468
+ for (let ci = 0; ci < colCount; ci++) {
1469
+ const cellLines = wrapped[ci] ?? [];
1470
+ const segment = cellLines[li] ?? " ".repeat(colWidths[ci] ?? TABLE_MIN_COL_WIDTH);
1471
+ row += " " + segment + " ";
1472
+ if (ci < colCount - 1) row += "\u2502";
1473
+ }
1474
+ row += "\u2502";
1475
+ lines.push(row);
1476
+ }
1477
+ return lines;
1478
+ }
1479
+ const headerRow = buildRow(headerCells);
1480
+ const dataRowsOut = dataRows.map(buildRow);
1481
+ const totalWidth = colWidths.reduce((s, w) => s + w + margin, 1);
1482
+ return {
1483
+ lines: [
1484
+ topBorder,
1485
+ ...headerRow,
1486
+ midBorder,
1487
+ ...dataRowsOut.flat(),
1488
+ botBorder
1489
+ ],
1490
+ colWidths,
1491
+ totalWidth
1492
+ };
1493
+ }
1494
+ function detectTermWidth() {
1495
+ const c = process.stdout?.columns;
1496
+ return typeof c === "number" && c > 0 ? c : void 0;
1497
+ }
1498
+
1499
+ // src/ui/HighlightedText.tsx
1281
1500
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1282
1501
  function parseCodeBlocks(text) {
1283
1502
  const segments = [];
@@ -1450,111 +1669,19 @@ function CodeBlockRenderer({ code }) {
1450
1669
  })
1451
1670
  ] });
1452
1671
  }
1453
- var TABLE_MAX_WIDTH = 90;
1454
- var TABLE_MIN_COL_WIDTH = 3;
1455
- function charWidth(ch) {
1456
- const cp2 = ch.codePointAt(0);
1457
- if (cp2 === void 0) return 0;
1458
- if (cp2 >= 4352 && cp2 <= 4447 || // Hangul Jamo
1459
- cp2 === 9001 || cp2 === 9002 || cp2 >= 11904 && cp2 <= 42191 || // CJK Radicals ~ Yi
1460
- cp2 >= 43360 && cp2 <= 43391 || // Hangul Extended
1461
- cp2 >= 44032 && cp2 <= 55215 || // Hangul Syllables
1462
- cp2 >= 63744 && cp2 <= 64255 || // CJK Compat
1463
- cp2 >= 65040 && cp2 <= 65055 || // Vertical forms
1464
- cp2 >= 65281 && cp2 <= 65376 || // Fullwidth
1465
- cp2 >= 65504 && cp2 <= 65510 || cp2 >= 126976 && cp2 <= 131071) {
1466
- return 2;
1467
- }
1468
- return 1;
1469
- }
1470
- function visualWidth(text) {
1471
- let w = 0;
1472
- for (const ch of text) {
1473
- w += charWidth(ch);
1474
- }
1475
- return w;
1476
- }
1477
- function padToWidth(text, targetWidth, align) {
1478
- const vw = visualWidth(text);
1479
- const delta = targetWidth - vw;
1480
- if (delta <= 0) return text;
1481
- const leftPad = align === "right" ? delta : align === "center" ? Math.floor(delta / 2) : 0;
1482
- const rightPad = delta - leftPad;
1483
- return " ".repeat(leftPad) + text + " ".repeat(rightPad);
1484
- }
1485
- function parseTableCells(line) {
1486
- const t = line.trim();
1487
- const inner = t.slice(1, t.length - 1);
1488
- return inner.split("|").map((c) => c.trim());
1489
- }
1490
- function parseAlignments(sepLine) {
1491
- const cells = parseTableCells(sepLine);
1492
- return cells.map((c) => {
1493
- const l = c.startsWith(":");
1494
- const r = c.endsWith(":");
1495
- if (l && r) return "center";
1496
- if (r) return "right";
1497
- return "left";
1498
- });
1499
- }
1500
1672
  function TableRenderer({ text }) {
1501
- const rawLines = text.split("\n").filter((l) => l.trim().length > 0);
1502
- if (rawLines.length < 2) return /* @__PURE__ */ jsx4(Text5, { children: text });
1503
- let sepIdx = -1;
1504
- for (let k = 0; k < rawLines.length; k++) {
1505
- if (isTableSepRow(rawLines[k])) {
1506
- sepIdx = k;
1507
- break;
1508
- }
1509
- }
1510
- if (sepIdx === -1) return /* @__PURE__ */ jsx4(Text5, { children: text });
1511
- const headerText = rawLines.slice(0, sepIdx).join("");
1512
- const headerCells = parseTableCells(headerText);
1513
- const alignments = parseAlignments(rawLines[sepIdx]);
1514
- const dataRows = rawLines.slice(sepIdx + 1).map(parseTableCells);
1515
- const colCount = headerCells.length;
1516
- const colMaxWidths = headerCells.map((_, ci) => {
1517
- let max = visualWidth(headerCells[ci] ?? "");
1518
- for (const row of dataRows) {
1519
- const w = visualWidth(row[ci] ?? "");
1520
- if (w > max) max = w;
1521
- }
1522
- return Math.max(max, TABLE_MIN_COL_WIDTH);
1523
- });
1524
- const margin = 2;
1525
- const totalMinWidth = colMaxWidths.reduce((s, w) => s + w + margin, 1);
1526
- let colWidths;
1527
- if (totalMinWidth <= TABLE_MAX_WIDTH) {
1528
- colWidths = colMaxWidths;
1529
- } else {
1530
- const available = TABLE_MAX_WIDTH - 1 - margin * colCount;
1531
- const sum = colMaxWidths.reduce((s, w) => s + w, 0);
1532
- colWidths = colMaxWidths.map((w) => Math.max(TABLE_MIN_COL_WIDTH, Math.floor(w / sum * available)));
1533
- }
1534
- const H = "\u2500";
1535
- const makeSep = (l, m, r) => l + colWidths.map((w) => H.repeat(w + margin)).join(m) + r;
1536
- const topBorder = makeSep("\u250C", "\u252C", "\u2510");
1537
- const midBorder = makeSep("\u251C", "\u253C", "\u2524");
1538
- const botBorder = makeSep("\u2514", "\u2534", "\u2518");
1539
- function rowLine(cells, keyBase) {
1540
- return /* @__PURE__ */ jsxs4(Text5, { children: [
1541
- "\u2502",
1542
- cells.map((cell, ci) => /* @__PURE__ */ jsxs4(Text5, { children: [
1543
- " ",
1544
- padToWidth(cell.slice(0, colWidths[ci] ?? TABLE_MIN_COL_WIDTH), colWidths[ci] ?? TABLE_MIN_COL_WIDTH, alignments[ci] ?? "left"),
1545
- " ",
1546
- "\u2502"
1547
- ] }, ci))
1548
- ] }, keyBase);
1549
- }
1550
- const rows = [
1551
- /* @__PURE__ */ jsx4(Text5, { color: "#888888", children: topBorder }, "top"),
1552
- rowLine(headerCells, "hdr"),
1553
- /* @__PURE__ */ jsx4(Text5, { color: "#888888", children: midBorder }, "mid"),
1554
- ...dataRows.map((cells, ri) => rowLine(cells, `d${ri}`)),
1555
- /* @__PURE__ */ jsx4(Text5, { color: "#888888", children: botBorder }, "bot")
1556
- ];
1557
- return /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", marginTop: 1, children: rows });
1673
+ const termWidth = detectTermWidth();
1674
+ const layout = layoutTable(text, { termWidth });
1675
+ if (layout.colWidths.length === 0) {
1676
+ return /* @__PURE__ */ jsx4(Text5, { children: text });
1677
+ }
1678
+ return /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", marginTop: 1, children: layout.lines.map((line, i) => {
1679
+ const isFirst = i === 0;
1680
+ const isLast = i === layout.lines.length - 1;
1681
+ const isMid = !isFirst && !isLast && line.startsWith("\u251C");
1682
+ const color = isFirst || isLast || isMid ? "#888888" : void 0;
1683
+ return /* @__PURE__ */ jsx4(Text5, { color, children: line }, i);
1684
+ }) });
1558
1685
  }
1559
1686
  var EMOJI_CP_RE = /[\u{1F000}-\u{1FFFF}\u{FE00}-\u{FE0F}\u{200D}\u{20E3}\u{2600}-\u{27BF}\u{231A}-\u{23FF}\u{2934}\u{2935}\u{25AA}-\u{25FE}\u{2B50}\u{2B55}\u{00A9}\u{00AE}\u{2122}\u{3030}\u{303D}\u{3297}\u{3299}]/u;
1560
1687
  function isEmojiCluster(text) {
@@ -1674,6 +1801,23 @@ function HighlightedText({ children: text }) {
1674
1801
  return /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", children: rendered });
1675
1802
  }
1676
1803
 
1804
+ // src/ui/reasoning-utils.ts
1805
+ var DEFAULT_REASONING_MAX_LINES = 8;
1806
+ function joinReasoningSegments(segments) {
1807
+ return segments.map((s) => s.trim()).filter((s) => s.length > 0).join("\n");
1808
+ }
1809
+ function truncateReasoningLines(text, maxLines) {
1810
+ const lines = text.split("\n");
1811
+ if (lines.length <= maxLines) {
1812
+ return { visible: text, hiddenLines: 0, totalLines: lines.length };
1813
+ }
1814
+ return {
1815
+ visible: lines.slice(-maxLines).join("\n"),
1816
+ hiddenLines: lines.length - maxLines,
1817
+ totalLines: lines.length
1818
+ };
1819
+ }
1820
+
1677
1821
  // src/ui/AssistantMessage.tsx
1678
1822
  import { Fragment, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1679
1823
  function formatElapsed(ms) {
@@ -1707,10 +1851,7 @@ function AnimatedUsage({ usage, cost }) {
1707
1851
  }
1708
1852
  const tokensDelta = targetTokens - displayedTokens;
1709
1853
  const costDelta = targetCost - displayedCost;
1710
- const durationMs = Math.min(
1711
- 600,
1712
- Math.max(220, Math.max(tokensDelta, 0) * 1.2 + 220)
1713
- );
1854
+ const durationMs = Math.min(600, Math.max(220, Math.max(tokensDelta, 0) * 1.2 + 220));
1714
1855
  startRef.current = {
1715
1856
  fromTokens: displayedTokens,
1716
1857
  fromCost: displayedCost,
@@ -1761,6 +1902,7 @@ function AnimatedUsage({ usage, cost }) {
1761
1902
  }
1762
1903
  function AssistantMessage({
1763
1904
  content,
1905
+ reasoning,
1764
1906
  toolCalls,
1765
1907
  isStreaming = false,
1766
1908
  usage,
@@ -1768,21 +1910,43 @@ function AssistantMessage({
1768
1910
  cost,
1769
1911
  model: _model
1770
1912
  }) {
1771
- if (!content && (!toolCalls || toolCalls.length === 0) && !isStreaming) {
1913
+ if (!content && (!toolCalls || toolCalls.length === 0) && (!reasoning || reasoning.length === 0) && !isStreaming) {
1772
1914
  return null;
1773
1915
  }
1774
1916
  return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, children: [
1917
+ reasoning && reasoning.length > 0 && (() => {
1918
+ const merged = joinReasoningSegments(reasoning);
1919
+ if (!merged) return null;
1920
+ const { visible } = truncateReasoningLines(
1921
+ merged,
1922
+ DEFAULT_REASONING_MAX_LINES
1923
+ );
1924
+ return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "row", marginBottom: 1, children: [
1925
+ /* @__PURE__ */ jsx5(Box5, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx5(Text6, { dimColor: true, children: "\u{1F9E0}" }) }),
1926
+ /* @__PURE__ */ jsx5(
1927
+ Box5,
1928
+ {
1929
+ flexGrow: 1,
1930
+ flexDirection: "column",
1931
+ borderStyle: "single",
1932
+ borderColor: "#444444",
1933
+ paddingLeft: 1,
1934
+ paddingRight: 1,
1935
+ children: /* @__PURE__ */ jsx5(Text6, { dimColor: true, wrap: "wrap", children: visible })
1936
+ }
1937
+ )
1938
+ ] });
1939
+ })(),
1775
1940
  /* @__PURE__ */ jsxs5(Box5, { flexDirection: "row", children: [
1776
1941
  /* @__PURE__ */ jsx5(Box5, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx5(Text6, { bold: true, color: "#ff00ff", children: "\u{1F916}" }) }),
1777
1942
  /* @__PURE__ */ jsxs5(Box5, { flexGrow: 1, flexDirection: "column", children: [
1778
1943
  content && /* @__PURE__ */ jsx5(HighlightedText, { children: content }),
1779
- isStreaming && !content && /* @__PURE__ */ jsx5(Text6, { color: "#888888", children: "..." })
1944
+ isStreaming && !content && (!reasoning || reasoning.length === 0) && /* @__PURE__ */ jsx5(Text6, { color: "#888888", children: "..." })
1780
1945
  ] })
1781
1946
  ] }),
1782
1947
  toolCalls && toolCalls.length > 0 && /* @__PURE__ */ jsx5(Box5, { flexDirection: "column", children: toolCalls.map((tc, i) => /* @__PURE__ */ jsx5(ToolCallBlock, { call: tc }, tc.id ?? i)) }),
1783
1948
  isStreaming && (usage || cost !== void 0) && /* @__PURE__ */ jsx5(Box5, { flexDirection: "row", marginTop: 1, marginLeft: 3, children: /* @__PURE__ */ jsxs5(Text6, { color: "#666666", dimColor: true, children: [
1784
- "\u23F3 \u5DF2\u6D88\u8017",
1785
- " ",
1949
+ "\u23F3 \u5DF2\u6D88\u8017 ",
1786
1950
  /* @__PURE__ */ jsx5(AnimatedUsage, { usage, cost })
1787
1951
  ] }) }),
1788
1952
  !isStreaming && (usage || elapsed !== void 0) && /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, marginLeft: 3, children: [
@@ -1891,6 +2055,49 @@ function FileSelector({ files, input, selectedIndex }) {
1891
2055
  ] });
1892
2056
  }
1893
2057
 
2058
+ // src/ui/TodoListPanel.tsx
2059
+ import { Box as Box9, Text as Text10 } from "ink";
2060
+ import InkSpinner2 from "ink-spinner";
2061
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
2062
+ function statusIcon(status) {
2063
+ switch (status) {
2064
+ case "pending":
2065
+ return "\u2610";
2066
+ case "done":
2067
+ return "\u2611";
2068
+ case "failed":
2069
+ return "\u2717";
2070
+ case "skipped":
2071
+ return "\u2298";
2072
+ // running 不走这里,由 TodoIcon 走 Spinner 组件
2073
+ case "running":
2074
+ return "\u25B6";
2075
+ }
2076
+ }
2077
+ function TodoIcon({ status }) {
2078
+ if (status === "running") {
2079
+ return /* @__PURE__ */ jsx9(Text10, { color: "cyan", children: /* @__PURE__ */ jsx9(InkSpinner2, { type: "dots" }) });
2080
+ }
2081
+ if (status === "done") {
2082
+ return /* @__PURE__ */ jsx9(Text10, { color: "green", children: "\u2705" });
2083
+ }
2084
+ if (status === "failed") {
2085
+ return /* @__PURE__ */ jsx9(Text10, { color: "red", children: statusIcon(status) });
2086
+ }
2087
+ return /* @__PURE__ */ jsx9(Text10, { dimColor: true, children: statusIcon(status) });
2088
+ }
2089
+ function TodoListPanel({ items }) {
2090
+ if (items.length === 0) return null;
2091
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginTop: 1, children: [
2092
+ /* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsx9(Text10, { dimColor: true, children: "\u{1F527} \u4EFB\u52A1\u8FDB\u5EA6" }) }),
2093
+ items.map((it) => /* @__PURE__ */ jsxs9(Box9, { children: [
2094
+ /* @__PURE__ */ jsx9(Text10, { dimColor: true, children: " " }),
2095
+ /* @__PURE__ */ jsx9(TodoIcon, { status: it.status }),
2096
+ /* @__PURE__ */ jsx9(Text10, { dimColor: true, children: ` ${it.content}` })
2097
+ ] }, it.id))
2098
+ ] });
2099
+ }
2100
+
1894
2101
  // src/provider/registry.ts
1895
2102
  var ProviderRegistry = class {
1896
2103
  #factories = /* @__PURE__ */ new Map();
@@ -1985,10 +2192,10 @@ var AlwaysAllowGate = class {
1985
2192
  import Handlebars from "handlebars";
1986
2193
 
1987
2194
  // src/agent/prompts/system-prompt.hbs
1988
- var system_prompt_default = '\u4F60\u662F dskcode\uFF0C\u4E00\u4E2A\u57FA\u4E8E DeepSeek \u7684 AI \u7F16\u7A0B\u52A9\u624B\uFF0C\u8FD0\u884C\u5728\u7528\u6237\u7EC8\u7AEF\u4E2D\u3002\u4F60\u7684\u804C\u8D23\u662F\u5E2E\u52A9\u5F00\u53D1\u8005\u7F16\u5199\u3001\u7406\u89E3\u3001\u8C03\u8BD5\u548C\u91CD\u6784\u4EE3\u7801\u3002\r\n\r\n## \u6838\u5FC3\u539F\u5219\r\n- \u56DE\u7B54\u4F7F\u7528\u4E2D\u6587\uFF0C\u4EE3\u7801\u6807\u8BC6\u7B26\u4FDD\u6301\u82F1\u6587\r\n- \u63D0\u4F9B\u51C6\u786E\u3001\u5B9E\u7528\u3001\u53EF\u64CD\u4F5C\u7684\u5EFA\u8BAE\r\n- \u5BF9\u4E0D\u786E\u5B9A\u7684\u5185\u5BB9\u660E\u786E\u6807\u6CE8\uFF0C\u4E0D\u7F16\u9020\u4E8B\u5B9E\r\n- \u4EE3\u7801\u793A\u4F8B\u4FDD\u6301\u7B80\u6D01\uFF0C\u9644\u5E26\u5FC5\u8981\u7684\u6CE8\u91CA\r\n\r\n## \u5DE5\u4F5C\u6D41\u7A0B\r\n\r\n\u4F60\u5904\u4E8E\u4E00\u4E2A\u5DE5\u5177\u8C03\u7528\u5FAA\u73AF\u4E2D\uFF1A\r\n1. \u6536\u5230\u7528\u6237\u8BF7\u6C42\r\n2. \u8C03\u7528\u5DE5\u5177\u83B7\u53D6\u4FE1\u606F\u6216\u4FEE\u6539\u6587\u4EF6\r\n3. \u5DE5\u5177\u7ED3\u679C\u8FD4\u56DE\u7ED9\u4F60\r\n4. \u6839\u636E\u7ED3\u679C\u51B3\u5B9A\u4E0B\u4E00\u6B65\uFF08\u7EE7\u7EED\u8C03\u7528\u5DE5\u5177\u6216\u7ED9\u51FA\u6700\u7EC8\u56DE\u7B54\uFF09\r\n5. \u4E00\u8F6E\u6700\u591A {{maxToolRounds}} \u6B21\u5DE5\u5177\u8C03\u7528\r\n\r\n## \u5DE5\u5177\u4F7F\u7528\u7B56\u7565\r\n\r\n{{#if tools}}\r\n\u5F53\u524D\u53EF\u7528\u7684\u5DE5\u5177\uFF1A\r\n{{#each tools}}\r\n- **{{name}}**\uFF1A{{description}}{{#if params}}\uFF08\u53C2\u6570\uFF1A{{params}}\uFF09{{/if}}\r\n{{/each}}\r\n\r\n\u4F7F\u7528\u539F\u5219\uFF1A\r\n- **\u5148\u641C\u7D22\u518D\u8BFB\u53D6** \u2014 \u4E0D\u786E\u5B9A\u76EE\u6807\u4F4D\u7F6E\u65F6\u5148\u7528 grep/glob \u5B9A\u4F4D\uFF0C\u518D\u7528 read_file \u8BFB\u53D6\r\n- **\u5148\u8BFB\u53D6\u518D\u4FEE\u6539** \u2014 \u7F16\u8F91\u6587\u4EF6\u524D\u5148 read_file \u786E\u8BA4\u5F53\u524D\u5185\u5BB9\r\n- **\u6279\u91CF\u64CD\u4F5C\u4E00\u6B21\u5B8C\u6210** \u2014 \u540C\u4E00\u6587\u4EF6\u7684\u591A\u5904\u6539\u52A8\u5728\u5355\u6B21 edit_file \u4E2D\u5B8C\u6210\r\n- **\u5DE5\u5177\u51FA\u9519\u65F6** \u2014 \u5206\u6790\u9519\u8BEF\u539F\u56E0\uFF08\u8DEF\u5F84\u4E0D\u5BF9\uFF1F\u683C\u5F0F\u4E0D\u5BF9\uFF1F\u6743\u9650\u4E0D\u8DB3\uFF1F\uFF09\uFF0C\u4FEE\u6B63\u540E\u91CD\u8BD5\uFF0C\u4E0D\u8981\u91CD\u590D\u76F8\u540C\u7684\u9519\u8BEF\u8C03\u7528\r\n- **\u5BA1\u614E\u6267\u884C** \u2014 bash \u6267\u884C\u5371\u9669\u547D\u4EE4\uFF08rm -rf\u3001\u5F3A\u5236\u5199\u5165\u7B49\uFF09\u524D\u5148\u786E\u8BA4\r\n{{/if}}\r\n\r\n{{#if projectContext}}\r\n## \u9879\u76EE\u4E0A\u4E0B\u6587\r\n\r\n{{projectContext}}\r\n{{/if}}\r\n\r\n## \u56DE\u7B54\u683C\u5F0F\r\n\r\n\u7EC8\u7AEF\u4E0D\u652F\u6301 Markdown \u6E32\u67D3\u3002\u56DE\u590D\u683C\u5F0F\u8981\u6C42\uFF1A\r\n\r\n- \u7528 emoji \u505A\u89C6\u89C9\u6807\u8BB0\uFF08\u{1F4CC} \u8981\u70B9 \u26A0\uFE0F \u6CE8\u610F \u{1F4DD} \u5F85\u529E \u2705 \u5B8C\u6210\uFF09\r\n- \u7528\u6570\u5B57\u5E8F\u53F7\u6216\u77ED\u6A2A\u7EBF\u7EC4\u7EC7\u5217\u8868\r\n- \u4EE3\u7801\u6807\u8BC6\u7B26\u3001\u6587\u4EF6\u8DEF\u5F84\u3001\u7C7B\u578B\u540D\u7528\u5355\u4E2A\u53CD\u5F15\u53F7 ` \u5305\u88F9\uFF0C\u4F1A\u88AB\u81EA\u52A8\u9AD8\u4EAE\r\n- \u5F3A\u8C03\u5185\u5BB9\u7528 **\u52A0\u7C97** \u5305\u88F9\uFF0C\u4F1A\u88AB\u81EA\u52A8\u9AD8\u4EAE\r\n- \u591A\u884C\u4EE3\u7801\u7528\u7F29\u8FDB 4 \u4E2A\u7A7A\u683C\u8868\u793A\uFF0C\u4E0D\u8981\u7528\u4E09\u53CD\u5F15\u53F7\u4EE3\u7801\u5757\r\n- \u6BB5\u843D\u95F4\u7528\u7A7A\u884C\u5206\u9694\r\n- **\u4E0D\u8981\u4F7F\u7528 `#` \u7B26\u53F7** \u2014 \u7EC8\u7AEF\u4E0D\u6E32\u67D3 Markdown \u6807\u9898\uFF0C\u8BF7\u7528 emoji + \u52A0\u7C97\u66FF\u4EE3\uFF08\u5982 `\u{1F4CC} **\u6807\u9898**`\uFF09\r\n\r\n\u6B63\u786E\u793A\u4F8B\uFF1A\r\n\r\n\u{1F4CC} **\u4FEE\u6539\u8BA1\u5212**\r\n\r\n\u{1F4CC} \u9700\u8981\u4FEE\u6539\u4E24\u4E2A\u5730\u65B9\uFF1A\r\n1. \u7B2C 12 \u884C\u7684 `interface FileDiff` \u6539\u4E3A `interface Diff`\r\n2. \u7B2C 432 \u884C\u7684 `): FileDiff` \u6539\u4E3A `): Diff`\r\n\r\n \u4EE3\u7801\u793A\u4F8B\uFF1A\r\n const pool = new Pool({ max: 20 });\r\n await pool.query("SELECT * FROM users");\r\n\r\n## \u884C\u4E3A\u7EA6\u675F\r\n- \u5982\u679C\u7528\u6237\u8BF7\u6C42\u4E0D\u660E\u786E\uFF0C\u4E3B\u52A8\u8BE2\u95EE\u6F84\u6E05\r\n- \u5BF9\u4E0D\u786E\u5B9A\u7684\u4FE1\u606F\u660E\u786E\u6807\u6CE8\uFF0C\u4E0D\u7F16\u9020\u4E8B\u5B9E\r\n- \u4E0D\u6267\u884C\u53EF\u80FD\u9020\u6210\u4E0D\u53EF\u9006\u635F\u5BB3\u7684\u64CD\u4F5C\uFF08\u5982 rm -rf\uFF09\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u9010\u6761\u786E\u8BA4\r\n- \u5DE5\u5177\u8C03\u7528\u8FD4\u56DE\u9519\u8BEF\u65F6\uFF0C\u5148\u5206\u6790\u539F\u56E0\u518D\u91CD\u8BD5\uFF0C\u4E0D\u8981\u76F2\u76EE\u91CD\u8BD5\r\n\r\n## \u5F53\u524D\u6A21\u578B\r\n- \u6A21\u578B\uFF1A{{model}}\r\n\r\n## \u65F6\u95F4\u4E0A\u4E0B\u6587\r\n- \u5F53\u524D\u65E5\u671F\uFF1A{{date}}\r\n- \u5F53\u524D\u65F6\u95F4\uFF1A{{time}}\r\n- \u5DE5\u4F5C\u76EE\u5F55\uFF1A{{cwd}}\r\n';
2195
+ var system_prompt_default = '\u4F60\u662F dskcode\uFF0C\u4E00\u4E2A\u57FA\u4E8E DeepSeek \u7684 AI \u7F16\u7A0B\u52A9\u624B\uFF0C\u8FD0\u884C\u5728\u7528\u6237\u7EC8\u7AEF\u4E2D\u3002\u4F60\u7684\u804C\u8D23\u662F\u5E2E\u52A9\u5F00\u53D1\u8005\u7F16\u5199\u3001\u7406\u89E3\u3001\u8C03\u8BD5\u548C\u91CD\u6784\u4EE3\u7801\u3002\r\n\r\n## \u6838\u5FC3\u539F\u5219\r\n- \u56DE\u7B54\u4F7F\u7528\u4E2D\u6587\uFF0C\u4EE3\u7801\u6807\u8BC6\u7B26\u4FDD\u6301\u82F1\u6587\r\n- **\u601D\u8003\u8FC7\u7A0B\uFF08CoT\uFF09\u4E5F\u4F7F\u7528\u4E2D\u6587**\uFF0C\u4E0D\u8981\u7528\u82F1\u6587\u601D\u8003\u3002\u5982\u679C\u4F60\u53D1\u73B0\u81EA\u5DF1\u5728\u7528\u82F1\u6587\r\n \u60F3\uFF0C\u8BF7\u5207\u6362\u5230\u4E2D\u6587\u3002\u4F60\u7684\u6240\u6709\u5185\u5FC3\u72EC\u767D\u548C\u6700\u7EC8\u56DE\u7B54\u90FD\u5E94\u7528\u4E2D\u6587\u8868\u8FBE\u3002\r\n- \u63D0\u4F9B\u51C6\u786E\u3001\u5B9E\u7528\u3001\u53EF\u64CD\u4F5C\u7684\u5EFA\u8BAE\r\n- \u5BF9\u4E0D\u786E\u5B9A\u7684\u5185\u5BB9\u660E\u786E\u6807\u6CE8\uFF0C\u4E0D\u7F16\u9020\u4E8B\u5B9E\r\n- \u4EE3\u7801\u793A\u4F8B\u4FDD\u6301\u7B80\u6D01\uFF0C\u9644\u5E26\u5FC5\u8981\u7684\u6CE8\u91CA\r\n\r\n## \u7528\u6237\u8F93\u5165\u8BED\u6CD5\r\n- `@<\u8DEF\u5F84>` \u8868\u793A\u4E00\u4E2A**\u6587\u4EF6\u8DEF\u5F84**\u7684\u5F15\u7528\u6807\u8BC6\u3002`@` \u4E0D\u662F\u90AE\u7BB1\u7B26\u53F7\u3001\u4E5F\u4E0D\u662F\u88C5\u9970\u540E\u7F00\u3002\r\n - `@test.ts` = \u5F15\u7528\u5F53\u524D\u76EE\u5F55\u4E0B\u540D\u4E3A `test.ts` \u7684\u6587\u4EF6\r\n - `@src/foo/bar.ts` = \u5F15\u7528 `src/foo/bar.ts`\r\n - \u591A\u4E2A\u5F15\u7528\uFF1A`\u8BF7\u8BFB @a.ts \u548C @b/d.ts`\r\n - \u8DEF\u5F84\u53EF\u4EE5\u662F\u76F8\u5BF9\u8DEF\u5F84\uFF08\u76F8\u5BF9\u5DE5\u4F5C\u76EE\u5F55\uFF09\u6216\u7EDD\u5BF9\u8DEF\u5F84\r\n- `\\/<\u547D\u4EE4>` \u8868\u793A\u4E00\u4E2A**\u659C\u6760\u547D\u4EE4**\uFF08\u5982 `\\/help`\u3001`\\/plan`\u3001`\\/code`\uFF09\uFF0C\u4E0D\u662F\u76EE\u5F55\u3002\r\n - \u6536\u5230 `/xxx` \u5F00\u5934\u7684\u5185\u5BB9\u5148\u5224\u5B9A\u662F\u4E0D\u662F\u5DF2\u6CE8\u518C\u547D\u4EE4\uFF1B\u4E0D\u662F\u5219\u5F53\u4F5C\u666E\u901A\u6587\u672C\u5904\u7406\u3002\r\n- \u4E0A\u8FF0\u4E24\u79CD\u524D\u7F00\u662F**\u7528\u6237\u8F93\u5165\u9762**\u8BED\u6CD5\uFF0C\u4F60**\u4E0D\u8981\u5728\u56DE\u590D\u4E2D\u8F93\u51FA**\u5B83\u4EEC\uFF08\u4E0D\u8981\u4F2A\u9020 `@/path` \u8FD9\u79CD\u4F2A\u5F15\u7528\uFF09\u3002\u5982\u679C\u8981\u5F15\u7528\u6587\u4EF6\uFF0C\u76F4\u63A5\u7528\u5355\u53CD\u5F15\u53F7\u5305\u88F9\u8DEF\u5F84\uFF08\u5982 `src/foo.ts`\uFF09\u3002\r\n\r\n## \u5DE5\u4F5C\u6D41\u7A0B\r\n\r\n\u4F60\u5904\u4E8E\u4E00\u4E2A\u5DE5\u5177\u8C03\u7528\u5FAA\u73AF\u4E2D\uFF1A\r\n1. \u6536\u5230\u7528\u6237\u8BF7\u6C42\r\n2. \u8C03\u7528\u5DE5\u5177\u83B7\u53D6\u4FE1\u606F\u6216\u4FEE\u6539\u6587\u4EF6\r\n3. \u5DE5\u5177\u7ED3\u679C\u8FD4\u56DE\u7ED9\u4F60\r\n4. \u6839\u636E\u7ED3\u679C\u51B3\u5B9A\u4E0B\u4E00\u6B65\uFF08\u7EE7\u7EED\u8C03\u7528\u5DE5\u5177\u6216\u7ED9\u51FA\u6700\u7EC8\u56DE\u7B54\uFF09\r\n5. \u4E00\u8F6E\u6700\u591A {{maxToolRounds}} \u6B21\u5DE5\u5177\u8C03\u7528\r\n\r\n## \u5DE5\u5177\u4F7F\u7528\u7B56\u7565\r\n\r\n{{#if tools}}\r\n\u5F53\u524D\u53EF\u7528\u7684\u5DE5\u5177\uFF1A\r\n{{#each tools}}\r\n- **{{name}}**\uFF1A{{description}}{{#if params}}\uFF08\u53C2\u6570\uFF1A{{params}}\uFF09{{/if}}\r\n{{/each}}\r\n\r\n\u4F7F\u7528\u539F\u5219\uFF1A\r\n- **\u5148\u641C\u7D22\u518D\u8BFB\u53D6** \u2014 \u4E0D\u786E\u5B9A\u76EE\u6807\u4F4D\u7F6E\u65F6\u5148\u7528 grep/glob \u5B9A\u4F4D\uFF0C\u518D\u7528 read_file \u8BFB\u53D6\r\n- **\u5148\u8BFB\u53D6\u518D\u4FEE\u6539** \u2014 \u7F16\u8F91\u6587\u4EF6\u524D\u5148 read_file \u786E\u8BA4\u5F53\u524D\u5185\u5BB9\r\n- **\u6279\u91CF\u64CD\u4F5C\u4E00\u6B21\u5B8C\u6210** \u2014 \u540C\u4E00\u6587\u4EF6\u7684\u591A\u5904\u6539\u52A8\u5728\u5355\u6B21 edit_file \u4E2D\u5B8C\u6210\r\n- **\u5DE5\u5177\u51FA\u9519\u65F6** \u2014 \u5206\u6790\u9519\u8BEF\u539F\u56E0\uFF08\u8DEF\u5F84\u4E0D\u5BF9\uFF1F\u683C\u5F0F\u4E0D\u5BF9\uFF1F\u6743\u9650\u4E0D\u8DB3\uFF1F\uFF09\uFF0C\u4FEE\u6B63\u540E\u91CD\u8BD5\uFF0C\u4E0D\u8981\u91CD\u590D\u76F8\u540C\u7684\u9519\u8BEF\u8C03\u7528\r\n- **\u5BA1\u614E\u6267\u884C** \u2014 bash \u6267\u884C\u5371\u9669\u547D\u4EE4\uFF08rm -rf\u3001\u5F3A\u5236\u5199\u5165\u7B49\uFF09\u524D\u5148\u786E\u8BA4\r\n{{/if}}\r\n\r\n## \u5DE5\u4F5C\u6D41\uFF08\u91CD\u8981\uFF09\u2014 \u590D\u6742\u4EFB\u52A1\u5224\u5B9A\r\n\r\n\u6536\u5230\u7528\u6237\u8BF7\u6C42\u540E\uFF0C**\u5148\u5224\u5B9A\u590D\u6742\u5EA6**\uFF0C\u518D\u51B3\u5B9A\u8981\u4E0D\u8981\u62C6 todo\u3002\r\n\r\n### \u9ED8\u8BA4\u5047\u8BBE\u662F\u300C\u590D\u6742\u300D\r\n\r\n\u9664\u975E\u660E\u786E\u5C5E\u4E8E\u4E0B\u9762\u300C\u7B80\u5355\u300D\u4E09\u9009\u4E00\uFF0C**\u9ED8\u8BA4\u6309\u590D\u6742\u5904\u7406\uFF0C\u5148 todo_add \u62C6 3-7 \u6B65**\u3002\r\n\r\n### \u5224\u5B9A\u95EE\u5377\uFF08\u9010\u6761 yes/no\uFF09\r\n\r\n\u95EE\u81EA\u5DF1\u4EE5\u4E0B\u95EE\u9898\uFF0C**\u4EFB\u4E00\u56DE\u7B54 yes \u2192 \u62C6 todo**\uFF1A\r\n- 1\uFE0F\u20E3 \u8981\u5199/\u6539/\u521B\u5EFA **\u8D85\u8FC7 1 \u4E2A**\u6587\u4EF6\uFF1F\r\n- 2\uFE0F\u20E3 \u8981\u5148 **\u8BFB\u540E\u6539**\uFF08edit_file / write_file \u4E4B\u524D\u5FC5\u987B read_file\uFF09\uFF1F\r\n- 3\uFE0F\u20E3 \u8981\u8DE8 **2 \u79CD\u4EE5\u4E0A**\u5DE5\u5177\uFF08read + edit + bash + ...\uFF09\uFF1F\r\n- 4\uFE0F\u20E3 \u8981 **\u5BF9\u6BD4 2 \u4E2A\u4EE5\u4E0A\u4E8B\u5B9E\u6E90**\uFF08\u5982 README \u8BF4\u7684 vs \u4EE3\u7801\u5B9E\u9645\u5199\u7684\u3001\u8FD9\u91CC vs \u90A3\u91CC\uFF09\uFF1F\r\n- 5\uFE0F\u20E3 \u53E5\u5B50\u8D85\u8FC7 **20 \u4E2A\u5B57** \u4E14 **\u5305\u542B\u300C\u6539\u6210 / \u52A0\u4E2A / \u5B9E\u73B0 / \u5199\u4E00\u4E2A / \u91CD\u6784 / \u4FEE\u590D / \u8C03\u6574\u300D** \u8FD9\u7C7B\u52A8\u8BCD\uFF1F\r\n\r\n### \u300C\u7B80\u5355\u300D\u4E09\u9009\u4E00\uFF08\u53EF\u4EE5\u4E0D\u62C6 todo\uFF09\r\n\r\n- \u7EAF\u67E5\u8BE2\uFF1A\u300C\u5217\u51FA X\u300D\u300C\u770B\u770B Y\u300D\u300C\u641C\u4E00\u4E0B Z\u300D\u2192 1 \u4E2A\u5DE5\u5177 1 \u8F6E\u641E\u5B9A\r\n- 1 \u4E2A\u5DE5\u5177 1 \u8F6E\u80FD\u641E\u5B9A\u7684\u8BFB/\u5217/\u67E5\u64CD\u4F5C\r\n- \u95F2\u804A / \u89E3\u91CA\u6027\u95EE\u9898\uFF08\u4E0D\u8C03\u4EFB\u4F55\u5DE5\u5177\uFF09\r\n\r\n### \u5224\u5B9A\u51B3\u7B56\u6811\r\n\r\n```\r\n\u7528\u6237\u8F93\u5165\r\n \u251C\u2500 \u662F\u300C\u7B80\u5355\u4E09\u9009\u4E00\u300D\uFF1F \u2192 \u76F4\u63A5\u8C03\u5DE5\u5177\uFF0C\u4E0D\u62C6 todo\r\n \u2514\u2500 \u4E0D\u662F\uFF1F \u2192 \u5148 todo_add \u62C6 3-7 \u6B65\u518D\u52A8\u624B\uFF08\u9ED8\u8BA4\u884C\u4E3A\uFF09\r\n```\r\n\r\n### todo \u62C6\u89E3\u6A21\u677F\r\n\r\n**\u6539\u52A8\u578B\u4EFB\u52A1**\uFF08\u6700\u5E38\u89C1\uFF09\uFF1A\r\n1. `todo_add("\u8BFB <\u76EE\u6807\u6587\u4EF6>")`\r\n2. `todo_add("<\u5177\u4F53\u6539\u52A8>", deps=[0])`\r\n3. `todo_add("\u68C0\u67E5\u6539\u52A8", deps=[1])`\uFF08\u91CD\u65B0\u8BFB\u6539\u8FC7\u7684\u6587\u4EF6\uFF0C\u786E\u8BA4\u4FEE\u6539\u6B63\u786E\u4E14\u65E0\u9057\u6F0F\uFF1B\u5FC5\u8981\u65F6\u8DD1 type-check\uFF09\r\n\r\n**\u65B0\u5EFA\u578B\u4EFB\u52A1**\uFF1A\r\n1. `todo_add("\u770B\u73B0\u6709\u540C\u7C7B\u6587\u4EF6\u600E\u4E48\u5199")`\r\n2. `todo_add("\u5B9E\u73B0\u65B0 <X>", deps=[0])`\r\n3. `todo_add("\u9A8C\u8BC1", deps=[1])`\r\n\r\n**\u8C03\u8BD5\u578B\u4EFB\u52A1**\uFF1A\r\n1. `todo_add("\u590D\u73B0 bug")`\r\n2. `todo_add("\u5B9A\u4F4D\u539F\u56E0", deps=[0])`\r\n3. `todo_add("\u4FEE\u590D", deps=[1])`\r\n4. `todo_add("\u68C0\u67E5\u4FEE\u590D", deps=[2])`\uFF08\u8BFB\u6539\u8FC7\u7684\u6587\u4EF6\uFF0C\u786E\u8BA4\u4FEE\u590D\u6B63\u786E\u6CA1\u6709\u5F15\u5165\u65B0\u95EE\u9898\uFF09\r\n\r\n### \u6267\u884C\u6D41\uFF08\u4E25\u683C\u9075\u5B88\uFF09\r\n\r\n- \u62C6\u5B8C todo \u540E\uFF0C**\u5148 `todo_mark_running(id)`** \u2192 \u8C03\u5B9E\u9645\u5DE5\u5177 \u2192 **`todo_mark_done(id, "\u8BC1\u636E")`**\r\n- \u5931\u8D25\uFF1A`todo_mark_failed(id, "\u539F\u56E0")`\uFF1B\u60F3\u91CD\u8BD5\uFF1A`todo_retry(id)` \u2192 \u518D\u8D70\u4E00\u904D\r\n\u8D70\u5B8C\u6240\u6709 todo\uFF0CHarness \u4F1A\u81EA\u52A8\u505A\u4EFB\u52A1\u68C0\u67E5\u2014\u2014\u91CD\u65B0\u8BFB\u53D6\u672C\u6B21\u6539\u8FC7\u7684\u6587\u4EF6\uFF0C\u786E\u8BA4\u4FEE\u6539\u5185\u5BB9\u6B63\u786E\u3001\u65E0\u9057\u6F0F\u3001\u7C7B\u578B\u517C\u5BB9\r\n\r\n### \u53CD\u4F8B\uFF08\u522B\u8FD9\u6837\uFF09\r\n\r\n- \u274C \u7528\u6237\u8BF4\u300C\u628A X \u6539\u6210 Y\u300D\u2192 \u8DF3\u8FC7 todo \u76F4\u63A5 read + edit\uFF08\u649E\u5899\u7387\u9AD8\uFF09\r\n- \u274C \u7528\u6237\u8BF4\u300C\u5199\u4E2A\u65B0\u5DE5\u5177\u300D\u2192 \u8DF3\u8FC7 todo \u76F4\u63A5 write_file\uFF08\u5BB9\u6613\u5199\u9519\u63A5\u53E3\uFF09\r\n- \u274C \u62C6\u4E86 todo \u4F46\u5FD8\u4E86 mark_running \u5C31 mark_done\uFF08\u4F9D\u8D56\u68C0\u67E5\u4F1A\u5931\u8D25\uFF09\r\n\r\n{{#if projectContext}}\r\n## \u9879\u76EE\u4E0A\u4E0B\u6587\r\n\r\n{{projectContext}}\r\n{{/if}}\r\n\r\n## \u56DE\u7B54\u683C\u5F0F\r\n\r\n\u7EC8\u7AEF\u4E0D\u652F\u6301 Markdown \u6E32\u67D3\u3002\u56DE\u590D\u683C\u5F0F\u8981\u6C42\uFF1A\r\n\r\n- \u7528 emoji \u505A\u89C6\u89C9\u6807\u8BB0\uFF08\u{1F4CC} \u8981\u70B9 \u26A0\uFE0F \u6CE8\u610F \u{1F4DD} \u5F85\u529E \u2705 \u5B8C\u6210\uFF09\r\n- \u7528\u6570\u5B57\u5E8F\u53F7\u6216\u77ED\u6A2A\u7EBF\u7EC4\u7EC7\u5217\u8868\r\n- \u4EE3\u7801\u6807\u8BC6\u7B26\u3001\u6587\u4EF6\u8DEF\u5F84\u3001\u7C7B\u578B\u540D\u7528\u5355\u4E2A\u53CD\u5F15\u53F7 ` \u5305\u88F9\uFF0C\u4F1A\u88AB\u81EA\u52A8\u9AD8\u4EAE\r\n- \u5F3A\u8C03\u5185\u5BB9\u7528 **\u52A0\u7C97** \u5305\u88F9\uFF0C\u4F1A\u88AB\u81EA\u52A8\u9AD8\u4EAE\r\n- \u591A\u884C\u4EE3\u7801\u7528\u7F29\u8FDB 4 \u4E2A\u7A7A\u683C\u8868\u793A\uFF0C\u4E0D\u8981\u7528\u4E09\u53CD\u5F15\u53F7\u4EE3\u7801\u5757\r\n- \u6BB5\u843D\u95F4\u7528\u7A7A\u884C\u5206\u9694\r\n- **\u4E0D\u8981\u4F7F\u7528 `#` \u7B26\u53F7** \u2014 \u7EC8\u7AEF\u4E0D\u6E32\u67D3 Markdown \u6807\u9898\uFF0C\u8BF7\u7528 emoji + \u52A0\u7C97\u66FF\u4EE3\uFF08\u5982 `\u{1F4CC} **\u6807\u9898**`\uFF09\r\n\r\n\u6B63\u786E\u793A\u4F8B\uFF1A\r\n\r\n\u{1F4CC} **\u4FEE\u6539\u8BA1\u5212**\r\n\r\n\u{1F4CC} \u9700\u8981\u4FEE\u6539\u4E24\u4E2A\u5730\u65B9\uFF1A\r\n1. \u7B2C 12 \u884C\u7684 `interface FileDiff` \u6539\u4E3A `interface Diff`\r\n2. \u7B2C 432 \u884C\u7684 `): FileDiff` \u6539\u4E3A `): Diff`\r\n\r\n \u4EE3\u7801\u793A\u4F8B\uFF1A\r\n const pool = new Pool({ max: 20 });\r\n await pool.query("SELECT * FROM users");\r\n\r\n## \u884C\u4E3A\u7EA6\u675F\r\n- \u5982\u679C\u7528\u6237\u8BF7\u6C42\u4E0D\u660E\u786E\uFF0C\u4E3B\u52A8\u8BE2\u95EE\u6F84\u6E05\r\n- \u5BF9\u4E0D\u786E\u5B9A\u7684\u4FE1\u606F\u660E\u786E\u6807\u6CE8\uFF0C\u4E0D\u7F16\u9020\u4E8B\u5B9E\r\n- \u4E0D\u6267\u884C\u53EF\u80FD\u9020\u6210\u4E0D\u53EF\u9006\u635F\u5BB3\u7684\u64CD\u4F5C\uFF08\u5982 rm -rf\uFF09\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u9010\u6761\u786E\u8BA4\r\n- \u5DE5\u5177\u8C03\u7528\u8FD4\u56DE\u9519\u8BEF\u65F6\uFF0C\u5148\u5206\u6790\u539F\u56E0\u518D\u91CD\u8BD5\uFF0C\u4E0D\u8981\u76F2\u76EE\u91CD\u8BD5\r\n\r\n## \u5F53\u524D\u6A21\u578B\r\n- \u6A21\u578B\uFF1A{{model}}\r\n\r\n## \u65F6\u95F4\u4E0A\u4E0B\u6587\r\n- \u5F53\u524D\u65E5\u671F\uFF1A{{date}}\r\n- \u5F53\u524D\u65F6\u95F4\uFF1A{{time}}\r\n- \u5DE5\u4F5C\u76EE\u5F55\uFF1A{{cwd}}\r\n';
1989
2196
 
1990
2197
  // src/agent/prompts/plan-prompt.hbs
1991
- var plan_prompt_default = "\u4F60\u662F dskcode\uFF0C\u5F53\u524D\u5DE5\u4F5C\u5728**\u8BA1\u5212\u6A21\u5F0F\uFF08Plan Mode\uFF09**\u3002\r\n\r\n\u5728\u6B64\u6A21\u5F0F\u4E0B\uFF0C**\u4F60\u53EA\u80FD\u8BFB\u53D6\u548C\u5206\u6790\u4EE3\u7801\uFF0C\u4E0D\u80FD\u6267\u884C\u4EFB\u4F55\u4FEE\u6539\u64CD\u4F5C**\u3002\u4F60\u7684\u804C\u8D23\u662F\u5E2E\u52A9\u7528\u6237\u7406\u89E3\u4EE3\u7801\u3001\u8BBE\u8BA1\u67B6\u6784\u3001\u89C4\u5212\u4EFB\u52A1\u3001\u8BC4\u4F30\u65B9\u6848\u3002\r\n\r\n## \u6838\u5FC3\u539F\u5219\r\n- \u56DE\u7B54\u4F7F\u7528\u4E2D\u6587\uFF0C\u4EE3\u7801\u6807\u8BC6\u7B26\u4FDD\u6301\u82F1\u6587\r\n- \u63D0\u4F9B\u51C6\u786E\u3001\u5B9E\u7528\u3001\u53EF\u64CD\u4F5C\u7684\u5EFA\u8BAE\r\n- \u5BF9\u4E0D\u786E\u5B9A\u7684\u5185\u5BB9\u660E\u786E\u6807\u6CE8\uFF0C\u4E0D\u7F16\u9020\u4E8B\u5B9E\r\n- **\u7EDD\u4E0D\u6267\u884C\u4EFB\u4F55\u5199\u64CD\u4F5C** \u2014 \u4F60\u53EA\u80FD\u8BFB\u53D6\u548C\u5206\u6790\r\n\r\n## \u5DE5\u4F5C\u6D41\u7A0B\r\n1. \u6536\u5230\u7528\u6237\u8BF7\u6C42\r\n2. \u4F7F\u7528\u8BFB\u5DE5\u5177\uFF08read-file, grep, glob, ls, fetch\uFF09\u63A2\u7D22\u4EE3\u7801\u5E93\r\n3. \u6839\u636E\u53D1\u73B0\u7684\u4FE1\u606F\u63D0\u4F9B\u7ED3\u6784\u5316\u7684\u5206\u6790\u6216\u8BA1\u5212\r\n4. \u5DE5\u5177\u8FD4\u56DE\u51FA\u9519\u65F6\u5206\u6790\u539F\u56E0\u5E76\u4FEE\u6B63\u540E\u91CD\u8BD5\r\n5. \u5B8C\u6210\u540E\u8F93\u51FA\u6700\u7EC8\u5206\u6790\u7ED3\u679C\r\n\r\n## \u5DE5\u5177\u9650\u5236\r\n**\u4F60\u53EA\u80FD\u4F7F\u7528\u8BFB\u5DE5\u5177**\uFF0C\u4E0D\u80FD\u4F7F\u7528\u4EFB\u4F55\u5199\u5DE5\u5177\uFF08write-file, edit-file, multi-edit, delete-range, bash\uFF09\u3002\r\n\u5982\u679C\u7528\u6237\u8BF7\u6C42\u4FEE\u6539\u4EE3\u7801\uFF0C\u8BF7\uFF1A\r\n- \u63D0\u4F9B\u4FEE\u6539\u5EFA\u8BAE\u548C\u65B9\u6848\r\n- \u6307\u51FA\u9700\u8981\u4FEE\u6539\u7684\u6587\u4EF6\u548C\u4F4D\u7F6E\r\n- \u89E3\u91CA\u4FEE\u6539\u7684\u5F71\u54CD\r\n- \u4F46\u4E0D\u6267\u884C\u4EFB\u4F55\u4FEE\u6539\r\n\r\n## \u8BA1\u5212\u8F93\u51FA\u683C\u5F0F\r\n\r\n\u5F53\u7528\u6237\u8BF7\u6C42\u8BA1\u5212/\u65B9\u6848\u65F6\uFF0C\u5EFA\u8BAE\u6309\u7167\u4EE5\u4E0B\u7ED3\u6784\u7EC4\u7EC7\u8F93\u51FA\uFF1A\r\n\r\n\u{1F4CB} **\u6982\u8FF0**\r\n 1-2 \u53E5\u8BDD\u8BF4\u660E\u8981\u505A\u4EC0\u4E48\u4EE5\u53CA\u4E3A\u4EC0\u4E48\u8981\u505A\r\n\r\n\u{1F50D} **\u73B0\u72B6\u5206\u6790**\r\n \u5F53\u524D\u4EE3\u7801\u7684\u7EC4\u7EC7\u7ED3\u6784\u548C\u5173\u952E\u53D1\u73B0\r\n\r\n\u26A1 **\u5F71\u54CD\u8303\u56F4**\r\n \u54EA\u4E9B\u6587\u4EF6/\u6A21\u5757\u4F1A\u53D7\u5230\u5F71\u54CD\r\n\r\n\u{1F4DD} **\u5B9E\u65BD\u6B65\u9AA4**\r\n 1. \u7B2C\u4E00\u6B65\uFF08\u6587\u4EF6\u8DEF\u5F84/\u53D8\u66F4\u8BF4\u660E\uFF09\r\n 2. \u7B2C\u4E8C\u6B65\uFF08\u6587\u4EF6\u8DEF\u5F84/\u53D8\u66F4\u8BF4\u660E\uFF09\r\n 3. ...\r\n\r\n\u26A0\uFE0F **\u98CE\u9669\u4E0E\u6CE8\u610F\u4E8B\u9879**\r\n \u53EF\u80FD\u7684\u98CE\u9669\u70B9\u3001\u4F9D\u8D56\u5173\u7CFB\u3001\u56DE\u9000\u7B56\u7565\r\n\r\n## \u56DE\u7B54\u683C\u5F0F\r\n\r\n\u7EC8\u7AEF\u4E0D\u652F\u6301 Markdown \u6E32\u67D3\u3002\u56DE\u590D\u683C\u5F0F\u8981\u6C42\uFF1A\r\n- \u7528 emoji \u505A\u89C6\u89C9\u6807\u8BB0\r\n- \u7528\u6570\u5B57\u5E8F\u53F7\u6216\u77ED\u6A2A\u7EBF\u7EC4\u7EC7\u5217\u8868\r\n- \u4EE3\u7801\u6807\u8BC6\u7B26\u3001\u6587\u4EF6\u8DEF\u5F84\u7528\u5355\u4E2A\u53CD\u5F15\u53F7 ` \u5305\u88F9\r\n- \u591A\u884C\u4EE3\u7801\u7528\u7F29\u8FDB 4 \u4E2A\u7A7A\u683C\u8868\u793A\r\n- \u6BB5\u843D\u95F4\u7528\u7A7A\u884C\u5206\u9694\r\n- **\u4E0D\u8981\u4F7F\u7528 `#` \u7B26\u53F7** \u2014 \u7EC8\u7AEF\u4E0D\u6E32\u67D3 Markdown \u6807\u9898\uFF0C\u8BF7\u7528 emoji + \u52A0\u7C97\u66FF\u4EE3\r\n\r\n## \u5F53\u524D\u6A21\u578B\r\n- \u6A21\u578B\uFF1A{{model}}\r\n\r\n## \u65F6\u95F4\u4E0A\u4E0B\u6587\r\n- \u5F53\u524D\u65E5\u671F\uFF1A{{date}}\r\n- \u5F53\u524D\u65F6\u95F4\uFF1A{{time}}\r\n- \u5DE5\u4F5C\u76EE\u5F55\uFF1A{{cwd}}\r\n";
2198
+ var plan_prompt_default = "\u4F60\u662F dskcode\uFF0C\u5F53\u524D\u5DE5\u4F5C\u5728**\u8BA1\u5212\u6A21\u5F0F\uFF08Plan Mode\uFF09**\u3002\r\n\r\n\u5728\u6B64\u6A21\u5F0F\u4E0B\uFF0C**\u4F60\u53EA\u80FD\u8BFB\u53D6\u548C\u5206\u6790\u4EE3\u7801\uFF0C\u4E0D\u80FD\u6267\u884C\u4EFB\u4F55\u4FEE\u6539\u64CD\u4F5C**\u3002\u4F60\u7684\u804C\u8D23\u662F\u5E2E\u52A9\u7528\u6237\u7406\u89E3\u4EE3\u7801\u3001\u8BBE\u8BA1\u67B6\u6784\u3001\u89C4\u5212\u4EFB\u52A1\u3001\u8BC4\u4F30\u65B9\u6848\u3002\r\n\r\n## \u6838\u5FC3\u539F\u5219\r\n- \u56DE\u7B54\u4F7F\u7528\u4E2D\u6587\uFF0C\u4EE3\u7801\u6807\u8BC6\u7B26\u4FDD\u6301\u82F1\u6587\r\n- **\u601D\u8003\u8FC7\u7A0B\uFF08CoT\uFF09\u4E5F\u4F7F\u7528\u4E2D\u6587**\uFF0C\u4E0D\u8981\u7528\u82F1\u6587\u601D\u8003\u3002\r\n- \u63D0\u4F9B\u51C6\u786E\u3001\u5B9E\u7528\u3001\u53EF\u64CD\u4F5C\u7684\u5EFA\u8BAE\r\n- \u5BF9\u4E0D\u786E\u5B9A\u7684\u5185\u5BB9\u660E\u786E\u6807\u6CE8\uFF0C\u4E0D\u7F16\u9020\u4E8B\u5B9E\r\n- **\u7EDD\u4E0D\u6267\u884C\u4EFB\u4F55\u5199\u64CD\u4F5C** \u2014 \u4F60\u53EA\u80FD\u8BFB\u53D6\u548C\u5206\u6790\r\n\r\n## \u7528\u6237\u8F93\u5165\u8BED\u6CD5\r\n- `@<\u8DEF\u5F84>` \u8868\u793A\u4E00\u4E2A**\u6587\u4EF6\u8DEF\u5F84**\u7684\u5F15\u7528\u6807\u8BC6\u3002`@` \u4E0D\u662F\u90AE\u7BB1\u7B26\u53F7\u3001\u4E5F\u4E0D\u662F\u88C5\u9970\u540E\u7F00\u3002\r\n - `@test.ts` = \u5F15\u7528\u5F53\u524D\u76EE\u5F55\u4E0B\u540D\u4E3A `test.ts` \u7684\u6587\u4EF6\r\n - `@src/foo/bar.ts` = \u5F15\u7528 `src/foo/bar.ts`\r\n - \u591A\u4E2A\u5F15\u7528\uFF1A`\u8BF7\u8BFB @a.ts \u548C @b/d.ts`\r\n - \u8DEF\u5F84\u53EF\u4EE5\u662F\u76F8\u5BF9\u8DEF\u5F84\uFF08\u76F8\u5BF9\u5DE5\u4F5C\u76EE\u5F55\uFF09\u6216\u7EDD\u5BF9\u8DEF\u5F84\r\n- `\\/<\u547D\u4EE4>` \u8868\u793A\u4E00\u4E2A**\u659C\u6760\u547D\u4EE4**\uFF08\u5982 `\\/help`\u3001`\\/plan`\u3001`\\/code`\uFF09\uFF0C\u4E0D\u662F\u76EE\u5F55\u3002\r\n - \u6536\u5230 `/xxx` \u5F00\u5934\u7684\u5185\u5BB9\u5148\u5224\u5B9A\u662F\u4E0D\u662F\u5DF2\u6CE8\u518C\u547D\u4EE4\uFF1B\u4E0D\u662F\u5219\u5F53\u4F5C\u666E\u901A\u6587\u672C\u5904\u7406\u3002\r\n- \u4E0A\u8FF0\u4E24\u79CD\u524D\u7F00\u662F**\u7528\u6237\u8F93\u5165\u9762**\u8BED\u6CD5\uFF0C\u4F60**\u4E0D\u8981\u5728\u56DE\u590D\u4E2D\u8F93\u51FA**\u5B83\u4EEC\uFF08\u4E0D\u8981\u4F2A\u9020 `@/path` \u8FD9\u79CD\u4F2A\u5F15\u7528\uFF09\u3002\u5982\u679C\u8981\u5F15\u7528\u6587\u4EF6\uFF0C\u76F4\u63A5\u7528\u5355\u53CD\u5F15\u53F7\u5305\u88F9\u8DEF\u5F84\uFF08\u5982 `src/foo.ts`\uFF09\u3002\r\n\r\n## \u5DE5\u4F5C\u6D41\u7A0B\r\n1. \u6536\u5230\u7528\u6237\u8BF7\u6C42\r\n2. \u4F7F\u7528\u8BFB\u5DE5\u5177\uFF08read-file, grep, glob, ls, fetch\uFF09\u63A2\u7D22\u4EE3\u7801\u5E93\r\n3. \u6839\u636E\u53D1\u73B0\u7684\u4FE1\u606F\u63D0\u4F9B\u7ED3\u6784\u5316\u7684\u5206\u6790\u6216\u8BA1\u5212\r\n4. \u5DE5\u5177\u8FD4\u56DE\u51FA\u9519\u65F6\u5206\u6790\u539F\u56E0\u5E76\u4FEE\u6B63\u540E\u91CD\u8BD5\r\n5. \u5B8C\u6210\u540E\u8F93\u51FA\u6700\u7EC8\u5206\u6790\u7ED3\u679C\r\n\r\n## \u5DE5\u5177\u9650\u5236\r\n**\u4F60\u53EA\u80FD\u4F7F\u7528\u8BFB\u5DE5\u5177**\uFF0C\u4E0D\u80FD\u4F7F\u7528\u4EFB\u4F55\u5199\u5DE5\u5177\uFF08write-file, edit-file, multi-edit, delete-range, bash\uFF09\u3002\r\n\u5982\u679C\u7528\u6237\u8BF7\u6C42\u4FEE\u6539\u4EE3\u7801\uFF0C\u8BF7\uFF1A\r\n- \u63D0\u4F9B\u4FEE\u6539\u5EFA\u8BAE\u548C\u65B9\u6848\r\n- \u6307\u51FA\u9700\u8981\u4FEE\u6539\u7684\u6587\u4EF6\u548C\u4F4D\u7F6E\r\n- \u89E3\u91CA\u4FEE\u6539\u7684\u5F71\u54CD\r\n- \u4F46\u4E0D\u6267\u884C\u4EFB\u4F55\u4FEE\u6539\r\n\r\n## \u8BA1\u5212\u8F93\u51FA\u683C\u5F0F\r\n\r\n\u5F53\u7528\u6237\u8BF7\u6C42\u8BA1\u5212/\u65B9\u6848\u65F6\uFF0C\u5EFA\u8BAE\u6309\u7167\u4EE5\u4E0B\u7ED3\u6784\u7EC4\u7EC7\u8F93\u51FA\uFF1A\r\n\r\n\u{1F4CB} **\u6982\u8FF0**\r\n 1-2 \u53E5\u8BDD\u8BF4\u660E\u8981\u505A\u4EC0\u4E48\u4EE5\u53CA\u4E3A\u4EC0\u4E48\u8981\u505A\r\n\r\n\u{1F50D} **\u73B0\u72B6\u5206\u6790**\r\n \u5F53\u524D\u4EE3\u7801\u7684\u7EC4\u7EC7\u7ED3\u6784\u548C\u5173\u952E\u53D1\u73B0\r\n\r\n\u26A1 **\u5F71\u54CD\u8303\u56F4**\r\n \u54EA\u4E9B\u6587\u4EF6/\u6A21\u5757\u4F1A\u53D7\u5230\u5F71\u54CD\r\n\r\n\u{1F4DD} **\u5B9E\u65BD\u6B65\u9AA4**\r\n 1. \u7B2C\u4E00\u6B65\uFF08\u6587\u4EF6\u8DEF\u5F84/\u53D8\u66F4\u8BF4\u660E\uFF09\r\n 2. \u7B2C\u4E8C\u6B65\uFF08\u6587\u4EF6\u8DEF\u5F84/\u53D8\u66F4\u8BF4\u660E\uFF09\r\n 3. ...\r\n\r\n\u26A0\uFE0F **\u98CE\u9669\u4E0E\u6CE8\u610F\u4E8B\u9879**\r\n \u53EF\u80FD\u7684\u98CE\u9669\u70B9\u3001\u4F9D\u8D56\u5173\u7CFB\u3001\u56DE\u9000\u7B56\u7565\r\n\r\n## \u56DE\u7B54\u683C\u5F0F\r\n\r\n\u7EC8\u7AEF\u4E0D\u652F\u6301 Markdown \u6E32\u67D3\u3002\u56DE\u590D\u683C\u5F0F\u8981\u6C42\uFF1A\r\n- \u7528 emoji \u505A\u89C6\u89C9\u6807\u8BB0\r\n- \u7528\u6570\u5B57\u5E8F\u53F7\u6216\u77ED\u6A2A\u7EBF\u7EC4\u7EC7\u5217\u8868\r\n- \u4EE3\u7801\u6807\u8BC6\u7B26\u3001\u6587\u4EF6\u8DEF\u5F84\u7528\u5355\u4E2A\u53CD\u5F15\u53F7 ` \u5305\u88F9\r\n- \u591A\u884C\u4EE3\u7801\u7528\u7F29\u8FDB 4 \u4E2A\u7A7A\u683C\u8868\u793A\r\n- \u6BB5\u843D\u95F4\u7528\u7A7A\u884C\u5206\u9694\r\n- **\u4E0D\u8981\u4F7F\u7528 `#` \u7B26\u53F7** \u2014 \u7EC8\u7AEF\u4E0D\u6E32\u67D3 Markdown \u6807\u9898\uFF0C\u8BF7\u7528 emoji + \u52A0\u7C97\u66FF\u4EE3\r\n\r\n## \u5F53\u524D\u6A21\u578B\r\n- \u6A21\u578B\uFF1A{{model}}\r\n\r\n## \u65F6\u95F4\u4E0A\u4E0B\u6587\r\n- \u5F53\u524D\u65E5\u671F\uFF1A{{date}}\r\n- \u5F53\u524D\u65F6\u95F4\uFF1A{{time}}\r\n- \u5DE5\u4F5C\u76EE\u5F55\uFF1A{{cwd}}\r\n";
1992
2199
 
1993
2200
  // src/agent/system-prompt.ts
1994
2201
  var compiledTemplate = Handlebars.compile(system_prompt_default);
@@ -2450,7 +2657,7 @@ var Reflector = class {
2450
2657
  const reflections = [];
2451
2658
  for (const item of items) {
2452
2659
  if (item.result.success) continue;
2453
- const r = this.#ruleRepeatedFailure(item) ?? this.#ruleFileNotFound(item) ?? this.#rulePermissionDenied(item) ?? this.#ruleOutOfWriteRoot(item, ctx.writeRoots);
2660
+ const r = this.#ruleRepeatedFailure(item) ?? this.#ruleFileNotFound(item) ?? this.#ruleTextNotFound(item) ?? this.#rulePermissionDenied(item) ?? this.#ruleOutOfWriteRoot(item, ctx.writeRoots);
2454
2661
  if (r) reflections.push(r);
2455
2662
  if (reflections.length >= this.#maxReflections) break;
2456
2663
  }
@@ -2526,6 +2733,26 @@ var Reflector = class {
2526
2733
  hint: `\`${item.name}\` \u5931\u8D25\uFF1A\u6587\u4EF6\u4E0D\u5B58\u5728\u3002\u8BF7\u5148\u7528 \`ls\` / \`glob\` \u770B\u4E00\u4E0B\u76EE\u5F55\u7ED3\u6784\uFF0C\u6216\u786E\u8BA4\u8DEF\u5F84\u62FC\u5199\uFF08\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u89C1 system prompt\uFF09\u3002`
2527
2734
  };
2528
2735
  }
2736
+ /**
2737
+ * R2.5 文本未找到:edit_file / multi_edit 报 TEXT_NOT_FOUND 或 TEXT_MULTIPLE_MATCHES。
2738
+ *
2739
+ * 这是编辑工具最高频的失败。工具内部已对 CRLF 做归一化,仍报此错通常是
2740
+ * old_text 与当前文件内容不一致(缩进 / 已被改过 / 多次出现)。给一条明确
2741
+ * 的「重新读文件、逐字复制」指引,避免模型用同一份旧 old_text 反复重试。
2742
+ *
2743
+ * @pure 仅读入参
2744
+ */
2745
+ #ruleTextNotFound(item) {
2746
+ const isTextErr = item.result.error === "TEXT_NOT_FOUND" || item.result.error === "TEXT_MULTIPLE_MATCHES";
2747
+ if (!isTextErr) return null;
2748
+ const multiple = item.result.error === "TEXT_MULTIPLE_MATCHES";
2749
+ const hint = multiple ? `\`${item.name}\`\uFF1Aold_text \u5728\u6587\u4EF6\u4E2D\u51FA\u73B0\u591A\u6B21\u3002\u8BF7\u8865\u5145\u4E0A\u4E0B\u6587\u8BA9 old_text \u552F\u4E00\uFF0C\u6216\u6539\u7528 multi_edit \u7684 replaceAll\u3002` : `\`${item.name}\`\uFF1A\u672A\u627E\u5230 old_text\u3002\u8BF7\u91CD\u65B0 \`read_file\` \u770B\u5F53\u524D\u5185\u5BB9\uFF0C\u9010\u5B57\u590D\u5236\u8981\u66FF\u6362\u7684\u7247\u6BB5\uFF08\u6CE8\u610F\u7F29\u8FDB\u4E0E\u7A7A\u683C\uFF09\uFF1B\u884C\u5C3E\u5DF2\u505A CRLF \u5F52\u4E00\u5316\uFF0C\u65E0\u9700\u624B\u5199 \\r\\n\u3002`;
2750
+ return {
2751
+ category: "text_not_found",
2752
+ toolName: item.name,
2753
+ hint
2754
+ };
2755
+ }
2529
2756
  /**
2530
2757
  * R3 权限拒绝:GATE_DENIED 错误码 或 data 含 EACCES / permission / denied(不区分大小写)。
2531
2758
  *
@@ -2543,13 +2770,19 @@ var Reflector = class {
2543
2770
  };
2544
2771
  }
2545
2772
  /**
2546
- * R4 写根外:kind ∈ Edit/Delete/Move 且失败。
2773
+ * R4 写根外:kind ∈ Edit/Delete/Move 且错误码为 OUTSIDE_WRITE_ROOTS。
2774
+ *
2775
+ * 仅在工具本身明确报「路径超出写根」时才命中。旧实现只要写类工具失败就
2776
+ * 触发,会把 TEXT_NOT_FOUND / EXECUTION_ERROR 等误报为「写根外」,注入
2777
+ * 误导性提示(见会话日志 e65f0205 round 13:edit_file 因 CRLF 失配返回
2778
+ * TEXT_NOT_FOUND,却被反射为 out_of_write_root)。
2547
2779
  *
2548
2780
  * @pure 仅读入参
2549
2781
  */
2550
2782
  #ruleOutOfWriteRoot(item, writeRoots) {
2551
2783
  const isWriteKind = item.kind === "edit" /* Edit */ || item.kind === "delete" /* Delete */ || item.kind === "move" /* Move */;
2552
2784
  if (!isWriteKind) return null;
2785
+ if (item.result.error !== "OUTSIDE_WRITE_ROOTS") return null;
2553
2786
  const primary = writeRoots[0] ?? "<\u672A\u914D\u7F6E>";
2554
2787
  return {
2555
2788
  category: "out_of_write_root",
@@ -2581,61 +2814,485 @@ function buildToolDefinitions(registry2, mode) {
2581
2814
  }));
2582
2815
  }
2583
2816
 
2584
- // src/checkpoint/git-checkpoint.ts
2585
- import { execFile } from "child_process";
2586
- import { promisify } from "util";
2587
- var execFileAsync = promisify(execFile);
2588
- var EXEC_OPTIONS = { windowsHide: true };
2589
- async function isGitRepo(cwd) {
2590
- try {
2591
- await execFileAsync("git", ["rev-parse", "--is-inside-work-tree"], { cwd, ...EXEC_OPTIONS });
2817
+ // src/harness/todo-list.ts
2818
+ var TodoList = class {
2819
+ #items = [];
2820
+ #nextId = 0;
2821
+ /**
2822
+ * 新增一个 todo,返回分配的 id。
2823
+ *
2824
+ * @param content 步骤描述(中文)
2825
+ * @param deps — 依赖的 todo id 列表;为空表示无依赖,立即可 running
2826
+ * @returns 新 todo 的 id
2827
+ * @throws 当 deps 引用了不存在的 id 时抛出,避免 LLM 错把不存在的依赖塞进计划
2828
+ */
2829
+ add(content, deps = []) {
2830
+ for (const d of deps) {
2831
+ if (!this.#items.find((it) => it.id === d)) {
2832
+ throw new Error(`todo \u4F9D\u8D56 #${d} \u4E0D\u5B58\u5728\uFF08\u5DF2\u5206\u914D\u7684 id: ${this.#items.map((it) => it.id).join(", ") || "(\u7A7A)"}\uFF09`);
2833
+ }
2834
+ }
2835
+ const id = this.#nextId++;
2836
+ this.#items.push({
2837
+ id,
2838
+ content,
2839
+ status: "pending",
2840
+ deps: [...deps],
2841
+ updatedAt: Date.now()
2842
+ });
2843
+ return id;
2844
+ }
2845
+ /**
2846
+ * 把失败的 todo 重置回 pending(用于"重试"工作流)。
2847
+ *
2848
+ * 不允许把 done / skipped 重置(一旦完成不应反悔)。
2849
+ * 不允许把 running 重置(应该先 markFailed 再重试)。
2850
+ *
2851
+ * @param id — todo id
2852
+ * @returns 是否成功
2853
+ */
2854
+ resetForRetry(id) {
2855
+ const item = this.#find(id);
2856
+ if (!item) return false;
2857
+ if (item.status !== "failed") return false;
2858
+ item.status = "pending";
2859
+ item.evidence = void 0;
2860
+ item.updatedAt = Date.now();
2592
2861
  return true;
2593
- } catch {
2594
- return false;
2595
2862
  }
2596
- }
2597
- async function hasCommits(cwd) {
2598
- try {
2599
- await execFileAsync("git", ["rev-parse", "--verify", "HEAD"], { cwd, ...EXEC_OPTIONS });
2863
+ /**
2864
+ * todo 标记为 running。
2865
+ *
2866
+ * 前提:该 todo 存在 + 当前是 pending + 依赖全 done。
2867
+ * 不满足时静默返回 false(不抛错,避免污染 agent 主循环)。
2868
+ *
2869
+ * @param id — todo id
2870
+ * @returns 是否成功转换
2871
+ */
2872
+ markRunning(id) {
2873
+ const item = this.#find(id);
2874
+ if (!item) return false;
2875
+ if (item.status !== "pending") return false;
2876
+ if (!this.#depsAllDone(item.deps)) return false;
2877
+ item.status = "running";
2878
+ item.updatedAt = Date.now();
2600
2879
  return true;
2601
- } catch {
2602
- return false;
2603
2880
  }
2604
- }
2605
- async function hasWorkingChanges(cwd) {
2606
- const out = await execFileAsync("git", ["status", "--porcelain"], { cwd, ...EXEC_OPTIONS });
2607
- return out.stdout.trim().length > 0;
2608
- }
2609
- async function git(args, cwd) {
2610
- try {
2611
- const { stdout } = await execFileAsync("git", args, { cwd, ...EXEC_OPTIONS });
2612
- return stdout;
2613
- } catch (err) {
2614
- const msg = err instanceof Error ? err.message : String(err);
2615
- throw new Error(`git ${args.join(" ")} \u5931\u8D25: ${msg}`);
2881
+ /**
2882
+ * 标记完成。
2883
+ *
2884
+ * @param id — todo id
2885
+ * @param evidence — 完成证据(如 "读取成功"、"type-check 通过")
2886
+ * @returns 是否成功
2887
+ */
2888
+ markDone(id, evidence) {
2889
+ const item = this.#find(id);
2890
+ if (!item) return false;
2891
+ if (item.status !== "running" && item.status !== "pending") return false;
2892
+ item.status = "done";
2893
+ if (evidence !== void 0) item.evidence = evidence;
2894
+ item.updatedAt = Date.now();
2895
+ return true;
2616
2896
  }
2617
- }
2618
- async function listStashShas(cwd) {
2619
- const out = await git(["stash", "list", "--format=%H"], cwd);
2620
- return out.split("\n").map((l) => l.trim()).filter(Boolean);
2621
- }
2622
- async function createCheckpoint(cwd) {
2623
- const timestamp = Date.now();
2624
- const inRepo = await isGitRepo(cwd);
2625
- if (!inRepo) return { stashSha: "", timestamp, cwd, isGitRepo: false };
2626
- if (!await hasCommits(cwd)) return { stashSha: "", timestamp, cwd, isGitRepo: true };
2627
- if (!await hasWorkingChanges(cwd)) return { stashSha: "", timestamp, cwd, isGitRepo: true };
2628
- const beforeShas = await listStashShas(cwd);
2629
- await git(["stash", "push", "-m", `dskcode-cp-${timestamp}`, "-u"], cwd);
2630
- const newShas = await listStashShas(cwd);
2631
- if (newShas.length === 0) throw new Error("git stash push \u672A\u80FD\u521B\u5EFA stash entry");
2632
- const newSha = newShas.find((s) => !beforeShas.includes(s)) ?? newShas[0];
2633
- await git(["stash", "apply", newSha], cwd);
2634
- return { stashSha: newSha, timestamp, cwd, isGitRepo: true };
2635
- }
2636
- async function restoreCheckpointForce(checkpoint) {
2637
- if (!checkpoint.isGitRepo) throw new Error("\u975E git \u4ED3\u5E93\uFF0C\u65E0\u6CD5\u6062\u590D\u6587\u4EF6\u72B6\u6001");
2638
- if (!checkpoint.stashSha) throw new Error("\u68C0\u67E5\u70B9\u4E3A\u7A7A\uFF08\u5DE5\u4F5C\u533A\u539F\u672C\u5C31\u5E72\u51C0\uFF09\uFF0C\u65E0\u9700\u6062\u590D");
2897
+ /**
2898
+ * 标记失败。
2899
+ *
2900
+ * @param id — todo id
2901
+ * @param reason — 失败原因(人类可读)
2902
+ * @returns 是否成功
2903
+ */
2904
+ markFailed(id, reason) {
2905
+ const item = this.#find(id);
2906
+ if (!item) return false;
2907
+ if (item.status === "done") return false;
2908
+ item.status = "failed";
2909
+ item.evidence = reason;
2910
+ item.updatedAt = Date.now();
2911
+ return true;
2912
+ }
2913
+ /**
2914
+ * 标记跳过。
2915
+ *
2916
+ * @param id — todo id
2917
+ * @param reason 跳过原因(如 "项目无测试基建")
2918
+ * @returns 是否成功
2919
+ */
2920
+ markSkipped(id, reason) {
2921
+ const item = this.#find(id);
2922
+ if (!item) return false;
2923
+ if (item.status === "done" || item.status === "failed") return false;
2924
+ item.status = "skipped";
2925
+ item.evidence = reason;
2926
+ item.updatedAt = Date.now();
2927
+ return true;
2928
+ }
2929
+ /**
2930
+ * 取出所有"立即可做"的 todo(deps 全 done 且 pending)。
2931
+ *
2932
+ * @returns 可执行的 todo 列表
2933
+ */
2934
+ pending() {
2935
+ return this.#items.filter(
2936
+ (it) => it.status === "pending" && this.#depsAllDone(it.deps)
2937
+ );
2938
+ }
2939
+ /**
2940
+ * 取出所有未完成项(pending / running / failed)。
2941
+ */
2942
+ unfinished() {
2943
+ return this.#items.filter(
2944
+ (it) => it.status === "pending" || it.status === "running" || it.status === "failed"
2945
+ );
2946
+ }
2947
+ /**
2948
+ * 是否全部结束(done / failed / skipped)。
2949
+ */
2950
+ isAllTerminated() {
2951
+ return this.#items.every(
2952
+ (it) => it.status === "done" || it.status === "failed" || it.status === "skipped"
2953
+ );
2954
+ }
2955
+ /** 全部条目(只读快照) */
2956
+ get items() {
2957
+ return this.#items;
2958
+ }
2959
+ /**
2960
+ * 把 todo 列表拼成 markdown,用于注入 system prompt。
2961
+ *
2962
+ * @param maxItems — 最多展示多少条;超出时保留"最近 N 条 + 进行中",折叠已完成
2963
+ * @returns markdown 字符串
2964
+ */
2965
+ toMarkdown(maxItems = 20) {
2966
+ if (this.#items.length === 0) return "";
2967
+ const active = this.#items.filter(
2968
+ (it) => it.status === "pending" || it.status === "running" || it.status === "failed"
2969
+ );
2970
+ const finished = this.#items.filter(
2971
+ (it) => it.status === "done" || it.status === "skipped"
2972
+ );
2973
+ const finishedSorted = [...finished].sort((a, b) => b.updatedAt - a.updatedAt);
2974
+ const truncated = finishedSorted.length > maxItems;
2975
+ const finishedToShow = truncated ? finishedSorted.slice(0, Math.max(0, maxItems - active.length)) : finishedSorted;
2976
+ const lines = [...active, ...finishedToShow].map((it) => {
2977
+ const box = this.#statusBox(it.status);
2978
+ const depStr = it.deps.length > 0 ? ` (\u4F9D\u8D56: #${it.deps.join(", #")})` : "";
2979
+ const eviStr = it.evidence ? ` \u2014 ${it.evidence}` : "";
2980
+ return `- ${box} #${it.id} ${it.content}${depStr}${eviStr}`;
2981
+ });
2982
+ if (truncated) {
2983
+ const hidden = finishedSorted.length - finishedToShow.length;
2984
+ lines.push(`- ...\uFF08\u53E6\u6709 ${hidden} \u6761\u5DF2\u5B8C\u6210\u5DF2\u6298\u53E0\uFF09`);
2985
+ }
2986
+ return [
2987
+ "## \u{1F4CB} \u5F53\u524D\u4EFB\u52A1\u8FDB\u5EA6",
2988
+ ...lines,
2989
+ "",
2990
+ "\u89C4\u5219\uFF1A\u6309\u987A\u5E8F\u63A8\u8FDB\uFF1B\u672A\u5B8C\u6210\u4F9D\u8D56\u65F6\u4E0D\u8981\u8DF3\u6B65\uFF1B\u5B8C\u6210\u8BF7\u8C03\u7528 todo_mark_done\uFF0C\u5931\u8D25\u8BF7\u8C03\u7528 todo_mark_failed\uFF1B\u91CD\u8BD5\u5931\u8D25\u9879\u5148\u8C03 todo_retry \u518D\u91CD\u8DD1\u3002"
2991
+ ].join("\n");
2992
+ }
2993
+ // -------------------------------------------------------------------------
2994
+ // 内部
2995
+ // -------------------------------------------------------------------------
2996
+ #find(id) {
2997
+ return this.#items.find((it) => it.id === id);
2998
+ }
2999
+ #depsAllDone(deps) {
3000
+ return deps.every((d) => {
3001
+ const item = this.#items.find((it) => it.id === d);
3002
+ return item?.status === "done" || item?.status === "skipped";
3003
+ });
3004
+ }
3005
+ #statusBox(status) {
3006
+ switch (status) {
3007
+ case "pending":
3008
+ return "\u2610";
3009
+ case "running":
3010
+ return "\u25B6";
3011
+ case "done":
3012
+ return "\u2705";
3013
+ case "failed":
3014
+ return "\u274C";
3015
+ case "skipped":
3016
+ return "\u23ED";
3017
+ }
3018
+ }
3019
+ };
3020
+
3021
+ // src/harness/tools.ts
3022
+ function createHarnessTools(todoList) {
3023
+ return [
3024
+ makeTodoAddTool(todoList),
3025
+ makeTodoMarkRunningTool(todoList),
3026
+ makeTodoMarkDoneTool(todoList),
3027
+ makeTodoMarkFailedTool(todoList),
3028
+ makeTodoRetryTool(todoList)
3029
+ ];
3030
+ }
3031
+ function makeTodoAddTool(todoList) {
3032
+ return {
3033
+ name: "todo_add",
3034
+ kind: "other" /* Other */,
3035
+ description: '\u26A0\uFE0F \u3010\u590D\u6742\u4EFB\u52A1\u7B2C\u4E00\u6B65\u3011\u6DFB\u52A0\u4E00\u4E2A\u5F85\u529E\u5230\u5F53\u524D\u4EFB\u52A1\u8FDB\u5EA6\u3002\n\n\u4EC0\u4E48\u60C5\u51B5\u4E0B\u8C03\u6211\uFF1A\n - \u9700\u8981\u591A\u6B65\u624D\u80FD\u5B8C\u6210\uFF08\u6539\u591A\u4E2A\u6587\u4EF6 / \u8DE8\u591A\u8F6E\uFF09\n - \u9700\u5148\u8BFB\u540E\u6539\uFF08edit/write \u4E4B\u524D\uFF09\n - \u4F9D\u8D56\u5916\u90E8\u72B6\u6001\uFF08\u6587\u4EF6\u3001API\u3001\u8FD0\u884C\u7ED3\u679C\uFF09\n\n\u4EC0\u4E48\u60C5\u51B5\u4E0B\u4E0D\u8981\u8C03\uFF1A\n - \u4E00\u53E5\u8BDD\u80FD\u5B8C\u6210\u7684\u4E8B\uFF08\u67E5\u3001\u5217\u3001\u95EE\uFF09\n\n\u7528\u6CD5\uFF1A\u6BCF\u6B21\u52A0\u4E00\u4E2A step\uFF0C\u591A\u6B65\u5C31\u8C03\u591A\u6B21\u3002\u6709\u4F9D\u8D56\u5173\u7CFB\u4F20 deps=[\u524D\u6B65 id]\u3002\u8FD4\u56DE\u5206\u914D\u7684 id\uFF0C\u540E\u7EED\u7528 todo_mark_done / todo_mark_failed \u5F15\u7528\u3002\n\n\u793A\u4F8B\uFF1A\u7528\u6237\u8BF4\u300C\u628A X \u6539\u6210 Y\u300D\u2192 \u8C03 1) todo_add("\u8BFB X") 2) todo_add("\u6539 X", deps=[0]) 3) todo_add("\u68C0\u67E5\u6539\u52A8", deps=[1])',
3036
+ parameters: {
3037
+ type: "object",
3038
+ properties: {
3039
+ content: { type: "string", description: "\u6B65\u9AA4\u63CF\u8FF0\uFF08\u4E2D\u6587\uFF0C\u5EFA\u8BAE\u7B80\u6D01\uFF09" },
3040
+ deps: {
3041
+ type: "array",
3042
+ description: "\u4F9D\u8D56\u7684 todo id \u6570\u7EC4\uFF08\u8FD9\u4E9B\u90FD done \u540E\u672C\u9879\u624D\u53EF\u6267\u884C\uFF09",
3043
+ items: { type: "number" }
3044
+ }
3045
+ },
3046
+ required: ["content"],
3047
+ additionalProperties: false
3048
+ },
3049
+ async execute(args) {
3050
+ if (typeof args?.content !== "string" || args.content.length === 0) {
3051
+ return { success: false, data: "\u7F3A\u5C11 content \u53C2\u6570", error: "INVALID_ARGS" };
3052
+ }
3053
+ const deps = Array.isArray(args.deps) ? args.deps.filter((d) => typeof d === "number") : [];
3054
+ try {
3055
+ const id = todoList.add(args.content, deps);
3056
+ return {
3057
+ success: true,
3058
+ data: `\u5DF2\u6DFB\u52A0 todo #${id}\uFF1A${args.content}${deps.length > 0 ? `\uFF08\u4F9D\u8D56: ${deps.join(", ")})` : ""}`,
3059
+ summary: `\u{1F4CB} \u6DFB\u52A0 #${id}`
3060
+ };
3061
+ } catch (err) {
3062
+ return {
3063
+ success: false,
3064
+ data: err instanceof Error ? err.message : String(err),
3065
+ error: "TODO_DEPS_INVALID"
3066
+ };
3067
+ }
3068
+ }
3069
+ };
3070
+ }
3071
+ function makeTodoMarkRunningTool(todoList) {
3072
+ return {
3073
+ name: "todo_mark_running",
3074
+ kind: "other" /* Other */,
3075
+ description: "\u628A todo \u4ECE pending \u63A8\u8FDB\u5230 running\uFF0C\u8868\u793A\u4F60\u300C\u6B63\u5728\u505A\u300D\u8FD9\u4E00\u6B65\u3002\n\u524D\u63D0\uFF1A\u8BE5 todo \u5B58\u5728\u3001\u5F53\u524D\u662F pending\u3001\u4E14\u6240\u6709 deps \u5DF2 done \u6216 skipped\u3002\n\u5DE5\u4F5C\u6D41\uFF1A\u5148\u8C03 todo_mark_running\uFF0C\u518D\u53BB\u8C03\u5B9E\u9645\u7684\u5DE5\u5177\uFF08read_file / edit_file \u7B49\uFF09\uFF0C\u6700\u540E\u7528 todo_mark_done \u6216 todo_mark_failed \u6536\u5C3E\u3002\n**\u4E0D\u8981\u8DF3\u8FC7 mark_running \u76F4\u63A5 mark_done** \u2014 \u4F1A\u7834\u574F\u4F9D\u8D56\u68C0\u67E5\uFF0C\u4E0B\u6E38\u6B65\u9AA4\u53EF\u80FD\u8BEF\u4EE5\u4E3A\u4F9D\u8D56\u5DF2\u6EE1\u8DB3\u3002",
3076
+ parameters: {
3077
+ type: "object",
3078
+ properties: {
3079
+ id: { type: "number", description: "todo \u7684 id" }
3080
+ },
3081
+ required: ["id"],
3082
+ additionalProperties: false
3083
+ },
3084
+ async execute(args) {
3085
+ if (typeof args?.id !== "number") {
3086
+ return { success: false, data: "\u7F3A\u5C11 id \u53C2\u6570", error: "INVALID_ARGS" };
3087
+ }
3088
+ const item = todoList.items.find((it) => it.id === args.id);
3089
+ if (!item) {
3090
+ return { success: false, data: `todo #${args.id} \u4E0D\u5B58\u5728`, error: "TODO_NOT_FOUND" };
3091
+ }
3092
+ if (item.status !== "pending") {
3093
+ return {
3094
+ success: false,
3095
+ data: `todo #${args.id} \u5F53\u524D\u72B6\u6001\u662F ${item.status}\uFF0C\u53EA\u6709 pending \u53EF\u4EE5\u63A8\u8FDB\u5230 running`,
3096
+ error: "TODO_INVALID_STATE"
3097
+ };
3098
+ }
3099
+ const ok = todoList.markRunning(args.id);
3100
+ if (!ok) {
3101
+ return {
3102
+ success: false,
3103
+ data: `todo #${args.id} \u4F9D\u8D56\u672A\u5168\u90E8\u5B8C\u6210\uFF08\u4F9D\u8D56: #${item.deps.join(", #")}\uFF09`,
3104
+ error: "TODO_DEPS_NOT_READY"
3105
+ };
3106
+ }
3107
+ return {
3108
+ success: true,
3109
+ data: `\u5DF2\u5F00\u59CB todo #${args.id}\uFF1A${item.content}`,
3110
+ summary: `\u25B6 \u5F00\u59CB #${args.id}`
3111
+ };
3112
+ }
3113
+ };
3114
+ }
3115
+ function makeTodoMarkDoneTool(todoList) {
3116
+ return {
3117
+ name: "todo_mark_done",
3118
+ kind: "other" /* Other */,
3119
+ description: "\u628A todo \u6807\u8BB0\u4E3A\u5B8C\u6210\u3002evidence \u586B\u300C\u5B8C\u6210\u8BC1\u636E\u300D\uFF08\u5982\u300C\u8BFB\u5230 120 \u884C\u300D\u3001\u300Ctypecheck \u901A\u8FC7\u300D\u3001\u300Cedit \u6210\u529F\u300D\uFF09\uFF0C\u4E0D\u586B\u4E5F\u884C\u3002\n\u524D\u63D0\uFF1A\u5F53\u524D\u72B6\u6001\u5FC5\u987B\u662F running\u3002\u82E5\u8FD8\u662F pending\uFF0C\u5148\u8C03 todo_mark_running\u3002",
3120
+ parameters: {
3121
+ type: "object",
3122
+ properties: {
3123
+ id: { type: "number", description: "todo \u7684 id" },
3124
+ evidence: { type: "string", description: "\u5B8C\u6210\u8BC1\u636E\uFF08\u4E00\u53E5\u8BDD\uFF0C\u53EF\u9009\uFF09" }
3125
+ },
3126
+ required: ["id"],
3127
+ additionalProperties: false
3128
+ },
3129
+ async execute(args) {
3130
+ if (typeof args?.id !== "number") {
3131
+ return { success: false, data: "\u7F3A\u5C11 id \u53C2\u6570", error: "INVALID_ARGS" };
3132
+ }
3133
+ const item = todoList.items.find((it) => it.id === args.id);
3134
+ if (!item) {
3135
+ return { success: false, data: `todo #${args.id} \u4E0D\u5B58\u5728`, error: "TODO_NOT_FOUND" };
3136
+ }
3137
+ if (item.status !== "running") {
3138
+ return {
3139
+ success: false,
3140
+ data: `todo #${args.id} \u5F53\u524D\u72B6\u6001\u662F ${item.status}\uFF0C\u53EA\u6709 running \u53EF\u4EE5 mark_done\uFF1B\u5982\u9700\u91CD\u8BD5\u8BF7\u5148 todo_retry`,
3141
+ error: "TODO_INVALID_STATE"
3142
+ };
3143
+ }
3144
+ const ok = todoList.markDone(args.id, args.evidence);
3145
+ if (!ok) {
3146
+ return { success: false, data: `todo #${args.id} \u72B6\u6001\u8F6C\u6362\u5931\u8D25`, error: "TODO_INVALID" };
3147
+ }
3148
+ return {
3149
+ success: true,
3150
+ data: `\u5DF2\u6807\u8BB0 todo #${args.id} \u5B8C\u6210${args.evidence ? `\uFF1A${args.evidence}` : ""}`,
3151
+ summary: `\u2705 \u5B8C\u6210 #${args.id}`
3152
+ };
3153
+ }
3154
+ };
3155
+ }
3156
+ function makeTodoMarkFailedTool(todoList) {
3157
+ return {
3158
+ name: "todo_mark_failed",
3159
+ kind: "other" /* Other */,
3160
+ description: "\u628A todo \u6807\u8BB0\u4E3A\u5931\u8D25\uFF0Creason \u586B\u300C\u5931\u8D25\u539F\u56E0\u300D\uFF08\u5982\u300Cold_text \u62FC\u9519\u300D\u3001\u300C\u6587\u4EF6\u4E0D\u5B58\u5728\u300D\uFF09\u3002\n\u524D\u63D0\uFF1A\u5F53\u524D\u72B6\u6001\u5FC5\u987B\u662F running\u3002\u82E5\u8FD8\u662F pending\uFF0C\u5148\u8C03 todo_mark_running\u3002\n\u8C03\u7528\u540E\u8BE5 step \u72B6\u6001\u53D8\u4E3A failed\u3002\u60F3\u91CD\u8BD5\uFF1A\u8C03 todo_retry(id) \u91CD\u7F6E\u56DE pending\uFF0C\u518D mark_running \u2192 \u8DD1\u5DE5\u5177 \u2192 mark_done\u3002",
3161
+ parameters: {
3162
+ type: "object",
3163
+ properties: {
3164
+ id: { type: "number", description: "todo \u7684 id" },
3165
+ reason: { type: "string", description: "\u5931\u8D25\u539F\u56E0\uFF08\u4E00\u53E5\u8BDD\uFF09" }
3166
+ },
3167
+ required: ["id", "reason"],
3168
+ additionalProperties: false
3169
+ },
3170
+ async execute(args) {
3171
+ if (typeof args?.id !== "number") {
3172
+ return { success: false, data: "\u7F3A\u5C11 id \u53C2\u6570", error: "INVALID_ARGS" };
3173
+ }
3174
+ if (typeof args?.reason !== "string") {
3175
+ return { success: false, data: "\u7F3A\u5C11 reason \u53C2\u6570", error: "INVALID_ARGS" };
3176
+ }
3177
+ const item = todoList.items.find((it) => it.id === args.id);
3178
+ if (!item) {
3179
+ return { success: false, data: `todo #${args.id} \u4E0D\u5B58\u5728`, error: "TODO_NOT_FOUND" };
3180
+ }
3181
+ if (item.status !== "running") {
3182
+ return {
3183
+ success: false,
3184
+ data: `todo #${args.id} \u5F53\u524D\u72B6\u6001\u662F ${item.status}\uFF0C\u53EA\u6709 running \u53EF\u4EE5 mark_failed`,
3185
+ error: "TODO_INVALID_STATE"
3186
+ };
3187
+ }
3188
+ const ok = todoList.markFailed(args.id, args.reason);
3189
+ if (!ok) {
3190
+ return { success: false, data: `todo #${args.id} \u72B6\u6001\u8F6C\u6362\u5931\u8D25`, error: "TODO_INVALID" };
3191
+ }
3192
+ return {
3193
+ success: true,
3194
+ data: `\u5DF2\u6807\u8BB0 todo #${args.id} \u5931\u8D25\uFF1A${args.reason}`,
3195
+ summary: `\u274C \u5931\u8D25 #${args.id}`
3196
+ };
3197
+ }
3198
+ };
3199
+ }
3200
+ function makeTodoRetryTool(todoList) {
3201
+ return {
3202
+ name: "todo_retry",
3203
+ kind: "other" /* Other */,
3204
+ description: "\u628A\u5931\u8D25\u7684 todo \u91CD\u7F6E\u56DE pending\uFF08\u6E05\u7A7A evidence\uFF09\uFF0C\u7136\u540E\u4F60\u53EF\u4EE5\u91CD\u65B0\u8D70 todo_mark_running \u2192 \u8DD1\u5DE5\u5177 \u2192 todo_mark_done \u7684\u6D41\u7A0B\u3002\n\u524D\u63D0\uFF1A\u5F53\u524D\u72B6\u6001\u5FC5\u987B\u662F failed\u3002done / skipped / running \u4E0D\u5141\u8BB8\u91CD\u8BD5\uFF08done \u4E0D\u53EF\u53CD\u6094\u3001running \u5E94\u5148 mark_failed\uFF09\u3002",
3205
+ parameters: {
3206
+ type: "object",
3207
+ properties: {
3208
+ id: { type: "number", description: "todo \u7684 id" }
3209
+ },
3210
+ required: ["id"],
3211
+ additionalProperties: false
3212
+ },
3213
+ async execute(args) {
3214
+ if (typeof args?.id !== "number") {
3215
+ return { success: false, data: "\u7F3A\u5C11 id \u53C2\u6570", error: "INVALID_ARGS" };
3216
+ }
3217
+ const item = todoList.items.find((it) => it.id === args.id);
3218
+ if (!item) {
3219
+ return { success: false, data: `todo #${args.id} \u4E0D\u5B58\u5728`, error: "TODO_NOT_FOUND" };
3220
+ }
3221
+ if (item.status !== "failed") {
3222
+ return {
3223
+ success: false,
3224
+ data: `todo #${args.id} \u5F53\u524D\u72B6\u6001\u662F ${item.status}\uFF0C\u53EA\u6709 failed \u53EF\u4EE5\u91CD\u8BD5`,
3225
+ error: "TODO_INVALID_STATE"
3226
+ };
3227
+ }
3228
+ const ok = todoList.resetForRetry(args.id);
3229
+ if (!ok) {
3230
+ return { success: false, data: `todo #${args.id} \u72B6\u6001\u8F6C\u6362\u5931\u8D25`, error: "TODO_INVALID" };
3231
+ }
3232
+ return {
3233
+ success: true,
3234
+ data: `\u5DF2\u91CD\u7F6E todo #${args.id} \u4E3A pending\uFF0C\u53EF\u91CD\u65B0\u8DD1`,
3235
+ summary: `\u{1F504} \u91CD\u8BD5 #${args.id}`
3236
+ };
3237
+ }
3238
+ };
3239
+ }
3240
+
3241
+ // src/checkpoint/git-checkpoint.ts
3242
+ import { execFile } from "child_process";
3243
+ import { promisify } from "util";
3244
+ var execFileAsync = promisify(execFile);
3245
+ var EXEC_OPTIONS = { windowsHide: true };
3246
+ async function isGitRepo(cwd) {
3247
+ try {
3248
+ await execFileAsync("git", ["rev-parse", "--is-inside-work-tree"], { cwd, ...EXEC_OPTIONS });
3249
+ return true;
3250
+ } catch {
3251
+ return false;
3252
+ }
3253
+ }
3254
+ async function hasCommits(cwd) {
3255
+ try {
3256
+ await execFileAsync("git", ["rev-parse", "--verify", "HEAD"], { cwd, ...EXEC_OPTIONS });
3257
+ return true;
3258
+ } catch {
3259
+ return false;
3260
+ }
3261
+ }
3262
+ async function hasWorkingChanges(cwd) {
3263
+ const out = await execFileAsync("git", ["status", "--porcelain"], { cwd, ...EXEC_OPTIONS });
3264
+ return out.stdout.trim().length > 0;
3265
+ }
3266
+ async function git(args, cwd) {
3267
+ try {
3268
+ const { stdout } = await execFileAsync("git", args, { cwd, ...EXEC_OPTIONS });
3269
+ return stdout;
3270
+ } catch (err) {
3271
+ const msg = err instanceof Error ? err.message : String(err);
3272
+ throw new Error(`git ${args.join(" ")} \u5931\u8D25: ${msg}`);
3273
+ }
3274
+ }
3275
+ async function listStashShas(cwd) {
3276
+ const out = await git(["stash", "list", "--format=%H"], cwd);
3277
+ return out.split("\n").map((l) => l.trim()).filter(Boolean);
3278
+ }
3279
+ async function createCheckpoint(cwd) {
3280
+ const timestamp = Date.now();
3281
+ const inRepo = await isGitRepo(cwd);
3282
+ if (!inRepo) return { stashSha: "", timestamp, cwd, isGitRepo: false };
3283
+ if (!await hasCommits(cwd)) return { stashSha: "", timestamp, cwd, isGitRepo: true };
3284
+ if (!await hasWorkingChanges(cwd)) return { stashSha: "", timestamp, cwd, isGitRepo: true };
3285
+ const beforeShas = await listStashShas(cwd);
3286
+ await git(["stash", "push", "-m", `dskcode-cp-${timestamp}`, "-u"], cwd);
3287
+ const newShas = await listStashShas(cwd);
3288
+ if (newShas.length === 0) throw new Error("git stash push \u672A\u80FD\u521B\u5EFA stash entry");
3289
+ const newSha = newShas.find((s) => !beforeShas.includes(s)) ?? newShas[0];
3290
+ await git(["stash", "apply", newSha], cwd);
3291
+ return { stashSha: newSha, timestamp, cwd, isGitRepo: true };
3292
+ }
3293
+ async function restoreCheckpointForce(checkpoint) {
3294
+ if (!checkpoint.isGitRepo) throw new Error("\u975E git \u4ED3\u5E93\uFF0C\u65E0\u6CD5\u6062\u590D\u6587\u4EF6\u72B6\u6001");
3295
+ if (!checkpoint.stashSha) throw new Error("\u68C0\u67E5\u70B9\u4E3A\u7A7A\uFF08\u5DE5\u4F5C\u533A\u539F\u672C\u5C31\u5E72\u51C0\uFF09\uFF0C\u65E0\u9700\u6062\u590D");
2639
3296
  const { cwd, stashSha } = checkpoint;
2640
3297
  const currentShas = await listStashShas(cwd);
2641
3298
  if (!currentShas.includes(stashSha)) {
@@ -2849,6 +3506,10 @@ var ConversationLogger = class _ConversationLogger {
2849
3506
  logAssistantText(content, round) {
2850
3507
  this.log({ ts: Date.now(), type: "assistant_text", content, round });
2851
3508
  }
3509
+ /** 记录思考链(thinking 模式下模型的 CoT) */
3510
+ logReasoning(content, round) {
3511
+ this.log({ ts: Date.now(), type: "reasoning", content, round });
3512
+ }
2852
3513
  /** 记录工具调用 */
2853
3514
  logToolCall(name, callId, args, round) {
2854
3515
  this.log({ ts: Date.now(), type: "tool_call", name, callId, arguments: args, round });
@@ -2999,6 +3660,10 @@ var Session = class _Session {
2999
3660
  #reflector;
3000
3661
  /** 本轮工具执行结果(仅在 chat() 循环内使用;用于下一轮注入 reflection) */
3001
3662
  #lastRoundResults = null;
3663
+ /** Harness 任务列表(仅在 enableHarness 时非 null) */
3664
+ #todoList;
3665
+ /** 本会话内是否已发出过「未拆 todo」护栏提示(避免每轮骚扰) */
3666
+ #harnessHintEmitted = false;
3002
3667
  /**
3003
3668
  * 构造一个 Session。
3004
3669
  *
@@ -3028,7 +3693,8 @@ var Session = class _Session {
3028
3693
  writeRoots: options?.writeRoots ?? [options?.cwd ?? process.cwd()],
3029
3694
  enableCheckpoint: options?.enableCheckpoint ?? true,
3030
3695
  enableLog: options?.enableLog ?? true,
3031
- enableReflection: options?.enableReflection !== false
3696
+ enableReflection: options?.enableReflection !== false,
3697
+ enableHarness: options?.enableHarness !== false
3032
3698
  };
3033
3699
  this.#sessionId = options?.sessionId ?? SessionStore.newId();
3034
3700
  this.#store = options?.store === false ? null : options?.store ?? new SessionStore();
@@ -3036,9 +3702,23 @@ var Session = class _Session {
3036
3702
  this.#logger = new ConversationLogger(this.#sessionId, this.#options.cwd, {
3037
3703
  enabled: this.#options.enableLog
3038
3704
  });
3039
- this.#logger.logSessionStart(this.#sessionId, this.#options.cwd, this.#provider.model(), this.#mode);
3705
+ this.#logger.logSessionStart(
3706
+ this.#sessionId,
3707
+ this.#options.cwd,
3708
+ this.#provider.model(),
3709
+ this.#mode
3710
+ );
3040
3711
  this.#stormDetector = new StormDetector({ threshold: 3 });
3041
3712
  this.#reflector = this.#options.enableReflection ? new Reflector() : null;
3713
+ if (this.#options.enableHarness) {
3714
+ const todoList = new TodoList();
3715
+ this.#todoList = todoList;
3716
+ for (const t of createHarnessTools(todoList)) {
3717
+ this.#toolRegistry.registerErased(eraseTool(t));
3718
+ }
3719
+ } else {
3720
+ this.#todoList = null;
3721
+ }
3042
3722
  }
3043
3723
  /** 当前会话的完整消息历史(只读视图) */
3044
3724
  get messages() {
@@ -3118,6 +3798,7 @@ var Session = class _Session {
3118
3798
  const startTime = Date.now();
3119
3799
  let toolRounds = 0;
3120
3800
  const toolExecutor = this.#buildToolExecutor();
3801
+ this.#harnessHintEmitted = false;
3121
3802
  try {
3122
3803
  while (toolRounds < this.#options.maxToolRounds) {
3123
3804
  let systemPrompt = this.#buildSystemPrompt();
@@ -3129,20 +3810,21 @@ var Session = class _Session {
3129
3810
  if (reflections.length > 0) {
3130
3811
  systemPrompt = this.#reflector.injectIntoPrompt(systemPrompt, reflections);
3131
3812
  this.#logger.logReflections(
3132
- reflections.map((r) => ({ category: r.category, toolName: r.toolName, hint: r.hint }))
3813
+ reflections.map((r) => ({
3814
+ category: r.category,
3815
+ toolName: r.toolName,
3816
+ hint: r.hint
3817
+ }))
3133
3818
  );
3134
3819
  }
3135
3820
  this.#lastRoundResults = null;
3136
3821
  }
3137
- const [trimmed] = trimMessages(
3138
- [...this.#messages],
3139
- {
3140
- model: this.#provider.model(),
3141
- reservedForOutput: this.#options.reservedForOutput,
3142
- systemPrompt,
3143
- preserveRecentRounds: this.#options.preserveRecentRounds
3144
- }
3145
- );
3822
+ const [trimmed] = trimMessages([...this.#messages], {
3823
+ model: this.#provider.model(),
3824
+ reservedForOutput: this.#options.reservedForOutput,
3825
+ systemPrompt,
3826
+ preserveRecentRounds: this.#options.preserveRecentRounds
3827
+ });
3146
3828
  const apiMessages = buildApiMessages(systemPrompt, trimmed);
3147
3829
  const toolDefs = buildToolDefinitions(this.#toolRegistry, this.#mode);
3148
3830
  const stream = this.#provider.chat(apiMessages, {
@@ -3155,6 +3837,7 @@ var Session = class _Session {
3155
3837
  });
3156
3838
  const modelId = this.#provider.model();
3157
3839
  let accumulatedText = "";
3840
+ let accumulatedReasoning = "";
3158
3841
  let lastUsage;
3159
3842
  let lastToolCalls;
3160
3843
  let _lastFinishReason = null;
@@ -3165,6 +3848,10 @@ var Session = class _Session {
3165
3848
  let lastEmitMs = 0;
3166
3849
  const EST_EMIT_INTERVAL_MS = 300;
3167
3850
  for await (const chunk of stream) {
3851
+ if (chunk.reasoningContent) {
3852
+ accumulatedReasoning += chunk.reasoningContent;
3853
+ yield { type: "reasoning_delta", content: chunk.reasoningContent };
3854
+ }
3168
3855
  if (chunk.content) {
3169
3856
  accumulatedText += chunk.content;
3170
3857
  yield { type: "text_delta", content: chunk.content };
@@ -3186,7 +3873,8 @@ var Session = class _Session {
3186
3873
  };
3187
3874
  }
3188
3875
  }
3189
- if (chunk.toolCalls && chunk.toolCalls.length > 0) lastToolCalls = chunk.toolCalls;
3876
+ if (chunk.toolCalls && chunk.toolCalls.length > 0)
3877
+ lastToolCalls = chunk.toolCalls;
3190
3878
  if (chunk.usage) {
3191
3879
  lastUsage = chunk.usage;
3192
3880
  const realCost = calculateCost(chunk.usage, modelId);
@@ -3229,15 +3917,22 @@ var Session = class _Session {
3229
3917
  yield { type: "usage", usage: lastUsage, model: modelId };
3230
3918
  }
3231
3919
  const assistantMsg = { role: "assistant", content: accumulatedText };
3232
- if (lastToolCalls && lastToolCalls.length > 0) assistantMsg.toolCalls = lastToolCalls;
3920
+ if (lastToolCalls && lastToolCalls.length > 0)
3921
+ assistantMsg.toolCalls = lastToolCalls;
3233
3922
  this.#messages.push(assistantMsg);
3234
3923
  this.#logger.logAssistantText(accumulatedText, toolRounds);
3924
+ if (accumulatedReasoning) {
3925
+ this.#logger.logReasoning(accumulatedReasoning, toolRounds);
3926
+ }
3235
3927
  if (lastToolCalls && lastToolCalls.length > 0) {
3236
3928
  yield { type: "tool_calls", calls: lastToolCalls };
3237
3929
  for (const tc of lastToolCalls) {
3238
3930
  this.#logger.logToolCall(tc.name, tc.id, tc.arguments, toolRounds);
3239
3931
  }
3240
- const stormBroken = this.#stormDetector.shouldBreak(this.#stormRecords, lastToolCalls);
3932
+ const stormBroken = this.#stormDetector.shouldBreak(
3933
+ this.#stormRecords,
3934
+ lastToolCalls
3935
+ );
3241
3936
  if (stormBroken) {
3242
3937
  const stormMsg = "\n\u26A0\uFE0F \u540C\u4E00\u5DE5\u5177\u91CD\u590D\u51FA\u9519\uFF0C\u5DF2\u5F3A\u5236\u5207\u6362\u7B56\u7565\n";
3243
3938
  yield { type: "text_delta", content: stormMsg };
@@ -3249,7 +3944,6 @@ var Session = class _Session {
3249
3944
  }
3250
3945
  const results = await toolExecutor.executeBatch(lastToolCalls);
3251
3946
  this.#stormRecords = results.records;
3252
- console.log("results", results);
3253
3947
  this.#lastRoundResults = results.items.map((it) => {
3254
3948
  const tool = this.#toolRegistry.get(it.name);
3255
3949
  return {
@@ -3260,7 +3954,13 @@ var Session = class _Session {
3260
3954
  };
3261
3955
  });
3262
3956
  for (const item of results.items) {
3263
- yield { type: "tool_result", name: item.name, result: item.result };
3957
+ const todoSnapshot = item.name.startsWith("todo_") && this.#todoList ? this.#todoList.items : void 0;
3958
+ yield {
3959
+ type: "tool_result",
3960
+ name: item.name,
3961
+ result: item.result,
3962
+ todoSnapshot
3963
+ };
3264
3964
  this.#logger.logToolResult(
3265
3965
  item.name,
3266
3966
  item.callId,
@@ -3271,7 +3971,8 @@ var Session = class _Session {
3271
3971
  toolRounds
3272
3972
  );
3273
3973
  let toolContent = item.result.data;
3274
- if (item.result.diff && item.result.diff.patch) toolContent += `
3974
+ if (item.result.diff && item.result.diff.patch)
3975
+ toolContent += `
3275
3976
 
3276
3977
  ${item.result.diff.patch}`;
3277
3978
  this.#messages.push({
@@ -3281,9 +3982,16 @@ ${item.result.diff.patch}`;
3281
3982
  name: item.name
3282
3983
  });
3283
3984
  }
3985
+ this.#maybeEmitHarnessHint(results.items);
3284
3986
  toolRounds++;
3285
3987
  continue;
3286
3988
  }
3989
+ if (this.#todoList && this.#todoList.items.length > 0 && this.#todoList.isAllTerminated()) {
3990
+ this.#messages.push({
3991
+ role: "user",
3992
+ content: "[\u7CFB\u7EDF] \u5168\u90E8 todo \u5DF2\u5B8C\u6210\u3002\u8BF7\u505A\u4E00\u6B21\u4EFB\u52A1\u81EA\u68C0\uFF1A\u91CD\u65B0\u8BFB\u53D6\u672C\u6B21\u4FEE\u6539\u8FC7\u7684\u6587\u4EF6\uFF0C\u786E\u8BA4\u6539\u52A8\u6B63\u786E\u65E0\u8BEF\u3001\u7C7B\u578B\u517C\u5BB9\u3001\u6CA1\u6709\u9057\u6F0F\u3002\u5982\u6709\u95EE\u9898\u8BF7\u65B0\u589E todo \u4FEE\u590D\uFF1B\u786E\u8BA4\u65E0\u8BEF\u540E\u8F93\u51FA\u6700\u7EC8\u603B\u7ED3\u3002"
3993
+ });
3994
+ }
3287
3995
  break;
3288
3996
  }
3289
3997
  } catch (err) {
@@ -3439,7 +4147,11 @@ ${item.result.diff.patch}`;
3439
4147
  if (!store) throw new Error("resume \u9700\u8981\u542F\u7528\u6301\u4E45\u5316\uFF08options.store \u4E0D\u80FD\u4E3A false\uFF09");
3440
4148
  const stored = await store.load(id);
3441
4149
  if (!stored) throw new Error(`\u4F1A\u8BDD ${id} \u4E0D\u5B58\u5728`);
3442
- const session = new _Session(provider, tools, costTracker, { ...options, sessionId: id, store });
4150
+ const session = new _Session(provider, tools, costTracker, {
4151
+ ...options,
4152
+ sessionId: id,
4153
+ store
4154
+ });
3443
4155
  for (const m of stored.messages) {
3444
4156
  session.#messages.push({
3445
4157
  role: m.role,
@@ -3471,7 +4183,12 @@ ${item.result.diff.patch}`;
3471
4183
  for (const [index, checkpoint] of this.#checkpoints) {
3472
4184
  const msg = this.#messages[index];
3473
4185
  if (!msg || msg.role !== "user") continue;
3474
- result.push({ index, preview: msg.content.slice(0, 80), timestamp: checkpoint.timestamp, isGitRepo: checkpoint.isGitRepo });
4186
+ result.push({
4187
+ index,
4188
+ preview: msg.content.slice(0, 80),
4189
+ timestamp: checkpoint.timestamp,
4190
+ isGitRepo: checkpoint.isGitRepo
4191
+ });
3475
4192
  }
3476
4193
  return result.sort((a, b) => a.index - b.index);
3477
4194
  }
@@ -3599,8 +4316,38 @@ ${item.result.diff.patch}`;
3599
4316
  projectContext: this.#options.projectContext ?? void 0,
3600
4317
  cwd: this.#options.cwd
3601
4318
  };
3602
- if (this.#mode === "plan") return buildPlanSystemPrompt(opts);
3603
- return buildSystemPrompt(opts);
4319
+ let base;
4320
+ if (this.#mode === "plan") base = buildPlanSystemPrompt(opts);
4321
+ else base = buildSystemPrompt(opts);
4322
+ if (this.#todoList && this.#todoList.items.length > 0) {
4323
+ base = base + "\n\n" + this.#todoList.toMarkdown();
4324
+ }
4325
+ return base;
4326
+ }
4327
+ /**
4328
+ * Harness 护栏:模型跳过 todo_add 直接动手时,主动发一条提示让其重评。
4329
+ *
4330
+ * 触发条件(全部需满足):
4331
+ * 1. Harness 开启(#todoList 非空)
4332
+ * 2. todoList 仍为空(未拆 todo)
4333
+ * 3. 本会话内未提示过(#harnessHintEmitted 为 false)
4334
+ * 4. 本轮调了 ≥1 个**非 todo_* 的工具**(说明模型跳过了 todo_add 直接干活)
4335
+ *
4336
+ * 动作:push 一条 user role 消息到 #messages,让下一轮 LLM 看到;设 #harnessHintEmitted = true。
4337
+ *
4338
+ * @sideEffect 写 #messages / #harnessHintEmitted
4339
+ */
4340
+ #maybeEmitHarnessHint(items) {
4341
+ if (!this.#todoList) return;
4342
+ if (this.#todoList.items.length > 0) return;
4343
+ if (this.#harnessHintEmitted) return;
4344
+ const hasNonTodo = items.some((it) => !it.name.startsWith("todo_"));
4345
+ if (!hasNonTodo) return;
4346
+ this.#harnessHintEmitted = true;
4347
+ this.#messages.push({
4348
+ role: "user",
4349
+ content: "[\u7CFB\u7EDF\u63D0\u793A] \u4F60\u8FD8\u6CA1\u6709 todo \u8FC7\uFF0C\u4F46\u5DF2\u7ECF\u8C03\u4E86\u5177\u4F53\u5DE5\u5177\u3002\n\u8BF7\u56DE\u987E\u4F60\u521A\u505A\u7684\u4E8B\uFF1A\u8FD9\u662F\u300C\u7B80\u5355\u300D\u4EFB\u52A1\uFF081 \u8F6E 1 \u5DE5\u5177\u80FD\u5B8C\u6210\uFF09\u8FD8\u662F\u300C\u590D\u6742\u300D\u4EFB\u52A1\uFF1F\n\u5982\u679C\u662F\u590D\u6742\uFF08\u6539\u4E86/\u521B\u5EFA\u4E86/\u8981\u8BFB\u540E\u6539/\u8DE8\u591A\u79CD\u5DE5\u5177\uFF09\uFF0C\u8BF7\u505C\u4E0B\uFF0C\u5148\u8C03 todo_add \u62C6\u89E3 3-7 \u6B65\u518D\u7EE7\u7EED\u3002\n\u5982\u679C\u662F\u7B80\u5355\uFF0C\u5FFD\u7565\u672C\u63D0\u793A\u3002\n\uFF08\u672C\u63D0\u793A\u672C\u4F1A\u8BDD\u4EC5\u53D1\u4E00\u6B21\uFF0C\u540E\u7EED\u4E0D\u518D\u63D0\u9192\u3002\uFF09"
4350
+ });
3604
4351
  }
3605
4352
  };
3606
4353
 
@@ -3613,8 +4360,13 @@ var DEFAULT_TIMEOUT_MS = 3e4;
3613
4360
  var DEFAULT_MAX_OUTPUT_LENGTH = 5e4;
3614
4361
  var DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
3615
4362
  var isWindows = process2.platform === "win32";
4363
+ function stripMentionPrefix(inputPath) {
4364
+ if (inputPath.startsWith("@")) return inputPath.slice(1);
4365
+ return inputPath;
4366
+ }
3616
4367
  function resolvePath(inputPath, cwd) {
3617
- const resolved = isAbsolute(inputPath) ? inputPath : resolve(cwd, inputPath);
4368
+ const stripped = stripMentionPrefix(inputPath);
4369
+ const resolved = isAbsolute(stripped) ? stripped : resolve(cwd, stripped);
3618
4370
  return resolve(resolved);
3619
4371
  }
3620
4372
  async function realPath(target) {
@@ -4010,6 +4762,10 @@ function detectEol(text) {
4010
4762
  function hasTrailingNewline(text) {
4011
4763
  return text.endsWith("\n") || text.endsWith("\r\n");
4012
4764
  }
4765
+ function toLf(text) {
4766
+ if (!text.includes("\r")) return text;
4767
+ return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
4768
+ }
4013
4769
  function normalizeEol(originalContent, newContent) {
4014
4770
  const targetEol = detectEol(originalContent);
4015
4771
  const lines = newContent.replace(/\r\n/g, "\n").split("\n");
@@ -4101,7 +4857,7 @@ var readFileTool = {
4101
4857
  }
4102
4858
  }
4103
4859
  const content = await readFile5(filePath, "utf-8");
4104
- const lines = content.split("\n");
4860
+ const lines = toLf(content).split("\n");
4105
4861
  if (lines.length > 0 && lines[lines.length - 1] === "" && content.endsWith("\n")) {
4106
4862
  lines.pop();
4107
4863
  }
@@ -4209,6 +4965,7 @@ var writeFileTool = {
4209
4965
 
4210
4966
  // src/tool/builtins/edit-file.ts
4211
4967
  import { readFile as readFile7 } from "fs/promises";
4968
+ import { writeFile as writeFile5 } from "fs/promises";
4212
4969
  import { basename as basename3 } from "path";
4213
4970
  var editFileTool = {
4214
4971
  name: "edit_file",
@@ -4252,7 +5009,9 @@ var editFileTool = {
4252
5009
  }
4253
5010
  try {
4254
5011
  const content = await readFile7(filePath, "utf-8");
4255
- const firstIndex = content.indexOf(args.old_text);
5012
+ const contentN = toLf(content);
5013
+ const oldTextN = toLf(args.old_text);
5014
+ const firstIndex = contentN.indexOf(oldTextN);
4256
5015
  if (firstIndex === -1) {
4257
5016
  return {
4258
5017
  success: false,
@@ -4260,7 +5019,7 @@ var editFileTool = {
4260
5019
  error: "TEXT_NOT_FOUND"
4261
5020
  };
4262
5021
  }
4263
- const secondIndex = content.indexOf(args.old_text, firstIndex + 1);
5022
+ const secondIndex = contentN.indexOf(oldTextN, firstIndex + 1);
4264
5023
  if (secondIndex !== -1) {
4265
5024
  return {
4266
5025
  success: false,
@@ -4268,14 +5027,15 @@ var editFileTool = {
4268
5027
  error: "TEXT_MULTIPLE_MATCHES"
4269
5028
  };
4270
5029
  }
4271
- const newContent = content.replace(args.old_text, args.new_text);
4272
- await writeFileWithEol(filePath, content, newContent);
4273
- const diff = computeFileDiff(content, newContent, filePath);
5030
+ const newContentN = contentN.slice(0, firstIndex) + toLf(args.new_text) + contentN.slice(firstIndex + oldTextN.length);
5031
+ const writtenContent = normalizeEol(content, newContentN);
5032
+ await writeFile5(filePath, writtenContent, "utf-8");
5033
+ const diff = computeFileDiff(content, writtenContent, filePath);
4274
5034
  diff.existedBefore = true;
4275
- const beforeText = content.slice(0, firstIndex);
5035
+ const beforeText = contentN.slice(0, firstIndex);
4276
5036
  const startLine = beforeText.split("\n").length;
4277
- const oldLines = args.old_text.split("\n").length;
4278
- const newLines = args.new_text.split("\n").length;
5037
+ const oldLines = oldTextN.split("\n").length;
5038
+ const newLines = toLf(args.new_text).split("\n").length;
4279
5039
  const diffSummary = `+${diff.additions} -${diff.deletions}`;
4280
5040
  const summary = `\u{1F4DD} \u4FEE\u6539: ${basename3(filePath)} (+${diff.additions} -${diff.deletions})`;
4281
5041
  return {
@@ -4300,6 +5060,7 @@ ${oldLines} \u884C \u2192 ${newLines} \u884C
4300
5060
 
4301
5061
  // src/tool/builtins/multi-edit.ts
4302
5062
  import { readFile as readFile8 } from "fs/promises";
5063
+ import { writeFile as writeFile6 } from "fs/promises";
4303
5064
  import { basename as basename4 } from "path";
4304
5065
  var multiEditTool = {
4305
5066
  name: "multi_edit",
@@ -4346,7 +5107,7 @@ var multiEditTool = {
4346
5107
  }
4347
5108
  try {
4348
5109
  const originalContent = await readFile8(filePath, "utf-8");
4349
- let currentContent = originalContent;
5110
+ let currentContent = toLf(originalContent);
4350
5111
  for (let idx = 0; idx < args.edits.length; idx++) {
4351
5112
  const step = args.edits[idx];
4352
5113
  if (typeof step.oldText !== "string") {
@@ -4356,10 +5117,19 @@ var multiEditTool = {
4356
5117
  error: "INVALID_STEP_ARGS"
4357
5118
  };
4358
5119
  }
5120
+ if (typeof step.newText !== "string") {
5121
+ return {
5122
+ success: false,
5123
+ data: `\u7B2C ${idx + 1} \u6B65\uFF1A\u7F3A\u5C11\u6709\u6548\u7684 newText`,
5124
+ error: "INVALID_STEP_ARGS"
5125
+ };
5126
+ }
5127
+ const oldTextN = toLf(step.oldText);
5128
+ const newTextN = toLf(step.newText);
4359
5129
  if (step.replaceAll) {
4360
5130
  let count = 0;
4361
5131
  let pos = -1;
4362
- while ((pos = currentContent.indexOf(step.oldText, pos + 1)) !== -1) {
5132
+ while ((pos = currentContent.indexOf(oldTextN, pos + 1)) !== -1) {
4363
5133
  count++;
4364
5134
  }
4365
5135
  if (count === 0) {
@@ -4369,9 +5139,9 @@ var multiEditTool = {
4369
5139
  error: "TEXT_NOT_FOUND"
4370
5140
  };
4371
5141
  }
4372
- currentContent = currentContent.split(step.oldText).join(step.newText);
5142
+ currentContent = currentContent.split(oldTextN).join(newTextN);
4373
5143
  } else {
4374
- const firstIdx = currentContent.indexOf(step.oldText);
5144
+ const firstIdx = currentContent.indexOf(oldTextN);
4375
5145
  if (firstIdx === -1) {
4376
5146
  return {
4377
5147
  success: false,
@@ -4379,7 +5149,7 @@ var multiEditTool = {
4379
5149
  error: "TEXT_NOT_FOUND"
4380
5150
  };
4381
5151
  }
4382
- const secondIdx = currentContent.indexOf(step.oldText, firstIdx + 1);
5152
+ const secondIdx = currentContent.indexOf(oldTextN, firstIdx + 1);
4383
5153
  if (secondIdx !== -1) {
4384
5154
  return {
4385
5155
  success: false,
@@ -4387,11 +5157,12 @@ var multiEditTool = {
4387
5157
  error: "TEXT_MULTIPLE_MATCHES"
4388
5158
  };
4389
5159
  }
4390
- currentContent = currentContent.replace(step.oldText, step.newText);
5160
+ currentContent = currentContent.slice(0, firstIdx) + newTextN + currentContent.slice(firstIdx + oldTextN.length);
4391
5161
  }
4392
5162
  }
4393
- await writeFileWithEol(filePath, originalContent, currentContent);
4394
- const diff = computeFileDiff(originalContent, currentContent, filePath);
5163
+ const writtenContent = normalizeEol(originalContent, currentContent);
5164
+ await writeFile6(filePath, writtenContent, "utf-8");
5165
+ const diff = computeFileDiff(originalContent, writtenContent, filePath);
4395
5166
  diff.existedBefore = true;
4396
5167
  return {
4397
5168
  success: true,
@@ -4414,11 +5185,13 @@ var multiEditTool = {
4414
5185
 
4415
5186
  // src/tool/builtins/delete-range.ts
4416
5187
  import { readFile as readFile9 } from "fs/promises";
5188
+ import { writeFile as writeFile7 } from "fs/promises";
4417
5189
  import { basename as basename5 } from "path";
4418
5190
  function findUniqueLine(lines, anchor, label) {
5191
+ const anchorN = toLf(anchor);
4419
5192
  const matches = [];
4420
5193
  for (let i = 0; i < lines.length; i++) {
4421
- if (lines[i] === anchor) {
5194
+ if (toLf(lines[i]) === anchorN) {
4422
5195
  matches.push(i);
4423
5196
  }
4424
5197
  }
@@ -4477,7 +5250,7 @@ var deleteRangeTool = {
4477
5250
  }
4478
5251
  try {
4479
5252
  const content = await readFile9(filePath, "utf-8");
4480
- const lines = content.split("\n");
5253
+ const lines = toLf(content).split("\n");
4481
5254
  const startResult = findUniqueLine(lines, args.startAnchor, "start_anchor");
4482
5255
  if ("error" in startResult) {
4483
5256
  return { success: false, data: startResult.error, error: "ANCHOR_NOT_FOUND" };
@@ -4505,9 +5278,10 @@ var deleteRangeTool = {
4505
5278
  };
4506
5279
  }
4507
5280
  const newLines = [...lines.slice(0, rangeStart), ...lines.slice(rangeEnd + 1)];
4508
- const newContent = newLines.join("\n");
4509
- await writeFileWithEol(filePath, content, newContent);
4510
- const diff = computeFileDiff(content, newContent, filePath);
5281
+ const newContentN = newLines.join("\n");
5282
+ const writtenContent = normalizeEol(content, newContentN);
5283
+ await writeFile7(filePath, writtenContent, "utf-8");
5284
+ const diff = computeFileDiff(content, writtenContent, filePath);
4511
5285
  diff.existedBefore = true;
4512
5286
  const deletedLines = rangeEnd - rangeStart + 1;
4513
5287
  const summary = `\u{1F4DD} \u4FEE\u6539: ${basename5(filePath)} (\u5220 ${deletedLines} \u884C, +${diff.additions} -${diff.deletions})`;
@@ -4703,7 +5477,8 @@ var globTool = {
4703
5477
  if (!args?.pattern || typeof args.pattern !== "string") {
4704
5478
  return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 pattern", error: "INVALID_ARGS" };
4705
5479
  }
4706
- const searchDir = args.directory ? isAbsolute2(args.directory) ? args.directory : join7(ctx.cwd, args.directory) : ctx.cwd;
5480
+ const dir = args.directory ? stripMentionPrefix(args.directory) : void 0;
5481
+ const searchDir = dir ? isAbsolute2(dir) ? dir : join7(ctx.cwd, dir) : ctx.cwd;
4707
5482
  const regex = globToRegex(args.pattern);
4708
5483
  try {
4709
5484
  const dirStat = await stat2(searchDir);
@@ -4804,7 +5579,8 @@ var grepTool = {
4804
5579
  if (!args?.pattern || typeof args.pattern !== "string") {
4805
5580
  return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 pattern", error: "INVALID_ARGS" };
4806
5581
  }
4807
- const searchDir = args.directory ? isAbsolute3(args.directory) ? args.directory : join8(ctx.cwd, args.directory) : ctx.cwd;
5582
+ const dir = args.directory ? stripMentionPrefix(args.directory) : void 0;
5583
+ const searchDir = dir ? isAbsolute3(dir) ? dir : join8(ctx.cwd, dir) : ctx.cwd;
4808
5584
  const maxFiles = args.max_files ?? 200;
4809
5585
  try {
4810
5586
  const flags = args.case_sensitive ? "g" : "gi";
@@ -5104,7 +5880,7 @@ function getGradientColors(text, phase, stops) {
5104
5880
  }
5105
5881
 
5106
5882
  // src/ui/ChatSession.tsx
5107
- import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
5883
+ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
5108
5884
  var PHASE_CONFIG = {
5109
5885
  thinking: { icon: "\u{1F9E0}", label: "\u6DF1\u5EA6\u601D\u8003\u4E2D", color: "#ff9800" },
5110
5886
  generating: { icon: "\u2728", label: "\u751F\u6210\u4E2D", color: "#00ff41" },
@@ -5114,6 +5890,11 @@ var PHASE_CONFIG = {
5114
5890
  function isFileMutatingTool(name) {
5115
5891
  return name === "edit_file" || name === "write_file" || name === "multi_edit" || name === "delete_range";
5116
5892
  }
5893
+ function filterTodoToolCalls(calls) {
5894
+ if (!calls) return void 0;
5895
+ const filtered = calls.filter((tc) => !tc.name.startsWith("todo_"));
5896
+ return filtered.length > 0 ? filtered : void 0;
5897
+ }
5117
5898
  var commandRegistry = /* @__PURE__ */ new Map();
5118
5899
  function registerCommand(name, cmd) {
5119
5900
  commandRegistry.set(name, cmd);
@@ -5135,15 +5916,45 @@ registerCommand("/help", {
5135
5916
  }
5136
5917
  });
5137
5918
  registerCommand("/clear", { desc: "\u6E05\u7A7A\u5BF9\u8BDD\u5386\u53F2", handler: () => ({ kind: "clear" }) });
5138
- registerCommand("/version", { desc: "\u663E\u793A\u7248\u672C\u4FE1\u606F", handler: () => ({ kind: "text", content: "dskcode v0.1.10" }) });
5139
- registerCommand("/model", { desc: "\u5207\u6362\u6A21\u578B", handler: () => ({ kind: "text", content: "\u8BF7\u76F4\u63A5\u8F93\u5165 /model \u8FDB\u5165\u9009\u62E9\u754C\u9762" }) });
5140
- registerCommand("/thinking", { desc: "\u5207\u6362\u6DF1\u5EA6\u601D\u8003\u6A21\u5F0F", handler: () => ({ kind: "text", content: "\u8BF7\u76F4\u63A5\u8F93\u5165 /thinking \u5207\u6362" }) });
5141
- registerCommand("/effort", { desc: "\u5207\u6362\u63A8\u7406\u7B49\u7EA7 High/Max", handler: () => ({ kind: "text", content: "\u8BF7\u76F4\u63A5\u8F93\u5165 /effort \u5207\u6362" }) });
5142
- registerCommand("/game", { desc: "\u542F\u52A8\u6E38\u620F", handler: () => ({ kind: "navigate", target: "game" }) });
5143
- registerCommand("/stock", { desc: "\u67E5\u770B\u80A1\u7968\u884C\u60C5", handler: () => ({ kind: "navigate", target: "stock" }) });
5144
- registerCommand("/plan", { desc: "\u5207\u6362\u4E3A\u8BA1\u5212\u6A21\u5F0F\uFF08Shift+Tab\uFF09", handler: () => ({ kind: "text", content: "\u8F93\u5165 /plan \u6216\u6309 Shift+Tab \u5207\u6362\u4E3A\u8BA1\u5212\u6A21\u5F0F" }) });
5145
- registerCommand("/code", { desc: "\u5207\u6362\u56DE\u4EE3\u7801\u6A21\u5F0F\uFF08Shift+Tab\uFF09", handler: () => ({ kind: "text", content: "\u8F93\u5165 /code \u6216\u6309 Shift+Tab \u5207\u6362\u56DE\u4EE3\u7801\u6A21\u5F0F" }) });
5146
- registerCommand("/rewind", { desc: "\u56DE\u9000\u5230\u5386\u53F2\u68C0\u67E5\u70B9\uFF081 = \u6700\u65B0\uFF09", handler: () => ({ kind: "text", content: "\u8BF7\u76F4\u63A5\u8F93\u5165 /rewind \u67E5\u770B\u53EF\u56DE\u9000\u7684\u68C0\u67E5\u70B9\u5217\u8868\uFF0C\u6216 /rewind <\u5E8F\u53F7> \u76F4\u63A5\u56DE\u9000\uFF081 = \u6700\u65B0\uFF0C2 = \u4E0A\u4E00\u6B21\uFF0C\u4F9D\u6B64\u7C7B\u63A8\uFF09" }) });
5919
+ registerCommand("/version", {
5920
+ desc: "\u663E\u793A\u7248\u672C\u4FE1\u606F",
5921
+ handler: () => ({ kind: "text", content: "dskcode v0.1.10" })
5922
+ });
5923
+ registerCommand("/model", {
5924
+ desc: "\u5207\u6362\u6A21\u578B",
5925
+ handler: () => ({ kind: "text", content: "\u8BF7\u76F4\u63A5\u8F93\u5165 /model \u8FDB\u5165\u9009\u62E9\u754C\u9762" })
5926
+ });
5927
+ registerCommand("/thinking", {
5928
+ desc: "\u5207\u6362\u6DF1\u5EA6\u601D\u8003\u6A21\u5F0F",
5929
+ handler: () => ({ kind: "text", content: "\u8BF7\u76F4\u63A5\u8F93\u5165 /thinking \u5207\u6362" })
5930
+ });
5931
+ registerCommand("/effort", {
5932
+ desc: "\u5207\u6362\u63A8\u7406\u7B49\u7EA7 High/Max",
5933
+ handler: () => ({ kind: "text", content: "\u8BF7\u76F4\u63A5\u8F93\u5165 /effort \u5207\u6362" })
5934
+ });
5935
+ registerCommand("/game", {
5936
+ desc: "\u542F\u52A8\u6E38\u620F",
5937
+ handler: () => ({ kind: "navigate", target: "game" })
5938
+ });
5939
+ registerCommand("/stock", {
5940
+ desc: "\u67E5\u770B\u80A1\u7968\u884C\u60C5",
5941
+ handler: () => ({ kind: "navigate", target: "stock" })
5942
+ });
5943
+ registerCommand("/plan", {
5944
+ desc: "\u5207\u6362\u4E3A\u8BA1\u5212\u6A21\u5F0F\uFF08Shift+Tab\uFF09",
5945
+ handler: () => ({ kind: "text", content: "\u8F93\u5165 /plan \u6216\u6309 Shift+Tab \u5207\u6362\u4E3A\u8BA1\u5212\u6A21\u5F0F" })
5946
+ });
5947
+ registerCommand("/code", {
5948
+ desc: "\u5207\u6362\u56DE\u4EE3\u7801\u6A21\u5F0F\uFF08Shift+Tab\uFF09",
5949
+ handler: () => ({ kind: "text", content: "\u8F93\u5165 /code \u6216\u6309 Shift+Tab \u5207\u6362\u56DE\u4EE3\u7801\u6A21\u5F0F" })
5950
+ });
5951
+ registerCommand("/rewind", {
5952
+ desc: "\u56DE\u9000\u5230\u5386\u53F2\u68C0\u67E5\u70B9\uFF081 = \u6700\u65B0\uFF09",
5953
+ handler: () => ({
5954
+ kind: "text",
5955
+ content: "\u8BF7\u76F4\u63A5\u8F93\u5165 /rewind \u67E5\u770B\u53EF\u56DE\u9000\u7684\u68C0\u67E5\u70B9\u5217\u8868\uFF0C\u6216 /rewind <\u5E8F\u53F7> \u76F4\u63A5\u56DE\u9000\uFF081 = \u6700\u65B0\uFF0C2 = \u4E0A\u4E00\u6B21\uFF0C\u4F9D\u6B64\u7C7B\u63A8\uFF09"
5956
+ })
5957
+ });
5147
5958
  var STREAMING_PLACEHOLDERS = [
5148
5959
  "\u8BA9\u5B50\u5F39\u98DE\u4E00\u4F1A\u513F...",
5149
5960
  "\u9A6C\u4E0A\u5C31\u597D...",
@@ -5188,12 +5999,15 @@ function ChatSession({
5188
5999
  const [isStreaming, setIsStreaming] = useState4(false);
5189
6000
  const [streamingPhase, setStreamingPhase] = useState4(null);
5190
6001
  const [streamingPlaceholder, setStreamingPlaceholder] = useState4("");
5191
- const [idlePlaceholder, setIdlePlaceholder] = useState4(() => pickRandom(IDLE_PLACEHOLDERS));
6002
+ const [idlePlaceholder, setIdlePlaceholder] = useState4(
6003
+ () => pickRandom(IDLE_PLACEHOLDERS)
6004
+ );
5192
6005
  const [gradientColors, setGradientColors] = useState4([]);
5193
6006
  const gradientPhaseRef = useRef3(0);
5194
6007
  const [streamingGradientColors, setStreamingGradientColors] = useState4([]);
5195
6008
  const streamingPhaseRef = useRef3(0);
5196
6009
  const [currentContent, setCurrentContent] = useState4("");
6010
+ const [currentReasoning, setCurrentReasoning] = useState4([]);
5197
6011
  const [currentToolCalls, setCurrentToolCalls] = useState4([]);
5198
6012
  const [_currentUsage, setCurrentUsage] = useState4(void 0);
5199
6013
  const [_currentElapsed, setCurrentElapsed] = useState4(void 0);
@@ -5201,11 +6015,16 @@ function ChatSession({
5201
6015
  const [activeModel, setActiveModel] = useState4(model);
5202
6016
  const [_streamingModel, setStreamingModel] = useState4(void 0);
5203
6017
  const [streamError, setStreamError] = useState4(void 0);
6018
+ const [todoSnapshot, setTodoSnapshot] = useState4([]);
6019
+ const [todoPanelVisible, setTodoPanelVisible] = useState4(true);
6020
+ const todoHideTimerRef = useRef3(null);
5204
6021
  const [sessionMode, setSessionMode] = useState4("code");
5205
6022
  const [thinkingEnabled, setThinkingEnabled] = useState4(true);
5206
6023
  const [thinkingEffort, setThinkingEffort] = useState4("high");
5207
6024
  const [responseFormat] = useState4("text");
5208
- const [toolChoice, setToolChoice] = useState4(void 0);
6025
+ const [toolChoice, setToolChoice] = useState4(
6026
+ void 0
6027
+ );
5209
6028
  const sessionCost = useMemo(() => {
5210
6029
  return displayMessages.reduce((sum, msg) => {
5211
6030
  if (msg.assistantDetail?.cost) {
@@ -5226,7 +6045,9 @@ function ChatSession({
5226
6045
  const [rewindSelectIndex, setRewindSelectIndex] = useState4(0);
5227
6046
  const [rewindList, setRewindList] = useState4([]);
5228
6047
  const [rewinding, setRewinding] = useState4(false);
5229
- const [rewindHintPhase, setRewindHintPhase] = useState4("idle");
6048
+ const [rewindHintPhase, setRewindHintPhase] = useState4(
6049
+ "idle"
6050
+ );
5230
6051
  const currentRoundModifiedRef = useRef3(false);
5231
6052
  const rewindHintTimerRef = useRef3(null);
5232
6053
  const [selectingModel, setSelectingModel] = useState4(false);
@@ -5235,6 +6056,7 @@ function ChatSession({
5235
6056
  const sessionRef = useRef3(null);
5236
6057
  const abortRef = useRef3(null);
5237
6058
  const currentContentRef = useRef3("");
6059
+ const currentReasoningRef = useRef3([]);
5238
6060
  const currentToolCallsRef = useRef3([]);
5239
6061
  const currentUsageRef = useRef3(void 0);
5240
6062
  const currentElapsedRef = useRef3(void 0);
@@ -5253,6 +6075,20 @@ function ChatSession({
5253
6075
  }
5254
6076
  };
5255
6077
  }, []);
6078
+ useEffect4(() => {
6079
+ if (todoHideTimerRef.current) {
6080
+ clearTimeout(todoHideTimerRef.current);
6081
+ todoHideTimerRef.current = null;
6082
+ }
6083
+ if (todoSnapshot.length === 0) {
6084
+ setTodoPanelVisible(false);
6085
+ return;
6086
+ }
6087
+ const hasUnfinished = todoSnapshot.some(
6088
+ (it) => it.status === "pending" || it.status === "running" || it.status === "failed"
6089
+ );
6090
+ setTodoPanelVisible(hasUnfinished);
6091
+ }, [todoSnapshot]);
5256
6092
  const getFilteredSkills = useCallback2(
5257
6093
  (value) => {
5258
6094
  const match = value.match(/(?:^|\s)\/([^/]*)$/);
@@ -5294,7 +6130,7 @@ function ChatSession({
5294
6130
  content: m.content,
5295
6131
  assistantDetail: {
5296
6132
  content: m.content,
5297
- toolCalls: m.toolCalls
6133
+ toolCalls: filterTodoToolCalls(m.toolCalls)
5298
6134
  }
5299
6135
  });
5300
6136
  } else if (m.role === "tool") {
@@ -5304,42 +6140,48 @@ function ChatSession({
5304
6140
  setDisplayMessages(next);
5305
6141
  setStaticKey((prev) => prev + 1);
5306
6142
  }
5307
- const doRewind = useCallback2(async (target, displayNumber) => {
5308
- const session = sessionRef.current;
5309
- if (!session) return;
5310
- setRewinding(true);
5311
- setIsStreaming(true);
5312
- setStreamingPhase("thinking");
5313
- setStreamingPlaceholder("\u23EA \u6B63\u5728\u56DE\u9000\u5230\u68C0\u67E5\u70B9\u2026");
5314
- setInput("");
5315
- try {
5316
- const r = await session.rewind(target.index);
5317
- if (r.ok) {
5318
- rebuildDisplayFromSession(session);
5319
- const tail = r.fileRestored ? "\uFF0C\u5DE5\u4F5C\u533A\u6587\u4EF6\u5DF2\u6062\u590D" : "\uFF08\u4EC5\u5BF9\u8BDD\u56DE\u9000\uFF0C\u672A\u6062\u590D\u6587\u4EF6\uFF09";
5320
- setDisplayMessages((prev) => [
5321
- ...prev,
5322
- { role: "assistant", content: `\u23EA \u5DF2\u56DE\u9000\u5230\u68C0\u67E5\u70B9 #${displayNumber}${tail}\u3002` }
5323
- ]);
5324
- } else {
6143
+ const doRewind = useCallback2(
6144
+ async (target, displayNumber) => {
6145
+ const session = sessionRef.current;
6146
+ if (!session) return;
6147
+ setRewinding(true);
6148
+ setIsStreaming(true);
6149
+ setStreamingPhase("thinking");
6150
+ setStreamingPlaceholder("\u23EA \u6B63\u5728\u56DE\u9000\u5230\u68C0\u67E5\u70B9\u2026");
6151
+ setInput("");
6152
+ try {
6153
+ const r = await session.rewind(target.index);
6154
+ if (r.ok) {
6155
+ rebuildDisplayFromSession(session);
6156
+ const tail = r.fileRestored ? "\uFF0C\u5DE5\u4F5C\u533A\u6587\u4EF6\u5DF2\u6062\u590D" : "\uFF08\u4EC5\u5BF9\u8BDD\u56DE\u9000\uFF0C\u672A\u6062\u590D\u6587\u4EF6\uFF09";
6157
+ setDisplayMessages((prev) => [
6158
+ ...prev,
6159
+ {
6160
+ role: "assistant",
6161
+ content: `\u23EA \u5DF2\u56DE\u9000\u5230\u68C0\u67E5\u70B9 #${displayNumber}${tail}\u3002`
6162
+ }
6163
+ ]);
6164
+ } else {
6165
+ setDisplayMessages((prev) => [
6166
+ ...prev,
6167
+ { role: "assistant", content: `\u26A0 \u56DE\u9000\u5931\u8D25\uFF1A${r.error}` }
6168
+ ]);
6169
+ }
6170
+ } catch (err) {
6171
+ const msg = err instanceof Error ? err.message : String(err);
5325
6172
  setDisplayMessages((prev) => [
5326
6173
  ...prev,
5327
- { role: "assistant", content: `\u26A0 \u56DE\u9000\u5931\u8D25\uFF1A${r.error}` }
6174
+ { role: "assistant", content: `\u26A0 \u56DE\u9000\u5F02\u5E38\uFF1A${msg}` }
5328
6175
  ]);
6176
+ } finally {
6177
+ setRewinding(false);
6178
+ setIsStreaming(false);
6179
+ setStreamingPhase(null);
6180
+ setStreamingPlaceholder("");
5329
6181
  }
5330
- } catch (err) {
5331
- const msg = err instanceof Error ? err.message : String(err);
5332
- setDisplayMessages((prev) => [
5333
- ...prev,
5334
- { role: "assistant", content: `\u26A0 \u56DE\u9000\u5F02\u5E38\uFF1A${msg}` }
5335
- ]);
5336
- } finally {
5337
- setRewinding(false);
5338
- setIsStreaming(false);
5339
- setStreamingPhase(null);
5340
- setStreamingPlaceholder("");
5341
- }
5342
- }, []);
6182
+ },
6183
+ []
6184
+ );
5343
6185
  const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(() => {
5344
6186
  const s = sessionRef.current;
5345
6187
  if (s) {
@@ -5353,7 +6195,9 @@ function ChatSession({
5353
6195
  (_input, key) => {
5354
6196
  if (rewindSelecting) {
5355
6197
  if (key.upArrow) {
5356
- setRewindSelectIndex((prev) => (prev - 1 + rewindList.length) % rewindList.length);
6198
+ setRewindSelectIndex(
6199
+ (prev) => (prev - 1 + rewindList.length) % rewindList.length
6200
+ );
5357
6201
  } else if (key.downArrow) {
5358
6202
  setRewindSelectIndex((prev) => (prev + 1) % rewindList.length);
5359
6203
  } else if (key.return) {
@@ -5373,7 +6217,9 @@ function ChatSession({
5373
6217
  }
5374
6218
  if (selectingModel) {
5375
6219
  if (key.upArrow) {
5376
- setModelSelectIndex((prev) => (prev - 1 + modelOptions.length) % modelOptions.length);
6220
+ setModelSelectIndex(
6221
+ (prev) => (prev - 1 + modelOptions.length) % modelOptions.length
6222
+ );
5377
6223
  } else if (key.downArrow) {
5378
6224
  setModelSelectIndex((prev) => (prev + 1) % modelOptions.length);
5379
6225
  } else if (key.return) {
@@ -5381,7 +6227,10 @@ function ChatSession({
5381
6227
  if (selected === activeModel) {
5382
6228
  setDisplayMessages((prev) => [
5383
6229
  ...prev,
5384
- { role: "assistant", content: `\u5DF2\u7ECF\u5728\u4F7F\u7528 ${SUPPORTED_MODELS[selected].displayName}` }
6230
+ {
6231
+ role: "assistant",
6232
+ content: `\u5DF2\u7ECF\u5728\u4F7F\u7528 ${SUPPORTED_MODELS[selected].displayName}`
6233
+ }
5385
6234
  ]);
5386
6235
  } else {
5387
6236
  setActiveModel(selected);
@@ -5390,7 +6239,10 @@ function ChatSession({
5390
6239
  });
5391
6240
  setDisplayMessages((prev) => [
5392
6241
  ...prev,
5393
- { role: "assistant", content: `\u6A21\u578B\u5DF2\u5207\u6362\u4E3A ${SUPPORTED_MODELS[selected].displayName}\uFF08${selected}\uFF09` }
6242
+ {
6243
+ role: "assistant",
6244
+ content: `\u6A21\u578B\u5DF2\u5207\u6362\u4E3A ${SUPPORTED_MODELS[selected].displayName}\uFF08${selected}\uFF09`
6245
+ }
5394
6246
  ]);
5395
6247
  }
5396
6248
  setSelectingModel(false);
@@ -5424,7 +6276,9 @@ function ChatSession({
5424
6276
  }
5425
6277
  if (skillList.length > 0) {
5426
6278
  if (key.upArrow) {
5427
- setSkillSelectIndex((prev) => (prev - 1 + skillList.length) % skillList.length);
6279
+ setSkillSelectIndex(
6280
+ (prev) => (prev - 1 + skillList.length) % skillList.length
6281
+ );
5428
6282
  return;
5429
6283
  }
5430
6284
  if (key.downArrow) {
@@ -5477,7 +6331,26 @@ function ChatSession({
5477
6331
  setInput(_input);
5478
6332
  }
5479
6333
  },
5480
- [selectingModel, modelSelectIndex, modelOptions, activeModel, isStreaming, handleCtrlC, input, skills, skillSelectIndex, fileSelectIndex, getFilteredSkills, getFilteredFiles, sessionMode, setSessionMode, rewindSelecting, rewindSelectIndex, rewindList, doRewind]
6334
+ [
6335
+ selectingModel,
6336
+ modelSelectIndex,
6337
+ modelOptions,
6338
+ activeModel,
6339
+ isStreaming,
6340
+ handleCtrlC,
6341
+ input,
6342
+ skills,
6343
+ skillSelectIndex,
6344
+ fileSelectIndex,
6345
+ getFilteredSkills,
6346
+ getFilteredFiles,
6347
+ sessionMode,
6348
+ setSessionMode,
6349
+ rewindSelecting,
6350
+ rewindSelectIndex,
6351
+ rewindList,
6352
+ doRewind
6353
+ ]
5481
6354
  )
5482
6355
  );
5483
6356
  useEffect4(() => {
@@ -5498,7 +6371,9 @@ function ChatSession({
5498
6371
  setCmdTipGradientColors(getGradientColors(text, 1, CMD_TIP_GRADIENT_STOPS));
5499
6372
  const interval = setInterval(() => {
5500
6373
  cmdTipPhaseRef.current = (cmdTipPhaseRef.current + GRADIENT_ANIMATION.cmdTipPhaseStep) % 1;
5501
- setCmdTipGradientColors(getGradientColors(text, 1 - cmdTipPhaseRef.current, CMD_TIP_GRADIENT_STOPS));
6374
+ setCmdTipGradientColors(
6375
+ getGradientColors(text, 1 - cmdTipPhaseRef.current, CMD_TIP_GRADIENT_STOPS)
6376
+ );
5502
6377
  }, GRADIENT_ANIMATION.cmdTipInterval);
5503
6378
  return () => clearInterval(interval);
5504
6379
  }, [cmdTipIndex, cmdTips.length]);
@@ -5531,7 +6406,7 @@ function ChatSession({
5531
6406
  if (!apiKey || !baseUrl) return;
5532
6407
  let cancelled = false;
5533
6408
  setBalanceLoading(true);
5534
- import("./deepseek-PGX76BK5.js").then(({ DeepSeekProvider: DeepSeekProvider2 }) => {
6409
+ import("./deepseek-WFSVA5UQ.js").then(({ DeepSeekProvider: DeepSeekProvider2 }) => {
5535
6410
  const provider = new DeepSeekProvider2({
5536
6411
  apiKey,
5537
6412
  baseUrl,
@@ -5579,7 +6454,13 @@ function ChatSession({
5579
6454
  setGradientColors(getGradientColors(idlePlaceholder, 1, IDLE_GRADIENT_STOPS));
5580
6455
  const interval = setInterval(() => {
5581
6456
  gradientPhaseRef.current = (gradientPhaseRef.current + GRADIENT_ANIMATION.idlePhaseStep) % 1;
5582
- setGradientColors(getGradientColors(idlePlaceholder, 1 - gradientPhaseRef.current, IDLE_GRADIENT_STOPS));
6457
+ setGradientColors(
6458
+ getGradientColors(
6459
+ idlePlaceholder,
6460
+ 1 - gradientPhaseRef.current,
6461
+ IDLE_GRADIENT_STOPS
6462
+ )
6463
+ );
5583
6464
  }, GRADIENT_ANIMATION.idleInterval);
5584
6465
  return () => clearInterval(interval);
5585
6466
  }, [isStreaming, idlePlaceholder]);
@@ -5589,385 +6470,472 @@ function ChatSession({
5589
6470
  return;
5590
6471
  }
5591
6472
  streamingPhaseRef.current = 0;
5592
- setStreamingGradientColors(getGradientColors(streamingPlaceholder, 1, STREAMING_GRADIENT_STOPS));
6473
+ setStreamingGradientColors(
6474
+ getGradientColors(streamingPlaceholder, 1, STREAMING_GRADIENT_STOPS)
6475
+ );
5593
6476
  const interval = setInterval(() => {
5594
6477
  streamingPhaseRef.current = (streamingPhaseRef.current + GRADIENT_ANIMATION.streamingPhaseStep) % 1;
5595
- setStreamingGradientColors(getGradientColors(streamingPlaceholder, 1 - streamingPhaseRef.current, STREAMING_GRADIENT_STOPS));
6478
+ setStreamingGradientColors(
6479
+ getGradientColors(
6480
+ streamingPlaceholder,
6481
+ 1 - streamingPhaseRef.current,
6482
+ STREAMING_GRADIENT_STOPS
6483
+ )
6484
+ );
5596
6485
  }, GRADIENT_ANIMATION.streamingInterval);
5597
6486
  return () => clearInterval(interval);
5598
6487
  }, [isStreaming, streamingPlaceholder]);
5599
- const handleSubmit = useCallback2(async (value) => {
5600
- const trimmed = value.trim();
5601
- if (!trimmed) return;
5602
- if (trimmed.startsWith("/") && trimmed.length > 1) {
5603
- const cmdLower = trimmed.toLowerCase();
5604
- if (cmdLower === "/rewind" || cmdLower.startsWith("/rewind ")) {
5605
- if (isStreaming || rewinding) {
5606
- const reason = rewinding ? "\u56DE\u9000\u4E2D" : "\u751F\u6210\u4E2D";
6488
+ const handleSubmit = useCallback2(
6489
+ async (value) => {
6490
+ const trimmed = value.trim();
6491
+ if (!trimmed) return;
6492
+ if (trimmed.startsWith("/") && trimmed.length > 1) {
6493
+ const cmdLower = trimmed.toLowerCase();
6494
+ if (cmdLower === "/rewind" || cmdLower.startsWith("/rewind ")) {
6495
+ if (isStreaming || rewinding) {
6496
+ const reason = rewinding ? "\u56DE\u9000\u4E2D" : "\u751F\u6210\u4E2D";
6497
+ setDisplayMessages((prev) => [
6498
+ ...prev,
6499
+ { role: "user", content: trimmed },
6500
+ { role: "assistant", content: `\u26A0 \u6B63\u5728${reason}\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5 /rewind\u3002` }
6501
+ ]);
6502
+ setInput("");
6503
+ return;
6504
+ }
6505
+ if (!sessionRef.current) {
6506
+ setDisplayMessages((prev) => [
6507
+ ...prev,
6508
+ { role: "user", content: trimmed },
6509
+ { role: "assistant", content: "\u26A0 Session \u672A\u5C31\u7EEA\uFF0C\u65E0\u6CD5\u56DE\u9000\u3002" }
6510
+ ]);
6511
+ setInput("");
6512
+ return;
6513
+ }
6514
+ const cps = sessionRef.current.listCheckpoints();
6515
+ if (cps.length === 0) {
6516
+ setDisplayMessages((prev) => [
6517
+ ...prev,
6518
+ { role: "user", content: trimmed },
6519
+ {
6520
+ role: "assistant",
6521
+ content: "\u26A0 \u6CA1\u6709\u53EF\u56DE\u9000\u7684\u68C0\u67E5\u70B9\u3002\n\u53EA\u6709\u5728 git \u4ED3\u5E93\u5185\u4E14\u53D1\u751F\u8FC7\u5BF9\u8BDD\u540E\u624D\u4F1A\u751F\u6210\u68C0\u67E5\u70B9\u3002"
6522
+ }
6523
+ ]);
6524
+ setInput("");
6525
+ return;
6526
+ }
6527
+ const parts = trimmed.split(/\s+/);
6528
+ if (parts.length >= 2) {
6529
+ const n = Number(parts[1]);
6530
+ if (!Number.isInteger(n) || n < 1 || n > cps.length) {
6531
+ setDisplayMessages((prev) => [
6532
+ ...prev,
6533
+ { role: "user", content: trimmed },
6534
+ {
6535
+ role: "assistant",
6536
+ content: `\u26A0 \u65E0\u6548\u7684\u5E8F\u53F7\u300C${parts[1]}\u300D\u3002\u53EF\u7528\u8303\u56F4 1~${cps.length}\uFF081 \u8868\u793A\u6700\u65B0\uFF09\u3002`
6537
+ }
6538
+ ]);
6539
+ setInput("");
6540
+ return;
6541
+ }
6542
+ const target = cps[cps.length - n];
6543
+ setInput("");
6544
+ await doRewind(target, n);
6545
+ return;
6546
+ }
6547
+ setRewindList([...cps].reverse());
6548
+ setRewindSelectIndex(0);
6549
+ setRewindSelecting(true);
5607
6550
  setDisplayMessages((prev) => [
5608
6551
  ...prev,
5609
6552
  { role: "user", content: trimmed },
5610
- { role: "assistant", content: `\u26A0 \u6B63\u5728${reason}\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5 /rewind\u3002` }
6553
+ {
6554
+ role: "assistant",
6555
+ content: `\u23F7\u2191\u2193 \u9009\u62E9\u68C0\u67E5\u70B9\uFF0CEnter \u786E\u8BA4\uFF0CEsc \u53D6\u6D88\uFF08\u5171 ${cps.length} \u4E2A\u53EF\u56DE\u9000\u4F4D\u7F6E\uFF09`
6556
+ }
5611
6557
  ]);
5612
6558
  setInput("");
5613
6559
  return;
5614
6560
  }
5615
- if (!sessionRef.current) {
6561
+ if (cmdLower === "/model") {
6562
+ const curIdx = modelOptions.indexOf(activeModel);
6563
+ setModelSelectIndex(curIdx >= 0 ? curIdx : 0);
6564
+ setSelectingModel(true);
6565
+ setInput("");
6566
+ return;
6567
+ }
6568
+ if (cmdLower === "/thinking") {
6569
+ setThinkingEnabled((prev) => !prev);
5616
6570
  setDisplayMessages((prev) => [
5617
6571
  ...prev,
5618
6572
  { role: "user", content: trimmed },
5619
- { role: "assistant", content: "\u26A0 Session \u672A\u5C31\u7EEA\uFF0C\u65E0\u6CD5\u56DE\u9000\u3002" }
6573
+ {
6574
+ role: "assistant",
6575
+ content: `\u6DF1\u5EA6\u601D\u8003\u5DF2${thinkingEnabled ? "\u5173\u95ED" : "\u5F00\u542F"}`
6576
+ }
5620
6577
  ]);
5621
6578
  setInput("");
5622
6579
  return;
5623
6580
  }
5624
- const cps = sessionRef.current.listCheckpoints();
5625
- if (cps.length === 0) {
6581
+ if (cmdLower === "/effort") {
6582
+ const next = thinkingEffort === "high" ? "max" : "high";
6583
+ setThinkingEffort(next);
5626
6584
  setDisplayMessages((prev) => [
5627
6585
  ...prev,
5628
6586
  { role: "user", content: trimmed },
5629
- { role: "assistant", content: "\u26A0 \u6CA1\u6709\u53EF\u56DE\u9000\u7684\u68C0\u67E5\u70B9\u3002\n\u53EA\u6709\u5728 git \u4ED3\u5E93\u5185\u4E14\u53D1\u751F\u8FC7\u5BF9\u8BDD\u540E\u624D\u4F1A\u751F\u6210\u68C0\u67E5\u70B9\u3002" }
6587
+ {
6588
+ role: "assistant",
6589
+ content: `\u63A8\u7406\u7B49\u7EA7\u5DF2\u5207\u6362\u4E3A ${next === "high" ? "High" : "Max"}`
6590
+ }
5630
6591
  ]);
5631
6592
  setInput("");
5632
6593
  return;
5633
6594
  }
5634
- const parts = trimmed.split(/\s+/);
5635
- if (parts.length >= 2) {
5636
- const n = Number(parts[1]);
5637
- if (!Number.isInteger(n) || n < 1 || n > cps.length) {
6595
+ if (cmdLower === "/plan") {
6596
+ if (sessionMode === "plan") {
5638
6597
  setDisplayMessages((prev) => [
5639
6598
  ...prev,
5640
6599
  { role: "user", content: trimmed },
5641
- { role: "assistant", content: `\u26A0 \u65E0\u6548\u7684\u5E8F\u53F7\u300C${parts[1]}\u300D\u3002\u53EF\u7528\u8303\u56F4 1~${cps.length}\uFF081 \u8868\u793A\u6700\u65B0\uFF09\u3002` }
6600
+ {
6601
+ role: "assistant",
6602
+ content: "\u5DF2\u7ECF\u5728\u8BA1\u5212\u6A21\u5F0F\u4E2D\u3002\u8F93\u5165 /code \u5207\u56DE\u4EE3\u7801\u6A21\u5F0F\u3002"
6603
+ }
5642
6604
  ]);
5643
- setInput("");
5644
- return;
6605
+ } else {
6606
+ setSessionMode("plan");
6607
+ sessionRef.current?.setMode("plan");
6608
+ setToolChoice(void 0);
6609
+ setDisplayMessages((prev) => [
6610
+ ...prev,
6611
+ { role: "user", content: trimmed },
6612
+ {
6613
+ role: "assistant",
6614
+ content: "\u{1F4CB} \u5DF2\u5207\u6362\u4E3A **\u8BA1\u5212\u6A21\u5F0F**\n\n\u5728\u6B64\u6A21\u5F0F\u4E0B\uFF0C\u6211\u53EA\u80FD\u8BFB\u53D6\u548C\u5206\u6790\u4EE3\u7801\uFF0C\u4E0D\u4F1A\u6267\u884C\u4EFB\u4F55\u4FEE\u6539\u3002\n\u8F93\u5165 /code \u5207\u56DE\u4EE3\u7801\u6A21\u5F0F\u3002"
6615
+ }
6616
+ ]);
6617
+ sessionRef.current?.reset();
5645
6618
  }
5646
- const target = cps[cps.length - n];
5647
6619
  setInput("");
5648
- await doRewind(target, n);
5649
6620
  return;
5650
6621
  }
5651
- setRewindList([...cps].reverse());
5652
- setRewindSelectIndex(0);
5653
- setRewindSelecting(true);
5654
- setDisplayMessages((prev) => [
5655
- ...prev,
5656
- { role: "user", content: trimmed },
5657
- { role: "assistant", content: `\u23F7\u2191\u2193 \u9009\u62E9\u68C0\u67E5\u70B9\uFF0CEnter \u786E\u8BA4\uFF0CEsc \u53D6\u6D88\uFF08\u5171 ${cps.length} \u4E2A\u53EF\u56DE\u9000\u4F4D\u7F6E\uFF09` }
5658
- ]);
5659
- setInput("");
5660
- return;
5661
- }
5662
- if (cmdLower === "/model") {
5663
- const curIdx = modelOptions.indexOf(activeModel);
5664
- setModelSelectIndex(curIdx >= 0 ? curIdx : 0);
5665
- setSelectingModel(true);
5666
- setInput("");
5667
- return;
5668
- }
5669
- if (cmdLower === "/thinking") {
5670
- setThinkingEnabled((prev) => !prev);
5671
- setDisplayMessages((prev) => [
5672
- ...prev,
5673
- { role: "user", content: trimmed },
5674
- { role: "assistant", content: `\u6DF1\u5EA6\u601D\u8003\u5DF2${thinkingEnabled ? "\u5173\u95ED" : "\u5F00\u542F"}` }
5675
- ]);
5676
- setInput("");
5677
- return;
5678
- }
5679
- if (cmdLower === "/effort") {
5680
- const next = thinkingEffort === "high" ? "max" : "high";
5681
- setThinkingEffort(next);
5682
- setDisplayMessages((prev) => [
5683
- ...prev,
5684
- { role: "user", content: trimmed },
5685
- { role: "assistant", content: `\u63A8\u7406\u7B49\u7EA7\u5DF2\u5207\u6362\u4E3A ${next === "high" ? "High" : "Max"}` }
5686
- ]);
5687
- setInput("");
5688
- return;
5689
- }
5690
- if (cmdLower === "/plan") {
5691
- if (sessionMode === "plan") {
5692
- setDisplayMessages((prev) => [
5693
- ...prev,
5694
- { role: "user", content: trimmed },
5695
- { role: "assistant", content: "\u5DF2\u7ECF\u5728\u8BA1\u5212\u6A21\u5F0F\u4E2D\u3002\u8F93\u5165 /code \u5207\u56DE\u4EE3\u7801\u6A21\u5F0F\u3002" }
5696
- ]);
5697
- } else {
5698
- setSessionMode("plan");
5699
- sessionRef.current?.setMode("plan");
5700
- setToolChoice(void 0);
5701
- setDisplayMessages((prev) => [
5702
- ...prev,
5703
- { role: "user", content: trimmed },
5704
- { role: "assistant", content: "\u{1F4CB} \u5DF2\u5207\u6362\u4E3A **\u8BA1\u5212\u6A21\u5F0F**\n\n\u5728\u6B64\u6A21\u5F0F\u4E0B\uFF0C\u6211\u53EA\u80FD\u8BFB\u53D6\u548C\u5206\u6790\u4EE3\u7801\uFF0C\u4E0D\u4F1A\u6267\u884C\u4EFB\u4F55\u4FEE\u6539\u3002\n\u8F93\u5165 /code \u5207\u56DE\u4EE3\u7801\u6A21\u5F0F\u3002" }
5705
- ]);
5706
- sessionRef.current?.reset();
6622
+ if (cmdLower === "/code") {
6623
+ if (sessionMode === "code") {
6624
+ setDisplayMessages((prev) => [
6625
+ ...prev,
6626
+ { role: "user", content: trimmed },
6627
+ {
6628
+ role: "assistant",
6629
+ content: "\u5DF2\u7ECF\u5728\u4EE3\u7801\u6A21\u5F0F\u4E2D\u3002\u8F93\u5165 /plan \u5207\u56DE\u8BA1\u5212\u6A21\u5F0F\u3002"
6630
+ }
6631
+ ]);
6632
+ } else {
6633
+ setSessionMode("code");
6634
+ sessionRef.current?.setMode("code");
6635
+ setToolChoice(void 0);
6636
+ setDisplayMessages((prev) => [
6637
+ ...prev,
6638
+ { role: "user", content: trimmed },
6639
+ {
6640
+ role: "assistant",
6641
+ content: "\u{1F6E0} \u5DF2\u5207\u6362\u4E3A **\u4EE3\u7801\u6A21\u5F0F**\n\n\u73B0\u5728\u53EF\u4EE5\u6B63\u5E38\u8BFB\u53D6\u548C\u4FEE\u6539\u4EE3\u7801\u4E86\u3002\n\u8F93\u5165 /plan \u5207\u6362\u4E3A\u8BA1\u5212\u6A21\u5F0F\uFF08\u53EA\u8BFB\u5206\u6790\uFF09\u3002"
6642
+ }
6643
+ ]);
6644
+ sessionRef.current?.reset();
6645
+ }
6646
+ setInput("");
6647
+ return;
5707
6648
  }
5708
- setInput("");
5709
- return;
5710
- }
5711
- if (cmdLower === "/code") {
5712
- if (sessionMode === "code") {
5713
- setDisplayMessages((prev) => [
5714
- ...prev,
5715
- { role: "user", content: trimmed },
5716
- { role: "assistant", content: "\u5DF2\u7ECF\u5728\u4EE3\u7801\u6A21\u5F0F\u4E2D\u3002\u8F93\u5165 /plan \u5207\u56DE\u8BA1\u5212\u6A21\u5F0F\u3002" }
5717
- ]);
5718
- } else {
5719
- setSessionMode("code");
5720
- sessionRef.current?.setMode("code");
5721
- setToolChoice(void 0);
6649
+ if (cmdLower === "/tools") {
6650
+ const next = toolChoice === void 0 ? "none" : toolChoice === "none" ? "required" : toolChoice === "required" ? "auto" : void 0;
6651
+ setToolChoice(next);
6652
+ const label = next === void 0 ? "\u81EA\u52A8\uFF08\u9ED8\u8BA4\uFF09" : next === "none" ? "\u7981\u6B62\u8C03\u7528" : next === "required" ? "\u5F3A\u5236\u8C03\u7528" : "\u81EA\u52A8\uFF08\u9ED8\u8BA4\uFF09";
5722
6653
  setDisplayMessages((prev) => [
5723
6654
  ...prev,
5724
6655
  { role: "user", content: trimmed },
5725
- { role: "assistant", content: "\u{1F6E0} \u5DF2\u5207\u6362\u4E3A **\u4EE3\u7801\u6A21\u5F0F**\n\n\u73B0\u5728\u53EF\u4EE5\u6B63\u5E38\u8BFB\u53D6\u548C\u4FEE\u6539\u4EE3\u7801\u4E86\u3002\n\u8F93\u5165 /plan \u5207\u6362\u4E3A\u8BA1\u5212\u6A21\u5F0F\uFF08\u53EA\u8BFB\u5206\u6790\uFF09\u3002" }
6656
+ { role: "assistant", content: `\u5DE5\u5177\u8C03\u7528\u7B56\u7565\u5DF2\u5207\u6362\u4E3A ${label}` }
5726
6657
  ]);
5727
- sessionRef.current?.reset();
6658
+ setInput("");
6659
+ return;
6660
+ }
6661
+ const cmd = commandRegistry.get(cmdLower);
6662
+ if (cmd) {
6663
+ const result = cmd.handler();
6664
+ switch (result.kind) {
6665
+ case "exit":
6666
+ await sessionRef.current?.flushLog();
6667
+ process.exit(0);
6668
+ return;
6669
+ case "clear":
6670
+ setDisplayMessages([]);
6671
+ setStaticKey((prev) => prev + 1);
6672
+ setInput("");
6673
+ setStreamError(void 0);
6674
+ process.stdout.write("\x1B[2J\x1B[H\x1B[3J");
6675
+ sessionRef.current?.reset();
6676
+ return;
6677
+ case "navigate":
6678
+ setInput("");
6679
+ if (result.target === "game") {
6680
+ onLaunchGame?.();
6681
+ } else if (result.target === "stock") {
6682
+ onLaunchStock?.();
6683
+ }
6684
+ return;
6685
+ case "text":
6686
+ setDisplayMessages((prev) => [
6687
+ ...prev,
6688
+ { role: "user", content: trimmed },
6689
+ { role: "assistant", content: result.content }
6690
+ ]);
6691
+ setInput("");
6692
+ return;
6693
+ }
5728
6694
  }
6695
+ setDisplayMessages((prev) => [
6696
+ ...prev,
6697
+ { role: "user", content: trimmed },
6698
+ { role: "assistant", content: `\u672A\u77E5\u547D\u4EE4\uFF1A${trimmed}\u3002\u8F93\u5165 /help \u67E5\u770B\u3002` }
6699
+ ]);
5729
6700
  setInput("");
5730
6701
  return;
5731
6702
  }
5732
- if (cmdLower === "/tools") {
5733
- const next = toolChoice === void 0 ? "none" : toolChoice === "none" ? "required" : toolChoice === "required" ? "auto" : void 0;
5734
- setToolChoice(next);
5735
- const label = next === void 0 ? "\u81EA\u52A8\uFF08\u9ED8\u8BA4\uFF09" : next === "none" ? "\u7981\u6B62\u8C03\u7528" : next === "required" ? "\u5F3A\u5236\u8C03\u7528" : "\u81EA\u52A8\uFF08\u9ED8\u8BA4\uFF09";
6703
+ if (!sessionRef.current) {
5736
6704
  setDisplayMessages((prev) => [
5737
6705
  ...prev,
5738
6706
  { role: "user", content: trimmed },
5739
- { role: "assistant", content: `\u5DE5\u5177\u8C03\u7528\u7B56\u7565\u5DF2\u5207\u6362\u4E3A ${label}` }
6707
+ {
6708
+ role: "assistant",
6709
+ content: "\u26A0 \u65E0\u6CD5\u8FDE\u63A5\u5230 Provider\u3002\u8BF7\u68C0\u67E5 API Key \u548C\u7F51\u7EDC\u914D\u7F6E\u3002"
6710
+ }
5740
6711
  ]);
5741
6712
  setInput("");
5742
6713
  return;
5743
6714
  }
5744
- const cmd = commandRegistry.get(cmdLower);
5745
- if (cmd) {
5746
- const result = cmd.handler();
5747
- switch (result.kind) {
5748
- case "exit":
5749
- await sessionRef.current?.flushLog();
5750
- process.exit(0);
5751
- return;
5752
- case "clear":
5753
- setDisplayMessages([]);
5754
- setStaticKey((prev) => prev + 1);
5755
- setInput("");
5756
- setStreamError(void 0);
5757
- process.stdout.write("\x1B[2J\x1B[H\x1B[3J");
5758
- sessionRef.current?.reset();
5759
- return;
5760
- case "navigate":
5761
- setInput("");
5762
- if (result.target === "game") {
5763
- onLaunchGame?.();
5764
- } else if (result.target === "stock") {
5765
- onLaunchStock?.();
5766
- }
5767
- return;
5768
- case "text":
5769
- setDisplayMessages((prev) => [
5770
- ...prev,
5771
- { role: "user", content: trimmed },
5772
- { role: "assistant", content: result.content }
5773
- ]);
5774
- setInput("");
5775
- return;
5776
- }
5777
- }
5778
- setDisplayMessages((prev) => [
5779
- ...prev,
5780
- { role: "user", content: trimmed },
5781
- { role: "assistant", content: `\u672A\u77E5\u547D\u4EE4\uFF1A${trimmed}\u3002\u8F93\u5165 /help \u67E5\u770B\u3002` }
5782
- ]);
6715
+ setDisplayMessages((prev) => [...prev, { role: "user", content: trimmed }]);
5783
6716
  setInput("");
5784
- return;
5785
- }
5786
- if (!sessionRef.current) {
5787
- setDisplayMessages((prev) => [
5788
- ...prev,
5789
- { role: "user", content: trimmed },
5790
- { role: "assistant", content: "\u26A0 \u65E0\u6CD5\u8FDE\u63A5\u5230 Provider\u3002\u8BF7\u68C0\u67E5 API Key \u548C\u7F51\u7EDC\u914D\u7F6E\u3002" }
5791
- ]);
5792
- setInput("");
5793
- return;
5794
- }
5795
- setDisplayMessages((prev) => [
5796
- ...prev,
5797
- { role: "user", content: trimmed }
5798
- ]);
5799
- setInput("");
5800
- setIsStreaming(true);
5801
- setStreamingPhase("thinking");
5802
- setStreamingPlaceholder(pickRandom(STREAMING_PLACEHOLDERS));
5803
- setCurrentContent("");
5804
- setCurrentToolCalls([]);
5805
- setCurrentUsage(void 0);
5806
- setCurrentElapsed(void 0);
5807
- setCurrentCost(void 0);
5808
- setStreamingModel(void 0);
5809
- setStreamError(void 0);
5810
- currentContentRef.current = "";
5811
- currentToolCallsRef.current = [];
5812
- currentUsageRef.current = void 0;
5813
- currentElapsedRef.current = void 0;
5814
- currentCostRef.current = void 0;
5815
- currentModelRef.current = void 0;
5816
- streamErrorRef.current = void 0;
5817
- currentRoundModifiedRef.current = false;
5818
- setRewindHintPhase("idle");
5819
- if (rewindHintTimerRef.current) {
5820
- clearTimeout(rewindHintTimerRef.current);
5821
- rewindHintTimerRef.current = null;
5822
- }
5823
- const session = sessionRef.current;
5824
- const abortController = new AbortController();
5825
- abortRef.current = abortController;
5826
- try {
5827
- for await (const event of session.chat(trimmed, {
5828
- thinkingAllowed: thinkingEnabled || void 0,
5829
- thinkingEffort: thinkingEnabled ? thinkingEffort : void 0,
5830
- responseFormat: responseFormat !== "text" ? responseFormat : void 0,
5831
- toolChoice
5832
- })) {
5833
- if (abortController.signal.aborted) break;
5834
- switch (event.type) {
5835
- case "text_delta":
5836
- setStreamingPhase("generating");
5837
- setCurrentContent((prev) => {
5838
- const next = prev + event.content;
5839
- currentContentRef.current = next;
5840
- return next;
5841
- });
5842
- break;
5843
- case "tool_calls":
5844
- setStreamingPhase("calling_tools");
5845
- setCurrentToolCalls((prev) => {
5846
- const next = [...prev, ...event.calls];
5847
- currentToolCallsRef.current = next;
5848
- return next;
5849
- });
5850
- for (const call of event.calls) {
5851
- if (isFileMutatingTool(call.name)) {
5852
- currentRoundModifiedRef.current = true;
5853
- break;
6717
+ setIsStreaming(true);
6718
+ setStreamingPhase("thinking");
6719
+ setStreamingPlaceholder(pickRandom(STREAMING_PLACEHOLDERS));
6720
+ setCurrentContent("");
6721
+ setCurrentReasoning([]);
6722
+ setCurrentToolCalls([]);
6723
+ setCurrentUsage(void 0);
6724
+ setCurrentElapsed(void 0);
6725
+ setCurrentCost(void 0);
6726
+ setStreamingModel(void 0);
6727
+ setStreamError(void 0);
6728
+ currentContentRef.current = "";
6729
+ currentReasoningRef.current = [];
6730
+ currentToolCallsRef.current = [];
6731
+ currentUsageRef.current = void 0;
6732
+ currentElapsedRef.current = void 0;
6733
+ currentCostRef.current = void 0;
6734
+ currentModelRef.current = void 0;
6735
+ streamErrorRef.current = void 0;
6736
+ currentRoundModifiedRef.current = false;
6737
+ setRewindHintPhase("idle");
6738
+ if (rewindHintTimerRef.current) {
6739
+ clearTimeout(rewindHintTimerRef.current);
6740
+ rewindHintTimerRef.current = null;
6741
+ }
6742
+ const session = sessionRef.current;
6743
+ const abortController = new AbortController();
6744
+ abortRef.current = abortController;
6745
+ try {
6746
+ for await (const event of session.chat(trimmed, {
6747
+ thinkingAllowed: thinkingEnabled || void 0,
6748
+ thinkingEffort: thinkingEnabled ? thinkingEffort : void 0,
6749
+ responseFormat: responseFormat !== "text" ? responseFormat : void 0,
6750
+ toolChoice
6751
+ })) {
6752
+ if (abortController.signal.aborted) break;
6753
+ switch (event.type) {
6754
+ case "text_delta":
6755
+ setStreamingPhase("generating");
6756
+ setCurrentContent((prev) => {
6757
+ const next = prev + event.content;
6758
+ currentContentRef.current = next;
6759
+ return next;
6760
+ });
6761
+ break;
6762
+ case "reasoning_delta":
6763
+ setStreamingPhase("thinking");
6764
+ setCurrentReasoning((prev) => {
6765
+ const last = prev[prev.length - 1];
6766
+ const next = last !== void 0 ? [...prev.slice(0, -1), last + event.content] : [...prev, event.content];
6767
+ currentReasoningRef.current = next;
6768
+ return next;
6769
+ });
6770
+ break;
6771
+ case "tool_calls":
6772
+ setStreamingPhase("calling_tools");
6773
+ setCurrentToolCalls((prev) => {
6774
+ const next = [...prev, ...event.calls];
6775
+ currentToolCallsRef.current = next;
6776
+ return next;
6777
+ });
6778
+ for (const call of event.calls) {
6779
+ if (isFileMutatingTool(call.name)) {
6780
+ currentRoundModifiedRef.current = true;
6781
+ break;
6782
+ }
5854
6783
  }
5855
- }
5856
- break;
5857
- case "tool_result":
5858
- setStreamingPhase("executing_tools");
5859
- setTimeout(() => setStreamingPhase("thinking"), 300);
5860
- setCurrentContent("");
5861
- currentContentRef.current = "";
5862
- setCurrentToolCalls([]);
5863
- currentToolCallsRef.current = [];
5864
- const r = event.result;
5865
- const line = r.success ? r.summary ?? `\u2705 ${event.name}: ${r.data.slice(0, 500)}${r.data.length > 500 ? "..." : ""}` : `\u274C ${event.name}: ${r.error ?? "\u6267\u884C\u5931\u8D25"}`;
5866
- setDisplayMessages((prev) => [
5867
- ...prev,
6784
+ break;
6785
+ case "tool_result":
6786
+ setStreamingPhase("executing_tools");
6787
+ setTimeout(() => setStreamingPhase("thinking"), 300);
6788
+ setCurrentContent("");
6789
+ currentContentRef.current = "";
6790
+ setCurrentReasoning((prev) => {
6791
+ const next = [...prev, ""];
6792
+ currentReasoningRef.current = next;
6793
+ return next;
6794
+ });
6795
+ setCurrentToolCalls([]);
6796
+ currentToolCallsRef.current = [];
6797
+ const r = event.result;
6798
+ if (event.name.startsWith("todo_") && event.todoSnapshot) {
6799
+ const allTerminated = event.todoSnapshot.length > 0 && event.todoSnapshot.every(
6800
+ (it) => it.status === "done" || it.status === "failed" || it.status === "skipped"
6801
+ );
6802
+ if (allTerminated) {
6803
+ setTodoSnapshot([]);
6804
+ } else {
6805
+ setTodoSnapshot(event.todoSnapshot);
6806
+ }
6807
+ } else {
6808
+ const line = r.success ? r.summary ?? `\u2705 ${event.name}: ${r.data.slice(0, 500)}${r.data.length > 500 ? "..." : ""}` : `\u274C ${event.name}: ${r.error ?? "\u6267\u884C\u5931\u8D25"}`;
6809
+ setDisplayMessages((prev) => [
6810
+ ...prev,
6811
+ {
6812
+ role: "tool",
6813
+ content: line,
6814
+ diff: r.diff
6815
+ }
6816
+ ]);
6817
+ }
6818
+ break;
6819
+ case "usage":
6820
+ setCurrentUsage(event.usage);
6821
+ setStreamingModel(event.model);
6822
+ currentUsageRef.current = event.usage;
6823
+ currentModelRef.current = event.model;
5868
6824
  {
5869
- role: "tool",
5870
- content: line,
5871
- diff: r.diff
6825
+ const cost = calculateCost(
6826
+ event.usage,
6827
+ event.model
6828
+ );
6829
+ setCurrentCost(cost.totalCost);
6830
+ currentCostRef.current = cost.totalCost;
5872
6831
  }
5873
- ]);
5874
- break;
5875
- case "usage":
5876
- setCurrentUsage(event.usage);
5877
- setStreamingModel(event.model);
5878
- currentUsageRef.current = event.usage;
5879
- currentModelRef.current = event.model;
6832
+ break;
6833
+ case "done":
6834
+ setCurrentElapsed(event.elapsed);
6835
+ currentElapsedRef.current = event.elapsed;
6836
+ break;
6837
+ case "error":
6838
+ setStreamError(event.error.message);
6839
+ streamErrorRef.current = event.error.message;
6840
+ break;
6841
+ }
6842
+ }
6843
+ } catch (err) {
6844
+ const msg = err instanceof Error ? err.message : String(err);
6845
+ setStreamError(msg);
6846
+ streamErrorRef.current = msg;
6847
+ } finally {
6848
+ setIsStreaming(false);
6849
+ setStreamingPhase(null);
6850
+ setIdlePlaceholder(pickRandom(IDLE_PLACEHOLDERS));
6851
+ abortRef.current = null;
6852
+ const finContent = currentContentRef.current;
6853
+ const finReasoning = currentReasoningRef.current.map((s) => s.trim()).filter((s) => s.length > 0);
6854
+ const finToolCalls = filterTodoToolCalls(currentToolCallsRef.current);
6855
+ const finStreamError = streamErrorRef.current;
6856
+ if (finContent || finToolCalls || finStreamError) {
6857
+ const completed = {
6858
+ content: finStreamError ? `\u26A0 \u8BF7\u6C42\u51FA\u9519\uFF1A${finStreamError}` : finContent || "",
6859
+ ...finReasoning.length > 0 ? { reasoning: finReasoning } : {},
6860
+ toolCalls: finToolCalls,
6861
+ usage: currentUsageRef.current,
6862
+ elapsed: currentElapsedRef.current,
6863
+ cost: currentCostRef.current,
6864
+ model: currentModelRef.current
6865
+ };
6866
+ setDisplayMessages((prev) => [
6867
+ ...prev,
5880
6868
  {
5881
- const cost = calculateCost(event.usage, event.model);
5882
- setCurrentCost(cost.totalCost);
5883
- currentCostRef.current = cost.totalCost;
6869
+ role: "assistant",
6870
+ content: completed.content,
6871
+ assistantDetail: completed
5884
6872
  }
5885
- break;
5886
- case "done":
5887
- setCurrentElapsed(event.elapsed);
5888
- currentElapsedRef.current = event.elapsed;
5889
- break;
5890
- case "error":
5891
- setStreamError(event.error.message);
5892
- streamErrorRef.current = event.error.message;
5893
- break;
6873
+ ]);
6874
+ }
6875
+ if (currentRoundModifiedRef.current && !finStreamError) {
6876
+ setRewindHintPhase("pending");
6877
+ if (rewindHintTimerRef.current) clearTimeout(rewindHintTimerRef.current);
6878
+ rewindHintTimerRef.current = setTimeout(() => {
6879
+ setRewindHintPhase((prev) => prev === "pending" ? "visible" : prev);
6880
+ rewindHintTimerRef.current = null;
6881
+ }, 2e3);
5894
6882
  }
5895
6883
  }
5896
- } catch (err) {
5897
- const msg = err instanceof Error ? err.message : String(err);
5898
- setStreamError(msg);
5899
- streamErrorRef.current = msg;
5900
- } finally {
5901
- setIsStreaming(false);
5902
- setStreamingPhase(null);
5903
- setIdlePlaceholder(pickRandom(IDLE_PLACEHOLDERS));
5904
- abortRef.current = null;
5905
- const finContent = currentContentRef.current;
5906
- const finToolCalls = currentToolCallsRef.current.length > 0 ? currentToolCallsRef.current : void 0;
5907
- const finStreamError = streamErrorRef.current;
5908
- if (finContent || finToolCalls || finStreamError) {
5909
- const completed = {
5910
- content: finStreamError ? `\u26A0 \u8BF7\u6C42\u51FA\u9519\uFF1A${finStreamError}` : finContent || "",
5911
- toolCalls: finToolCalls,
5912
- usage: currentUsageRef.current,
5913
- elapsed: currentElapsedRef.current,
5914
- cost: currentCostRef.current,
5915
- model: currentModelRef.current
5916
- };
5917
- setDisplayMessages((prev) => [
5918
- ...prev,
5919
- {
5920
- role: "assistant",
5921
- content: completed.content,
5922
- assistantDetail: completed
5923
- }
5924
- ]);
5925
- }
5926
- if (currentRoundModifiedRef.current && !finStreamError) {
5927
- setRewindHintPhase("pending");
5928
- if (rewindHintTimerRef.current) clearTimeout(rewindHintTimerRef.current);
5929
- rewindHintTimerRef.current = setTimeout(() => {
5930
- setRewindHintPhase((prev) => prev === "pending" ? "visible" : prev);
5931
- rewindHintTimerRef.current = null;
5932
- }, 2e3);
5933
- }
5934
- }
5935
- }, [onLaunchGame, onLaunchStock, currentContent, currentToolCalls, skills, skillSelectIndex, getFilteredSkills, thinkingEnabled, thinkingEffort, responseFormat, toolChoice, activeModel, sessionMode, isStreaming, rewinding]);
6884
+ },
6885
+ [
6886
+ onLaunchGame,
6887
+ onLaunchStock,
6888
+ currentContent,
6889
+ currentReasoning,
6890
+ currentToolCalls,
6891
+ skills,
6892
+ skillSelectIndex,
6893
+ getFilteredSkills,
6894
+ thinkingEnabled,
6895
+ thinkingEffort,
6896
+ responseFormat,
6897
+ toolChoice,
6898
+ activeModel,
6899
+ sessionMode,
6900
+ isStreaming,
6901
+ rewinding
6902
+ ]
6903
+ );
5936
6904
  useEffect4(() => {
5937
6905
  if (!isStreaming && externalCostTracker) {
5938
6906
  setTodayCost(externalCostTracker.todayTotalCost);
5939
6907
  }
5940
6908
  }, [isStreaming, externalCostTracker]);
5941
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [
5942
- !hasConversationStarted && /* @__PURE__ */ jsxs9(Box9, { flexDirection: "row", marginBottom: 1, children: [
5943
- /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", marginRight: 4, children: LOGO_LINES.map((line, i) => {
6909
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [
6910
+ !hasConversationStarted && /* @__PURE__ */ jsxs10(Box10, { flexDirection: "row", marginBottom: 1, children: [
6911
+ /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", marginRight: 4, children: LOGO_LINES.map((line, i) => {
5944
6912
  const colorIndex = (i + offset) % CYBER_PALETTE.length;
5945
- return /* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsx9(Text10, { bold: true, color: CYBER_PALETTE[colorIndex], children: line }) }, i);
6913
+ return /* @__PURE__ */ jsx10(Box10, { children: /* @__PURE__ */ jsx10(Text11, { bold: true, color: CYBER_PALETTE[colorIndex], children: line }) }, i);
5946
6914
  }) }),
5947
- /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", justifyContent: "center", children: [
5948
- /* @__PURE__ */ jsxs9(Text10, { color: "#00ff41", children: [
6915
+ /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", justifyContent: "center", children: [
6916
+ /* @__PURE__ */ jsxs10(Text11, { color: "#00ff41", children: [
5949
6917
  " \u2714 ",
5950
6918
  "\u5DF2\u5C31\u7EEA ",
5951
6919
  skillCount,
5952
6920
  " \u4E2A Skill"
5953
6921
  ] }),
5954
- /* @__PURE__ */ jsxs9(Text10, { color: "#00ffff", children: [
6922
+ /* @__PURE__ */ jsxs10(Text11, { color: "#00ffff", children: [
5955
6923
  " \u2139 ",
5956
6924
  "\u5DF2\u5C31\u7EEA ",
5957
6925
  toolCount,
5958
6926
  " \u4E2A\u5DE5\u5177"
5959
6927
  ] }),
5960
- /* @__PURE__ */ jsxs9(Text10, { color: "#00ffff", children: [
6928
+ /* @__PURE__ */ jsxs10(Text11, { color: "#00ffff", children: [
5961
6929
  " \u{1F527} \u6A21\u578B ",
5962
6930
  SUPPORTED_MODELS[activeModel]?.displayName ?? activeModel
5963
6931
  ] }),
5964
- thinkingEnabled && /* @__PURE__ */ jsxs9(Text10, { color: "#ff9800", children: [
6932
+ thinkingEnabled && /* @__PURE__ */ jsxs10(Text11, { color: "#ff9800", children: [
5965
6933
  " \u{1F9E0} \u6DF1\u5EA6\u601D\u8003 ",
5966
6934
  thinkingEffort === "max" ? "Max" : "High"
5967
6935
  ] }),
5968
- sessionMode === "plan" && /* @__PURE__ */ jsx9(Text10, { color: "#ff69b4", bold: true, children: " \u{1F4CB} \u8BA1\u5212\u6A21\u5F0F" }),
5969
- responseFormat === "json_object" && /* @__PURE__ */ jsx9(Text10, { color: "#4caf50", children: " \u{1F4C4} JSON" }),
5970
- toolChoice !== void 0 && /* @__PURE__ */ jsxs9(Text10, { color: "#e91e63", children: [
6936
+ sessionMode === "plan" && /* @__PURE__ */ jsx10(Text11, { color: "#ff69b4", bold: true, children: " \u{1F4CB} \u8BA1\u5212\u6A21\u5F0F" }),
6937
+ responseFormat === "json_object" && /* @__PURE__ */ jsx10(Text11, { color: "#4caf50", children: " \u{1F4C4} JSON" }),
6938
+ toolChoice !== void 0 && /* @__PURE__ */ jsxs10(Text11, { color: "#e91e63", children: [
5971
6939
  " \u{1F6E0} ",
5972
6940
  toolChoice === "none" ? "\u7981\u6B62\u5DE5\u5177" : toolChoice === "required" ? "\u5F3A\u5236\u5DE5\u5177" : ""
5973
6941
  ] }),
@@ -5975,52 +6943,62 @@ function ChatSession({
5975
6943
  const tip = cmdTips[cmdTipIndex % cmdTips.length];
5976
6944
  if (!tip) return null;
5977
6945
  const text = `${tip.name} ${tip.desc}`;
5978
- return /* @__PURE__ */ jsxs9(Text10, { children: [
5979
- /* @__PURE__ */ jsx9(Text10, { color: "#808080", children: " \u{1F4A1} " }),
5980
- cmdTipGradientColors.length > 0 ? text.split("").map((ch, i) => /* @__PURE__ */ jsx9(Text10, { color: cmdTipGradientColors[i] || void 0, children: ch }, i)) : /* @__PURE__ */ jsx9(Text10, { color: "#808080", children: text })
6946
+ return /* @__PURE__ */ jsxs10(Text11, { children: [
6947
+ /* @__PURE__ */ jsx10(Text11, { color: "#808080", children: " \u{1F4A1} " }),
6948
+ cmdTipGradientColors.length > 0 ? text.split("").map((ch, i) => /* @__PURE__ */ jsx10(Text11, { color: cmdTipGradientColors[i] || void 0, children: ch }, i)) : /* @__PURE__ */ jsx10(Text11, { color: "#808080", children: text })
5981
6949
  ] });
5982
6950
  })(),
5983
- verbose ? /* @__PURE__ */ jsx9(Text10, { color: "#ff1493", children: " \u26A1 Verbose" }) : null
6951
+ verbose ? /* @__PURE__ */ jsx10(Text11, { color: "#ff1493", children: " \u26A1 Verbose" }) : null
5984
6952
  ] }),
5985
- /* @__PURE__ */ jsxs9(Box9, { flexGrow: 1, flexDirection: "column", justifyContent: "center", alignItems: "flex-end", children: [
5986
- balanceLoading && balance === null ? /* @__PURE__ */ jsx9(Text10, { color: "yellow", children: " \u23F3 \u67E5\u8BE2\u4F59\u989D..." }) : balance !== null ? /* @__PURE__ */ jsxs9(Box9, { flexDirection: "row", children: [
5987
- /* @__PURE__ */ jsx9(Text10, { color: "yellow", children: "\u{1F4B0} " }),
5988
- /* @__PURE__ */ jsxs9(Text10, { color: "yellow", children: [
5989
- "\u4F59\u989D \xA5",
5990
- balance.toFixed(2)
5991
- ] })
5992
- ] }) : null,
5993
- todayCost !== null ? /* @__PURE__ */ jsxs9(Box9, { flexDirection: "row", children: [
5994
- /* @__PURE__ */ jsx9(Text10, { color: "cyan", children: "\u{1F4CA} " }),
5995
- /* @__PURE__ */ jsxs9(Text10, { color: "cyan", children: [
5996
- "\u4ECA\u65E5 \xA5",
5997
- todayCost.toFixed(2)
5998
- ] })
5999
- ] }) : null
6000
- ] })
6953
+ /* @__PURE__ */ jsxs10(
6954
+ Box10,
6955
+ {
6956
+ flexGrow: 1,
6957
+ flexDirection: "column",
6958
+ justifyContent: "center",
6959
+ alignItems: "flex-end",
6960
+ children: [
6961
+ balanceLoading && balance === null ? /* @__PURE__ */ jsx10(Text11, { color: "yellow", children: " \u23F3 \u67E5\u8BE2\u4F59\u989D..." }) : balance !== null ? /* @__PURE__ */ jsxs10(Box10, { flexDirection: "row", children: [
6962
+ /* @__PURE__ */ jsx10(Text11, { color: "yellow", children: "\u{1F4B0} " }),
6963
+ /* @__PURE__ */ jsxs10(Text11, { color: "yellow", children: [
6964
+ "\u4F59\u989D \xA5",
6965
+ balance.toFixed(2)
6966
+ ] })
6967
+ ] }) : null,
6968
+ todayCost !== null ? /* @__PURE__ */ jsxs10(Box10, { flexDirection: "row", children: [
6969
+ /* @__PURE__ */ jsx10(Text11, { color: "cyan", children: "\u{1F4CA} " }),
6970
+ /* @__PURE__ */ jsxs10(Text11, { color: "cyan", children: [
6971
+ "\u4ECA\u65E5 \xA5",
6972
+ todayCost.toFixed(2)
6973
+ ] })
6974
+ ] }) : null
6975
+ ]
6976
+ }
6977
+ )
6001
6978
  ] }),
6002
- /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginTop: 1, children: [
6003
- /* @__PURE__ */ jsx9(Static, { items: displayMessages, children: (msg, i) => {
6979
+ /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", marginTop: 1, children: [
6980
+ /* @__PURE__ */ jsx10(Static, { items: displayMessages, children: (msg, i) => {
6004
6981
  if (msg.role === "user") {
6005
- return /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, children: [
6006
- /* @__PURE__ */ jsx9(Box9, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx9(Text10, { bold: true, color: "#00ff41", children: "\u{1F464}" }) }),
6007
- /* @__PURE__ */ jsx9(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsx9(Text10, { wrap: "wrap", children: msg.content }) })
6982
+ return /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, children: [
6983
+ /* @__PURE__ */ jsx10(Box10, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx10(Text11, { bold: true, color: "#00ff41", children: "\u{1F464}" }) }),
6984
+ /* @__PURE__ */ jsx10(Box10, { flexGrow: 1, children: /* @__PURE__ */ jsx10(Text11, { wrap: "wrap", children: msg.content }) })
6008
6985
  ] }, i);
6009
6986
  }
6010
6987
  if (msg.role === "tool") {
6011
- return /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexDirection: "column", children: [
6012
- /* @__PURE__ */ jsxs9(Box9, { flexDirection: "row", children: [
6013
- /* @__PURE__ */ jsx9(Box9, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx9(Text10, { bold: true, color: "#f59e0b", children: "\u{1F527}" }) }),
6014
- /* @__PURE__ */ jsx9(Box9, { flexGrow: 1, children: /* @__PURE__ */ jsx9(Text10, { wrap: "wrap", children: msg.content }) })
6988
+ return /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, flexDirection: "column", children: [
6989
+ /* @__PURE__ */ jsxs10(Box10, { flexDirection: "row", children: [
6990
+ /* @__PURE__ */ jsx10(Box10, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx10(Text11, { dimColor: true, children: "\u{1F527}" }) }),
6991
+ /* @__PURE__ */ jsx10(Box10, { flexGrow: 1, children: /* @__PURE__ */ jsx10(Text11, { dimColor: true, wrap: "wrap", children: msg.content }) })
6015
6992
  ] }),
6016
- msg.diff && /* @__PURE__ */ jsx9(DiffPreview, { diff: msg.diff })
6993
+ msg.diff && /* @__PURE__ */ jsx10(DiffPreview, { diff: msg.diff })
6017
6994
  ] }, i);
6018
6995
  }
6019
6996
  const detail = msg.assistantDetail;
6020
- return /* @__PURE__ */ jsx9(
6997
+ return /* @__PURE__ */ jsx10(
6021
6998
  AssistantMessage,
6022
6999
  {
6023
7000
  content: msg.content,
7001
+ reasoning: detail?.reasoning,
6024
7002
  toolCalls: detail?.toolCalls,
6025
7003
  isStreaming: false,
6026
7004
  usage: detail?.usage,
@@ -6031,10 +7009,11 @@ function ChatSession({
6031
7009
  i
6032
7010
  );
6033
7011
  } }, staticKey),
6034
- isStreaming && /* @__PURE__ */ jsx9(
7012
+ isStreaming && /* @__PURE__ */ jsx10(
6035
7013
  AssistantMessage,
6036
7014
  {
6037
7015
  content: currentContent,
7016
+ reasoning: currentReasoning,
6038
7017
  toolCalls: currentToolCalls.length > 0 ? currentToolCalls : void 0,
6039
7018
  isStreaming: true,
6040
7019
  usage: _currentUsage,
@@ -6042,106 +7021,100 @@ function ChatSession({
6042
7021
  model: _streamingModel
6043
7022
  }
6044
7023
  ),
6045
- !isStreaming && streamError && /* @__PURE__ */ jsx9(Box9, { marginTop: 1, marginLeft: 3, children: /* @__PURE__ */ jsxs9(Text10, { color: "red", children: [
7024
+ !isStreaming && streamError && /* @__PURE__ */ jsx10(Box10, { marginTop: 1, marginLeft: 3, children: /* @__PURE__ */ jsxs10(Text11, { color: "red", children: [
6046
7025
  "\u26A0 ",
6047
7026
  streamError
6048
7027
  ] }) })
6049
7028
  ] }),
6050
- selectingModel ? /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexDirection: "column", children: [
6051
- /* @__PURE__ */ jsx9(Text10, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) }),
6052
- /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginTop: 1, children: [
6053
- /* @__PURE__ */ jsx9(Text10, { bold: true, color: "#00ffff", children: "\u9009\u62E9\u6A21\u578B\uFF1A" }),
7029
+ todoPanelVisible && todoSnapshot.length > 0 && /* @__PURE__ */ jsx10(TodoListPanel, { items: todoSnapshot }),
7030
+ selectingModel ? /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, flexDirection: "column", children: [
7031
+ /* @__PURE__ */ jsx10(Text11, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) }),
7032
+ /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", marginTop: 1, children: [
7033
+ /* @__PURE__ */ jsx10(Text11, { bold: true, color: "#00ffff", children: "\u9009\u62E9\u6A21\u578B\uFF1A" }),
6054
7034
  modelOptions.map((id, i) => {
6055
7035
  const meta = SUPPORTED_MODELS[id];
6056
7036
  const isCurrent = id === activeModel;
6057
7037
  const isSelected = i === modelSelectIndex;
6058
7038
  const marker = isSelected ? " > " : " ";
6059
7039
  const suffix = isCurrent ? " (\u5F53\u524D)" : "";
6060
- return /* @__PURE__ */ jsxs9(Box9, { children: [
6061
- /* @__PURE__ */ jsxs9(
6062
- Text10,
6063
- {
6064
- color: isSelected ? "#00ff41" : void 0,
6065
- bold: isSelected,
6066
- children: [
6067
- marker,
6068
- meta.displayName,
6069
- suffix
6070
- ]
6071
- }
6072
- ),
6073
- isSelected && /* @__PURE__ */ jsxs9(Text10, { color: "#808080", children: [
7040
+ return /* @__PURE__ */ jsxs10(Box10, { children: [
7041
+ /* @__PURE__ */ jsxs10(Text11, { color: isSelected ? "#00ff41" : void 0, bold: isSelected, children: [
7042
+ marker,
7043
+ meta.displayName,
7044
+ suffix
7045
+ ] }),
7046
+ isSelected && /* @__PURE__ */ jsxs10(Text11, { color: "#808080", children: [
6074
7047
  " \u2014 ",
6075
7048
  id
6076
7049
  ] })
6077
7050
  ] }, id);
6078
7051
  }),
6079
- /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text10, { color: "#808080", dimColor: true, children: "\u2191\u2193 \u9009\u62E9 \xB7 Enter \u786E\u8BA4 \xB7 Esc \u53D6\u6D88" }) })
7052
+ /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text11, { color: "#808080", dimColor: true, children: "\u2191\u2193 \u9009\u62E9 \xB7 Enter \u786E\u8BA4 \xB7 Esc \u53D6\u6D88" }) })
6080
7053
  ] }),
6081
- /* @__PURE__ */ jsx9(Text10, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) })
6082
- ] }) : rewindSelecting ? /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, flexDirection: "column", children: [
6083
- /* @__PURE__ */ jsx9(Text10, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) }),
6084
- /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginTop: 1, children: [
6085
- /* @__PURE__ */ jsx9(Text10, { bold: true, color: "#ff9800", children: "\u9009\u62E9\u8981\u56DE\u9000\u7684\u68C0\u67E5\u70B9\uFF1A" }),
7054
+ /* @__PURE__ */ jsx10(Text11, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) })
7055
+ ] }) : rewindSelecting ? /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, flexDirection: "column", children: [
7056
+ /* @__PURE__ */ jsx10(Text11, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) }),
7057
+ /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", marginTop: 1, children: [
7058
+ /* @__PURE__ */ jsx10(Text11, { bold: true, color: "#ff9800", children: "\u9009\u62E9\u8981\u56DE\u9000\u7684\u68C0\u67E5\u70B9\uFF1A" }),
6086
7059
  rewindList.map((cp2, i) => {
6087
7060
  const isSelected = i === rewindSelectIndex;
6088
7061
  const marker = isSelected ? " > " : " ";
6089
7062
  const time = new Date(cp2.timestamp).toLocaleTimeString();
6090
7063
  const preview = cp2.preview || "(\u7A7A)";
6091
7064
  const tag = cp2.isGitRepo ? "" : " [\u975E git\uFF0C\u4EC5\u5BF9\u8BDD]";
6092
- return /* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsxs9(
6093
- Text10,
6094
- {
6095
- color: isSelected ? "#ff9800" : void 0,
6096
- bold: isSelected,
6097
- children: [
6098
- marker,
6099
- "#",
6100
- i + 1,
6101
- " ",
6102
- time,
6103
- " `",
6104
- preview,
6105
- tag,
6106
- "`"
6107
- ]
6108
- }
6109
- ) }, cp2.index);
7065
+ return /* @__PURE__ */ jsx10(Box10, { children: /* @__PURE__ */ jsxs10(Text11, { color: isSelected ? "#ff9800" : void 0, bold: isSelected, children: [
7066
+ marker,
7067
+ "#",
7068
+ i + 1,
7069
+ " ",
7070
+ time,
7071
+ " `",
7072
+ preview,
7073
+ tag,
7074
+ "`"
7075
+ ] }) }, cp2.index);
6110
7076
  }),
6111
- /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text10, { color: "#808080", dimColor: true, children: "\u2191\u2193 \u9009\u62E9 \xB7 Enter \u786E\u8BA4 \xB7 Esc \u53D6\u6D88" }) })
7077
+ /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text11, { color: "#808080", dimColor: true, children: "\u2191\u2193 \u9009\u62E9 \xB7 Enter \u786E\u8BA4 \xB7 Esc \u53D6\u6D88" }) })
6112
7078
  ] }),
6113
- /* @__PURE__ */ jsx9(Text10, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) })
6114
- ] }) : /* @__PURE__ */ jsxs9(Fragment2, { children: [
6115
- (hasConversationStarted || sessionMode === "plan") && isStreaming && streamingPhase ? /* @__PURE__ */ jsx9(Box9, { marginTop: 1, justifyContent: "center", children: /* @__PURE__ */ jsxs9(Text10, { bold: true, color: PHASE_CONFIG[streamingPhase].color, children: [
7079
+ /* @__PURE__ */ jsx10(Text11, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) })
7080
+ ] }) : /* @__PURE__ */ jsxs10(Fragment2, { children: [
7081
+ (hasConversationStarted || sessionMode === "plan") && isStreaming && streamingPhase ? /* @__PURE__ */ jsx10(Box10, { marginTop: 1, justifyContent: "center", children: /* @__PURE__ */ jsxs10(Text11, { bold: true, color: PHASE_CONFIG[streamingPhase].color, children: [
6116
7082
  PHASE_CONFIG[streamingPhase].icon,
6117
7083
  " ",
6118
7084
  PHASE_CONFIG[streamingPhase].label,
6119
7085
  " ",
6120
- /* @__PURE__ */ jsx9(InkSpinner2, { type: "dots" })
7086
+ /* @__PURE__ */ jsx10(InkSpinner3, { type: "dots" })
6121
7087
  ] }) }) : rewindHintPhase === "visible" ? (
6122
7088
  // 本轮修改了文件时,流式结束后 2s 在原位置展示 /rewind 提示
6123
7089
  // 一直保留到下次对话开始,与 /rewind 1(最新检查点)配套
6124
- /* @__PURE__ */ jsx9(Box9, { marginTop: 1, justifyContent: "center", children: /* @__PURE__ */ jsx9(Text10, { color: "#808080", children: "\u21A9 /rewind 1 \u53EF\u64A4\u56DE\u672C\u6B21\u4FEE\u6539" }) })
7090
+ /* @__PURE__ */ jsx10(Box10, { marginTop: 1, justifyContent: "center", children: /* @__PURE__ */ jsx10(Text11, { color: "#808080", children: "\u21A9 /rewind 1 \u53EF\u64A4\u56DE\u672C\u6B21\u4FEE\u6539" }) })
6125
7091
  ) : null,
6126
- /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: hasConversationStarted || sessionMode === "plan" ? /* @__PURE__ */ jsxs9(Text10, { color: sessionMode === "plan" ? "#ff69b4" : "#00ffff", dimColor: sessionMode !== "plan", children: [
6127
- /* @__PURE__ */ jsx9(Text10, { children: sessionMode === "plan" ? "\u2500".repeat(Math.max(dividerWidth - 48, 1)) : "\u2500".repeat(Math.max(dividerWidth - 35, 10)) }),
6128
- balance !== null && /* @__PURE__ */ jsxs9(Text10, { color: "yellow", children: [
6129
- " \u{1F4B0} \u4F59\u989D \xA5",
6130
- balance.toFixed(2)
6131
- ] }),
6132
- isStreaming ? /* @__PURE__ */ jsxs9(Text10, { color: "cyan", children: [
6133
- " \u{1F4CA} \u672C\u6B21 \xA5",
6134
- sessionCost > 0 ? sessionCost.toFixed(4) + " " : "",
6135
- /* @__PURE__ */ jsx9(InkSpinner2, { type: "dots" })
6136
- ] }) : sessionCost > 0 ? /* @__PURE__ */ jsxs9(Text10, { color: "cyan", children: [
6137
- " \u{1F4CA} \u672C\u6B21 \xA5",
6138
- sessionCost.toFixed(4)
6139
- ] }) : null,
6140
- sessionMode === "plan" && /* @__PURE__ */ jsx9(Text10, { color: "#ff69b4", bold: true, children: " \u{1F4CB} \u8BA1\u5212\u6A21\u5F0F" })
6141
- ] }) : /* @__PURE__ */ jsx9(Text10, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) }) }),
6142
- /* @__PURE__ */ jsxs9(Box9, { children: [
6143
- /* @__PURE__ */ jsx9(Box9, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx9(Text10, { bold: true, color: "#00ff41", children: "\u26A1" }) }),
6144
- /* @__PURE__ */ jsx9(Box9, { flexGrow: 1, children: !input && !isStreaming && idlePlaceholder && gradientColors.length > 0 ? /* @__PURE__ */ jsx9(Text10, { children: idlePlaceholder.split("").map((ch, i) => /* @__PURE__ */ jsx9(Text10, { color: gradientColors[i] ?? void 0, children: ch }, i)) }) : !input && isStreaming && streamingPlaceholder && streamingGradientColors.length > 0 ? /* @__PURE__ */ jsx9(Text10, { children: streamingPlaceholder.split("").map((ch, i) => /* @__PURE__ */ jsx9(Text10, { color: streamingGradientColors[i] ?? void 0, children: ch }, i)) }) : /* @__PURE__ */ jsx9(
7092
+ /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: hasConversationStarted || sessionMode === "plan" ? /* @__PURE__ */ jsxs10(
7093
+ Text11,
7094
+ {
7095
+ color: sessionMode === "plan" ? "#ff69b4" : "#00ffff",
7096
+ dimColor: sessionMode !== "plan",
7097
+ children: [
7098
+ /* @__PURE__ */ jsx10(Text11, { children: sessionMode === "plan" ? "\u2500".repeat(Math.max(dividerWidth - 48, 1)) : "\u2500".repeat(Math.max(dividerWidth - 35, 10)) }),
7099
+ balance !== null && /* @__PURE__ */ jsxs10(Text11, { color: "yellow", children: [
7100
+ " \u{1F4B0} \u4F59\u989D \xA5",
7101
+ balance.toFixed(2)
7102
+ ] }),
7103
+ isStreaming ? /* @__PURE__ */ jsxs10(Text11, { color: "cyan", children: [
7104
+ " \u{1F4CA} \u672C\u6B21 \xA5",
7105
+ sessionCost > 0 ? sessionCost.toFixed(4) + " " : "",
7106
+ /* @__PURE__ */ jsx10(InkSpinner3, { type: "dots" })
7107
+ ] }) : sessionCost > 0 ? /* @__PURE__ */ jsxs10(Text11, { color: "cyan", children: [
7108
+ " \u{1F4CA} \u672C\u6B21 \xA5",
7109
+ sessionCost.toFixed(4)
7110
+ ] }) : null,
7111
+ sessionMode === "plan" && /* @__PURE__ */ jsx10(Text11, { color: "#ff69b4", bold: true, children: " \u{1F4CB} \u8BA1\u5212\u6A21\u5F0F" })
7112
+ ]
7113
+ }
7114
+ ) : /* @__PURE__ */ jsx10(Text11, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) }) }),
7115
+ /* @__PURE__ */ jsxs10(Box10, { children: [
7116
+ /* @__PURE__ */ jsx10(Box10, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx10(Text11, { bold: true, color: "#00ff41", children: "\u26A1" }) }),
7117
+ /* @__PURE__ */ jsx10(Box10, { flexGrow: 1, children: !input && !isStreaming && idlePlaceholder && gradientColors.length > 0 ? /* @__PURE__ */ jsx10(Text11, { children: idlePlaceholder.split("").map((ch, i) => /* @__PURE__ */ jsx10(Text11, { color: gradientColors[i] ?? void 0, children: ch }, i)) }) : !input && isStreaming && streamingPlaceholder && streamingGradientColors.length > 0 ? /* @__PURE__ */ jsx10(Text11, { children: streamingPlaceholder.split("").map((ch, i) => /* @__PURE__ */ jsx10(Text11, { color: streamingGradientColors[i] ?? void 0, children: ch }, i)) }) : /* @__PURE__ */ jsx10(
6145
7118
  TextInput,
6146
7119
  {
6147
7120
  value: input,
@@ -6152,19 +7125,19 @@ function ChatSession({
6152
7125
  inputKey
6153
7126
  ) })
6154
7127
  ] }),
6155
- /* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsx9(Text10, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) }) }),
6156
- /* @__PURE__ */ jsx9(SkillSelector, { skills, input, selectedIndex: skillSelectIndex }),
6157
- /* @__PURE__ */ jsx9(FileSelector, { files, input, selectedIndex: fileSelectIndex })
7128
+ /* @__PURE__ */ jsx10(Box10, { children: /* @__PURE__ */ jsx10(Text11, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) }) }),
7129
+ /* @__PURE__ */ jsx10(SkillSelector, { skills, input, selectedIndex: skillSelectIndex }),
7130
+ /* @__PURE__ */ jsx10(FileSelector, { files, input, selectedIndex: fileSelectIndex })
6158
7131
  ] }),
6159
- doubleCtrlC && !isStreaming && /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text10, { color: "#ff1493", bold: true, children: " \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode" }) }),
6160
- isStreaming && /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text10, { color: "yellow", dimColor: true, children: " \u63D0\u793A\uFF1A\u6309 Ctrl+C \u53D6\u6D88\u5F53\u524D\u8BF7\u6C42" }) })
7132
+ doubleCtrlC && !isStreaming && /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text11, { color: "#ff1493", bold: true, children: " \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode" }) }),
7133
+ isStreaming && /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text11, { color: "yellow", dimColor: true, children: " \u63D0\u793A\uFF1A\u6309 Ctrl+C \u53D6\u6D88\u5F53\u524D\u8BF7\u6C42" }) })
6161
7134
  ] });
6162
7135
  }
6163
7136
 
6164
7137
  // src/ui/GamePicker.tsx
6165
- import { Box as Box10, Text as Text11, useInput as useInput2 } from "ink";
7138
+ import { Box as Box11, Text as Text12, useInput as useInput2 } from "ink";
6166
7139
  import { useState as useState5, useCallback as useCallback3 } from "react";
6167
- import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
7140
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
6168
7141
  function GamePicker({ games, onSelect, onExit, onBackToChat }) {
6169
7142
  const [selectedIndex, setSelectedIndex] = useState5(0);
6170
7143
  const exitAction = onBackToChat ?? onExit ?? (() => process.exit(0));
@@ -6192,18 +7165,18 @@ function GamePicker({ games, onSelect, onExit, onBackToChat }) {
6192
7165
  [games, selectedIndex, onSelect, onExit, onBackToChat, handleCtrlC]
6193
7166
  )
6194
7167
  );
6195
- return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
6196
- /* @__PURE__ */ jsx10(Box10, { marginBottom: 1, children: /* @__PURE__ */ jsx10(Text11, { bold: true, color: "#00ffff", children: "\u{1F3AE} \u6E38\u620F\u5217\u8868" }) }),
6197
- /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: games.map((game, index) => {
7168
+ return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
7169
+ /* @__PURE__ */ jsx11(Box11, { marginBottom: 1, children: /* @__PURE__ */ jsx11(Text12, { bold: true, color: "#00ffff", children: "\u{1F3AE} \u6E38\u620F\u5217\u8868" }) }),
7170
+ /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", children: games.map((game, index) => {
6198
7171
  const isSelected = index === selectedIndex;
6199
- return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "row", children: [
6200
- /* @__PURE__ */ jsx10(Box10, { width: 3, flexShrink: 0, children: isSelected ? /* @__PURE__ */ jsx10(Text11, { bold: true, color: "#00ff41", children: "\u25B8 " }) : /* @__PURE__ */ jsx10(Text11, { children: " " }) }),
6201
- /* @__PURE__ */ jsx10(Box10, { width: 20, flexShrink: 0, children: /* @__PURE__ */ jsx10(Text11, { bold: true, color: isSelected ? "#00ff41" : "#ffffff", children: game.name }) }),
6202
- /* @__PURE__ */ jsx10(Box10, { children: /* @__PURE__ */ jsx10(Text11, { color: "#888888", children: game.description }) })
7172
+ return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "row", children: [
7173
+ /* @__PURE__ */ jsx11(Box11, { width: 3, flexShrink: 0, children: isSelected ? /* @__PURE__ */ jsx11(Text12, { bold: true, color: "#00ff41", children: "\u25B8 " }) : /* @__PURE__ */ jsx11(Text12, { children: " " }) }),
7174
+ /* @__PURE__ */ jsx11(Box11, { width: 20, flexShrink: 0, children: /* @__PURE__ */ jsx11(Text12, { bold: true, color: isSelected ? "#00ff41" : "#ffffff", children: game.name }) }),
7175
+ /* @__PURE__ */ jsx11(Box11, { children: /* @__PURE__ */ jsx11(Text12, { color: "#888888", children: game.description }) })
6203
7176
  ] }, game.id);
6204
7177
  }) }),
6205
- /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text11, { dimColor: true, children: " \u2191/\u2193 \u9009\u62E9 Enter \u542F\u52A8 q \u8FD4\u56DE" }) }),
6206
- doubleCtrlC && /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text11, { color: "#ff1493", bold: true, children: " \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode" }) })
7178
+ /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text12, { dimColor: true, children: " \u2191/\u2193 \u9009\u62E9 Enter \u542F\u52A8 q \u8FD4\u56DE" }) }),
7179
+ doubleCtrlC && /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text12, { color: "#ff1493", bold: true, children: " \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode" }) })
6207
7180
  ] });
6208
7181
  }
6209
7182
 
@@ -6220,9 +7193,9 @@ function listGames() {
6220
7193
  }
6221
7194
 
6222
7195
  // src/game/brick-breaker/index.tsx
6223
- import { Box as Box11, Text as Text12, useInput as useInput3, render as render2 } from "ink";
7196
+ import { Box as Box12, Text as Text13, useInput as useInput3, render as render2 } from "ink";
6224
7197
  import { useState as useState6, useEffect as useEffect5, useRef as useRef4, useCallback as useCallback4 } from "react";
6225
- import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
7198
+ import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
6226
7199
  var GAME_WIDTH = 40;
6227
7200
  var GAME_HEIGHT = 18;
6228
7201
  var PADDLE_WIDTH = 9;
@@ -6412,49 +7385,49 @@ function BrickBreakerGame({ onExit: _onExit }) {
6412
7385
  const board = buildBoard(s);
6413
7386
  const def = getLevel(s.level);
6414
7387
  void tick2;
6415
- return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
6416
- /* @__PURE__ */ jsxs11(Box11, { flexDirection: "row", children: [
6417
- /* @__PURE__ */ jsx11(Box11, { width: 20, children: /* @__PURE__ */ jsxs11(Text12, { children: [
7388
+ return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
7389
+ /* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", children: [
7390
+ /* @__PURE__ */ jsx12(Box12, { width: 20, children: /* @__PURE__ */ jsxs12(Text13, { children: [
6418
7391
  "\u5173\u5361 ",
6419
7392
  s.level,
6420
7393
  ": ",
6421
- /* @__PURE__ */ jsx11(Text12, { color: "cyan", children: def.desc })
7394
+ /* @__PURE__ */ jsx12(Text13, { color: "cyan", children: def.desc })
6422
7395
  ] }) }),
6423
- /* @__PURE__ */ jsx11(Box11, { width: 12, children: /* @__PURE__ */ jsxs11(Text12, { children: [
7396
+ /* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsxs12(Text13, { children: [
6424
7397
  "\u5206\u6570: ",
6425
- /* @__PURE__ */ jsx11(Text12, { color: "yellow", children: String(s.score).padStart(3, "0") })
7398
+ /* @__PURE__ */ jsx12(Text13, { color: "yellow", children: String(s.score).padStart(3, "0") })
6426
7399
  ] }) }),
6427
- /* @__PURE__ */ jsx11(Box11, { width: 12, children: /* @__PURE__ */ jsxs11(Text12, { children: [
7400
+ /* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsxs12(Text13, { children: [
6428
7401
  "\u751F\u547D: ",
6429
- /* @__PURE__ */ jsx11(Text12, { color: "red", children: "\u2665".repeat(Math.max(0, s.lives)) })
7402
+ /* @__PURE__ */ jsx12(Text13, { color: "red", children: "\u2665".repeat(Math.max(0, s.lives)) })
6430
7403
  ] }) }),
6431
- /* @__PURE__ */ jsx11(Box11, { width: 10, children: /* @__PURE__ */ jsxs11(Text12, { children: [
7404
+ /* @__PURE__ */ jsx12(Box12, { width: 10, children: /* @__PURE__ */ jsxs12(Text13, { children: [
6432
7405
  "\u7816\u5757: ",
6433
- /* @__PURE__ */ jsx11(Text12, { color: "cyan", children: aliveCount })
7406
+ /* @__PURE__ */ jsx12(Text13, { color: "cyan", children: aliveCount })
6434
7407
  ] }) }),
6435
- /* @__PURE__ */ jsx11(Box11, { children: /* @__PURE__ */ jsxs11(Text12, { color: s.paused ? "gray" : "green", children: [
7408
+ /* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsxs12(Text13, { color: s.paused ? "gray" : "green", children: [
6436
7409
  "[",
6437
7410
  s.paused ? "\u6682\u505C" : "\u8FD0\u884C\u4E2D",
6438
7411
  "]"
6439
7412
  ] }) })
6440
7413
  ] }),
6441
- /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
6442
- /* @__PURE__ */ jsxs11(Text12, { children: [
7414
+ /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
7415
+ /* @__PURE__ */ jsxs12(Text13, { children: [
6443
7416
  "\u250C",
6444
7417
  "\u2500".repeat(GAME_WIDTH),
6445
7418
  "\u2510"
6446
7419
  ] }),
6447
- /* @__PURE__ */ jsx11(Text12, { children: selectingLevel ? " \x1B[90m\u9009\u62E9\u5173\u5361\u540E\u5F00\u59CB...\x1B[0m" : board }),
6448
- /* @__PURE__ */ jsxs11(Text12, { children: [
7420
+ /* @__PURE__ */ jsx12(Text13, { children: selectingLevel ? " \x1B[90m\u9009\u62E9\u5173\u5361\u540E\u5F00\u59CB...\x1B[0m" : board }),
7421
+ /* @__PURE__ */ jsxs12(Text13, { children: [
6449
7422
  "\u2514",
6450
7423
  "\u2500".repeat(GAME_WIDTH),
6451
7424
  "\u2518"
6452
7425
  ] })
6453
7426
  ] }),
6454
- selectingLevel && /* @__PURE__ */ jsxs11(Box11, { marginTop: 1, flexDirection: "column", children: [
6455
- /* @__PURE__ */ jsx11(Text12, { bold: true, color: "yellow", children: "\u9009\u62E9\u5173\u5361" }),
6456
- /* @__PURE__ */ jsx11(Box11, { flexDirection: "row", flexWrap: "wrap", children: LEVELS.map((lv, i) => /* @__PURE__ */ jsx11(Box11, { width: 22, children: /* @__PURE__ */ jsxs11(Text12, { children: [
6457
- /* @__PURE__ */ jsx11(Text12, { color: initialLevel === i + 1 ? "green" : "white", children: i + 1 === 10 ? "0" : String(i + 1) }),
7427
+ selectingLevel && /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, flexDirection: "column", children: [
7428
+ /* @__PURE__ */ jsx12(Text13, { bold: true, color: "yellow", children: "\u9009\u62E9\u5173\u5361" }),
7429
+ /* @__PURE__ */ jsx12(Box12, { flexDirection: "row", flexWrap: "wrap", children: LEVELS.map((lv, i) => /* @__PURE__ */ jsx12(Box12, { width: 22, children: /* @__PURE__ */ jsxs12(Text13, { children: [
7430
+ /* @__PURE__ */ jsx12(Text13, { color: initialLevel === i + 1 ? "green" : "white", children: i + 1 === 10 ? "0" : String(i + 1) }),
6458
7431
  ". ",
6459
7432
  lv.desc,
6460
7433
  " (",
@@ -6463,16 +7436,16 @@ function BrickBreakerGame({ onExit: _onExit }) {
6463
7436
  lv.cols,
6464
7437
  ")"
6465
7438
  ] }) }, i)) }),
6466
- /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text12, { dimColor: true, children: "\u6309\u6570\u5B57\u9009\u5173 q \u9000\u51FA" }) })
7439
+ /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text13, { dimColor: true, children: "\u6309\u6570\u5B57\u9009\u5173 q \u9000\u51FA" }) })
6467
7440
  ] }),
6468
- !selectingLevel && (s.gameOver || s.win) && /* @__PURE__ */ jsxs11(Box11, { marginTop: 1, children: [
6469
- /* @__PURE__ */ jsx11(Text12, { bold: true, color: s.gameOver ? "red" : "green", children: s.gameOver ? "\u6E38\u620F\u7ED3\u675F\uFF01" : "\u606D\u559C\u901A\u5173\uFF01" }),
6470
- /* @__PURE__ */ jsxs11(Text12, { children: [
7441
+ !selectingLevel && (s.gameOver || s.win) && /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, children: [
7442
+ /* @__PURE__ */ jsx12(Text13, { bold: true, color: s.gameOver ? "red" : "green", children: s.gameOver ? "\u6E38\u620F\u7ED3\u675F\uFF01" : "\u606D\u559C\u901A\u5173\uFF01" }),
7443
+ /* @__PURE__ */ jsxs12(Text13, { children: [
6471
7444
  " \u5206\u6570: ",
6472
- /* @__PURE__ */ jsx11(Text12, { color: "yellow", children: s.score })
7445
+ /* @__PURE__ */ jsx12(Text13, { color: "yellow", children: s.score })
6473
7446
  ] })
6474
7447
  ] }),
6475
- !selectingLevel && /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: s.gameOver || s.win ? /* @__PURE__ */ jsx11(Text12, { dimColor: true, children: "\u2190 \u2192 \u79FB\u52A8 r \u91CD\u5F00 l \u9009\u5173 q \u9000\u51FA" }) : /* @__PURE__ */ jsx11(Text12, { dimColor: true, children: "\u2190 \u2192 \u79FB\u52A8 p \u6682\u505C q \u9000\u51FA" }) })
7448
+ !selectingLevel && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: s.gameOver || s.win ? /* @__PURE__ */ jsx12(Text13, { dimColor: true, children: "\u2190 \u2192 \u79FB\u52A8 r \u91CD\u5F00 l \u9009\u5173 q \u9000\u51FA" }) : /* @__PURE__ */ jsx12(Text13, { dimColor: true, children: "\u2190 \u2192 \u79FB\u52A8 p \u6682\u505C q \u9000\u51FA" }) })
6476
7449
  ] });
6477
7450
  }
6478
7451
  var brick_breaker_default = {
@@ -6482,7 +7455,7 @@ var brick_breaker_default = {
6482
7455
  play: async () => {
6483
7456
  await new Promise((resolve2) => {
6484
7457
  const { unmount } = render2(
6485
- /* @__PURE__ */ jsx11(BrickBreakerGame, { onExit: () => {
7458
+ /* @__PURE__ */ jsx12(BrickBreakerGame, { onExit: () => {
6486
7459
  unmount();
6487
7460
  resolve2();
6488
7461
  } })
@@ -6492,9 +7465,9 @@ var brick_breaker_default = {
6492
7465
  };
6493
7466
 
6494
7467
  // src/game/coder-check/index.tsx
6495
- import { Box as Box12, Text as Text13, useInput as useInput4, render as render3 } from "ink";
7468
+ import { Box as Box13, Text as Text14, useInput as useInput4, render as render3 } from "ink";
6496
7469
  import { useState as useState7, useEffect as useEffect6, useRef as useRef5, useCallback as useCallback5 } from "react";
6497
- import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
7470
+ import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
6498
7471
  var GAME_W = 66;
6499
7472
  var GAME_H = 20;
6500
7473
  var SCORE_H = 6;
@@ -6965,44 +7938,44 @@ function CoderCheck({ onExit: _onExit }) {
6965
7938
  const scoreLines = buildScoreLines(scoreStr);
6966
7939
  const view = buildGameView(s, scoreLines, scoreColor, s.message);
6967
7940
  void tick2;
6968
- return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", paddingX: 1, children: [
6969
- /* @__PURE__ */ jsx12(Box12, { flexDirection: "row", children: /* @__PURE__ */ jsxs12(Text13, { children: [
7941
+ return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", paddingX: 1, children: [
7942
+ /* @__PURE__ */ jsx13(Box13, { flexDirection: "row", children: /* @__PURE__ */ jsxs13(Text14, { children: [
6970
7943
  "\u751F\u547D ",
6971
- /* @__PURE__ */ jsx12(Text13, { color: "red", children: "\u2665".repeat(Math.max(0, s.lives)) }),
7944
+ /* @__PURE__ */ jsx13(Text14, { color: "red", children: "\u2665".repeat(Math.max(0, s.lives)) }),
6972
7945
  " ",
6973
7946
  "\u901F\u5EA6 ",
6974
- /* @__PURE__ */ jsxs12(Text13, { color: "cyan", children: [
7947
+ /* @__PURE__ */ jsxs13(Text14, { color: "cyan", children: [
6975
7948
  "Lv.",
6976
7949
  Math.floor(s.speed * 10)
6977
7950
  ] })
6978
7951
  ] }) }),
6979
- !s.gameOver && s.target && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text13, { children: [
7952
+ !s.gameOver && s.target && /* @__PURE__ */ jsx13(Box13, { marginTop: 1, children: /* @__PURE__ */ jsxs13(Text14, { children: [
6980
7953
  "\u6253\u5B57: ",
6981
- /* @__PURE__ */ jsx12(Text13, { color: "green", children: s.typed }),
6982
- /* @__PURE__ */ jsx12(Text13, { color: "white", children: s.target.slice(s.typed.length) })
7954
+ /* @__PURE__ */ jsx13(Text14, { color: "green", children: s.typed }),
7955
+ /* @__PURE__ */ jsx13(Text14, { color: "white", children: s.target.slice(s.typed.length) })
6983
7956
  ] }) }),
6984
- /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginTop: 1, children: [
6985
- /* @__PURE__ */ jsxs12(Text13, { children: [
7957
+ /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", marginTop: 1, children: [
7958
+ /* @__PURE__ */ jsxs13(Text14, { children: [
6986
7959
  "\u250C",
6987
7960
  "\u2500".repeat(GAME_W),
6988
7961
  "\u2510"
6989
7962
  ] }),
6990
- view.map((row, i) => /* @__PURE__ */ jsx12(Text13, { children: `\u2502${row}\u2502` }, i)),
6991
- /* @__PURE__ */ jsxs12(Text13, { children: [
7963
+ view.map((row, i) => /* @__PURE__ */ jsx13(Text14, { children: `\u2502${row}\u2502` }, i)),
7964
+ /* @__PURE__ */ jsxs13(Text14, { children: [
6992
7965
  "\u2514",
6993
7966
  "\u2500".repeat(GAME_W),
6994
7967
  "\u2518"
6995
7968
  ] })
6996
7969
  ] }),
6997
- s.gameOver && /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, children: [
6998
- /* @__PURE__ */ jsx12(Text13, { bold: true, color: "red", children: "\u6E38\u620F\u7ED3\u675F\uFF01" }),
6999
- /* @__PURE__ */ jsxs12(Text13, { children: [
7970
+ s.gameOver && /* @__PURE__ */ jsxs13(Box13, { marginTop: 1, children: [
7971
+ /* @__PURE__ */ jsx13(Text14, { bold: true, color: "red", children: "\u6E38\u620F\u7ED3\u675F\uFF01" }),
7972
+ /* @__PURE__ */ jsxs13(Text14, { children: [
7000
7973
  " \u5F97\u5206: ",
7001
- /* @__PURE__ */ jsx12(Text13, { color: "yellow", children: s.score }),
7974
+ /* @__PURE__ */ jsx13(Text14, { color: "yellow", children: s.score }),
7002
7975
  " r \u91CD\u5F00 q \u9000\u51FA"
7003
7976
  ] })
7004
7977
  ] }),
7005
- /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text13, { dimColor: true, children: "\u6253\u5B57\u6D88\u9664\u5355\u8BCD \u7A7A\u683C/Ctrl+P\u6682\u505C Ctrl+Q\u9000\u51FA" }) })
7978
+ /* @__PURE__ */ jsx13(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: "\u6253\u5B57\u6D88\u9664\u5355\u8BCD \u7A7A\u683C/Ctrl+P\u6682\u505C Ctrl+Q\u9000\u51FA" }) })
7006
7979
  ] });
7007
7980
  }
7008
7981
  var coder_check_default = {
@@ -7012,7 +7985,7 @@ var coder_check_default = {
7012
7985
  play: async () => {
7013
7986
  await new Promise((resolve2) => {
7014
7987
  const { unmount } = render3(
7015
- /* @__PURE__ */ jsx12(CoderCheck, { onExit: () => {
7988
+ /* @__PURE__ */ jsx13(CoderCheck, { onExit: () => {
7016
7989
  unmount();
7017
7990
  resolve2();
7018
7991
  } })
@@ -7392,12 +8365,12 @@ import { render as render4 } from "ink";
7392
8365
  import chalk5 from "chalk";
7393
8366
 
7394
8367
  // src/stock/StockList.tsx
7395
- import { Box as Box13, Text as Text14, useInput as useInput5 } from "ink";
8368
+ import { Box as Box14, Text as Text15, useInput as useInput5 } from "ink";
7396
8369
  import { useState as useState8, useCallback as useCallback6, useEffect as useEffect7, useMemo as useMemo2 } from "react";
7397
8370
  import asciichart from "asciichart";
7398
8371
  import os from "os";
7399
8372
  import { join as join10 } from "path";
7400
- import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
8373
+ import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
7401
8374
  var SETTINGS_PATH = join10(os.homedir(), ".dskcode", "settings.json");
7402
8375
  function toFileUrl(p) {
7403
8376
  const norm = p.replace(/\\/g, "/");
@@ -7616,64 +8589,64 @@ function StockList({ codes, onExit, onBackToChat }) {
7616
8589
  );
7617
8590
  if (detailView) {
7618
8591
  if (detailLoading) {
7619
- return /* @__PURE__ */ jsx13(Box13, { paddingLeft: 1, children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: " \u27F3 \u52A0\u8F7D\u5206\u65F6\u6570\u636E..." }) });
8592
+ return /* @__PURE__ */ jsx14(Box14, { paddingLeft: 1, children: /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: " \u27F3 \u52A0\u8F7D\u5206\u65F6\u6570\u636E..." }) });
7620
8593
  }
7621
8594
  return renderDetail(detailView, () => setDetailView(null), detailPrices ?? void 0, detailCountdown, currentTime);
7622
8595
  }
7623
8596
  const cp2 = (c) => dimMode ? { dimColor: true } : { color: c };
7624
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
7625
- /* @__PURE__ */ jsxs13(Box13, { marginBottom: 1, justifyContent: "space-between", children: [
7626
- /* @__PURE__ */ jsx13(Text14, { bold: true, ...cp2("#00ffff"), children: " \u{1F4C8} \u81EA\u9009\u80A1\u76D1\u63A7" }),
7627
- /* @__PURE__ */ jsxs13(Text14, { dimColor: true, children: [
8597
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
8598
+ /* @__PURE__ */ jsxs14(Box14, { marginBottom: 1, justifyContent: "space-between", children: [
8599
+ /* @__PURE__ */ jsx14(Text15, { bold: true, ...cp2("#00ffff"), children: " \u{1F4C8} \u81EA\u9009\u80A1\u76D1\u63A7" }),
8600
+ /* @__PURE__ */ jsxs14(Text15, { dimColor: true, children: [
7628
8601
  " \u{1F550} ",
7629
8602
  currentTime
7630
8603
  ] }),
7631
- /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: loading ? " \u27F3 \u5237\u65B0\u4E2D..." : ` ${countdown}s \u540E\u81EA\u52A8\u5237\u65B0` })
8604
+ /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: loading ? " \u27F3 \u5237\u65B0\u4E2D..." : ` ${countdown}s \u540E\u81EA\u52A8\u5237\u65B0` })
7632
8605
  ] }),
7633
- /* @__PURE__ */ jsxs13(Box13, { children: [
7634
- /* @__PURE__ */ jsx13(Box13, { width: 3 }),
7635
- /* @__PURE__ */ jsx13(Box13, { width: 9, children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: "\u4EE3\u7801" }) }),
7636
- /* @__PURE__ */ jsx13(Box13, { width: 16, children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: "\u540D\u79F0" }) }),
7637
- /* @__PURE__ */ jsx13(Box13, { width: 12, children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: "\u6700\u65B0\u4EF7" }) }),
7638
- /* @__PURE__ */ jsx13(Box13, { width: 12, children: /* @__PURE__ */ jsxs13(Text14, { dimColor: true, children: [
8606
+ /* @__PURE__ */ jsxs14(Box14, { children: [
8607
+ /* @__PURE__ */ jsx14(Box14, { width: 3 }),
8608
+ /* @__PURE__ */ jsx14(Box14, { width: 9, children: /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: "\u4EE3\u7801" }) }),
8609
+ /* @__PURE__ */ jsx14(Box14, { width: 16, children: /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: "\u540D\u79F0" }) }),
8610
+ /* @__PURE__ */ jsx14(Box14, { width: 12, children: /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: "\u6700\u65B0\u4EF7" }) }),
8611
+ /* @__PURE__ */ jsx14(Box14, { width: 12, children: /* @__PURE__ */ jsxs14(Text15, { dimColor: true, children: [
7639
8612
  "\u6DA8\u8DCC\u5E45",
7640
8613
  sortOrder === "desc" ? " \u25BC" : sortOrder === "asc" ? " \u25B2" : ""
7641
8614
  ] }) }),
7642
- /* @__PURE__ */ jsx13(Box13, { width: 12, children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: "\u6DA8\u8DCC\u989D" }) }),
7643
- /* @__PURE__ */ jsx13(Box13, { width: 12, children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: "\u6700\u9AD8" }) }),
7644
- /* @__PURE__ */ jsx13(Box13, { width: 12, children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: "\u6700\u4F4E" }) }),
7645
- /* @__PURE__ */ jsx13(Box13, { children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: "\u6210\u4EA4\u989D" }) })
8615
+ /* @__PURE__ */ jsx14(Box14, { width: 12, children: /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: "\u6DA8\u8DCC\u989D" }) }),
8616
+ /* @__PURE__ */ jsx14(Box14, { width: 12, children: /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: "\u6700\u9AD8" }) }),
8617
+ /* @__PURE__ */ jsx14(Box14, { width: 12, children: /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: "\u6700\u4F4E" }) }),
8618
+ /* @__PURE__ */ jsx14(Box14, { children: /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: "\u6210\u4EA4\u989D" }) })
7646
8619
  ] }),
7647
- /* @__PURE__ */ jsx13(Box13, { children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: " " + "\u2500".repeat(100) }) }),
7648
- /* @__PURE__ */ jsx13(Box13, { flexDirection: "column", children: sortedStocks.map((stock, index) => {
8620
+ /* @__PURE__ */ jsx14(Box14, { children: /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: " " + "\u2500".repeat(100) }) }),
8621
+ /* @__PURE__ */ jsx14(Box14, { flexDirection: "column", children: sortedStocks.map((stock, index) => {
7649
8622
  const isSelected = index === selectedIndex;
7650
8623
  const isUp = stock.changePercent >= 0;
7651
8624
  const color = isUp ? "#ff1493" : "#00ff41";
7652
- return /* @__PURE__ */ jsxs13(Box13, { children: [
7653
- /* @__PURE__ */ jsx13(Box13, { width: 3, flexShrink: 0, children: isSelected ? /* @__PURE__ */ jsx13(Text14, { bold: true, ...cp2("#00ffff"), children: "\u25B8 " }) : /* @__PURE__ */ jsx13(Text14, { children: " " }) }),
7654
- /* @__PURE__ */ jsx13(Box13, { width: 9, children: /* @__PURE__ */ jsx13(Text14, { bold: true, ...cp2(isSelected ? "#00ffff" : "#ffffff"), children: stock.code }) }),
7655
- /* @__PURE__ */ jsx13(Box13, { width: 16, children: /* @__PURE__ */ jsx13(Text14, { ...cp2(isSelected ? "#ffffff" : "#cccccc"), children: stock.name }) }),
7656
- /* @__PURE__ */ jsx13(Box13, { width: 12, children: /* @__PURE__ */ jsx13(Text14, { bold: true, ...cp2(color), children: formatPrice(stock.price) }) }),
7657
- /* @__PURE__ */ jsx13(Box13, { width: 12, children: /* @__PURE__ */ jsxs13(Text14, { ...cp2(color), children: [
8625
+ return /* @__PURE__ */ jsxs14(Box14, { children: [
8626
+ /* @__PURE__ */ jsx14(Box14, { width: 3, flexShrink: 0, children: isSelected ? /* @__PURE__ */ jsx14(Text15, { bold: true, ...cp2("#00ffff"), children: "\u25B8 " }) : /* @__PURE__ */ jsx14(Text15, { children: " " }) }),
8627
+ /* @__PURE__ */ jsx14(Box14, { width: 9, children: /* @__PURE__ */ jsx14(Text15, { bold: true, ...cp2(isSelected ? "#00ffff" : "#ffffff"), children: stock.code }) }),
8628
+ /* @__PURE__ */ jsx14(Box14, { width: 16, children: /* @__PURE__ */ jsx14(Text15, { ...cp2(isSelected ? "#ffffff" : "#cccccc"), children: stock.name }) }),
8629
+ /* @__PURE__ */ jsx14(Box14, { width: 12, children: /* @__PURE__ */ jsx14(Text15, { bold: true, ...cp2(color), children: formatPrice(stock.price) }) }),
8630
+ /* @__PURE__ */ jsx14(Box14, { width: 12, children: /* @__PURE__ */ jsxs14(Text15, { ...cp2(color), children: [
7658
8631
  isUp ? "+" : "",
7659
8632
  stock.changePercent.toFixed(2),
7660
8633
  "%"
7661
8634
  ] }) }),
7662
- /* @__PURE__ */ jsx13(Box13, { width: 12, children: /* @__PURE__ */ jsxs13(Text14, { ...cp2(color), children: [
8635
+ /* @__PURE__ */ jsx14(Box14, { width: 12, children: /* @__PURE__ */ jsxs14(Text15, { ...cp2(color), children: [
7663
8636
  isUp ? "+" : "",
7664
8637
  stock.changeAmount.toFixed(3)
7665
8638
  ] }) }),
7666
- /* @__PURE__ */ jsx13(Box13, { width: 12, children: /* @__PURE__ */ jsx13(Text14, { ...cp2("#cccccc"), children: formatPrice(stock.high) }) }),
7667
- /* @__PURE__ */ jsx13(Box13, { width: 12, children: /* @__PURE__ */ jsx13(Text14, { ...cp2("#888888"), children: formatPrice(stock.low) }) }),
7668
- /* @__PURE__ */ jsx13(Box13, { children: /* @__PURE__ */ jsx13(Text14, { ...cp2("#888888"), children: formatAmount(stock.amount) }) })
8639
+ /* @__PURE__ */ jsx14(Box14, { width: 12, children: /* @__PURE__ */ jsx14(Text15, { ...cp2("#cccccc"), children: formatPrice(stock.high) }) }),
8640
+ /* @__PURE__ */ jsx14(Box14, { width: 12, children: /* @__PURE__ */ jsx14(Text15, { ...cp2("#888888"), children: formatPrice(stock.low) }) }),
8641
+ /* @__PURE__ */ jsx14(Box14, { children: /* @__PURE__ */ jsx14(Text15, { ...cp2("#888888"), children: formatAmount(stock.amount) }) })
7669
8642
  ] }, stock.code);
7670
8643
  }) }),
7671
- /* @__PURE__ */ jsx13(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: ` \u2191/\u2193 \u9009\u62E9 Enter \u8BE6\u60C5 r \u624B\u52A8\u5237\u65B0 o \u6392\u5E8F h \u7F6E\u7070/\u6062\u590D q \u8FD4\u56DE` }) }),
7672
- /* @__PURE__ */ jsxs13(Box13, { children: [
7673
- /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: ` \u6700\u540E\u66F4\u65B0: ${lastUpdate} \u7F16\u8F91\u81EA\u9009\u80A1: ` }),
7674
- /* @__PURE__ */ jsx13(Text14, { ...cp2("#c792ea"), children: osc8Link(toFileUrl(SETTINGS_PATH), SETTINGS_PATH) })
8644
+ /* @__PURE__ */ jsx14(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: ` \u2191/\u2193 \u9009\u62E9 Enter \u8BE6\u60C5 r \u624B\u52A8\u5237\u65B0 o \u6392\u5E8F h \u7F6E\u7070/\u6062\u590D q \u8FD4\u56DE` }) }),
8645
+ /* @__PURE__ */ jsxs14(Box14, { children: [
8646
+ /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: ` \u6700\u540E\u66F4\u65B0: ${lastUpdate} \u7F16\u8F91\u81EA\u9009\u80A1: ` }),
8647
+ /* @__PURE__ */ jsx14(Text15, { ...cp2("#c792ea"), children: osc8Link(toFileUrl(SETTINGS_PATH), SETTINGS_PATH) })
7675
8648
  ] }),
7676
- doubleCtrlC && /* @__PURE__ */ jsx13(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text14, { bold: true, ...cp2("#ff1493"), children: " \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode" }) })
8649
+ doubleCtrlC && /* @__PURE__ */ jsx14(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text15, { bold: true, ...cp2("#ff1493"), children: " \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode" }) })
7677
8650
  ] });
7678
8651
  }
7679
8652
  function renderDetail(stock, _onBack, prices, countdown = 10, currentTime) {
@@ -7691,33 +8664,33 @@ function renderDetail(stock, _onBack, prices, countdown = 10, currentTime) {
7691
8664
  raw = raw.replaceAll("\u256D", "\u250C").replaceAll("\u256E", "\u2510").replaceAll("\u2570", "\u2514").replaceAll("\u256F", "\u2518");
7692
8665
  chartLines = raw.split("\n");
7693
8666
  }
7694
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", paddingLeft: 1, children: [
7695
- /* @__PURE__ */ jsxs13(Box13, { marginBottom: 1, justifyContent: "space-between", children: [
7696
- /* @__PURE__ */ jsxs13(Box13, { children: [
7697
- /* @__PURE__ */ jsxs13(Text14, { bold: true, color: "#00ffff", children: [
8667
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", paddingLeft: 1, children: [
8668
+ /* @__PURE__ */ jsxs14(Box14, { marginBottom: 1, justifyContent: "space-between", children: [
8669
+ /* @__PURE__ */ jsxs14(Box14, { children: [
8670
+ /* @__PURE__ */ jsxs14(Text15, { bold: true, color: "#00ffff", children: [
7698
8671
  " \u{1F4CA} ",
7699
8672
  stock.name,
7700
8673
  " "
7701
8674
  ] }),
7702
- /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: stock.code }),
7703
- currentTime && /* @__PURE__ */ jsxs13(Text14, { dimColor: true, children: [
8675
+ /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: stock.code }),
8676
+ currentTime && /* @__PURE__ */ jsxs14(Text15, { dimColor: true, children: [
7704
8677
  " \u{1F550} ",
7705
8678
  currentTime
7706
8679
  ] })
7707
8680
  ] }),
7708
- /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: `${countdown}s \u540E\u5237\u65B0` })
8681
+ /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: `${countdown}s \u540E\u5237\u65B0` })
7709
8682
  ] }),
7710
- /* @__PURE__ */ jsxs13(Box13, { children: [
7711
- /* @__PURE__ */ jsx13(Box13, { width: 16, children: /* @__PURE__ */ jsx13(Text14, { bold: true, color: "#888888", children: "\u5F53\u524D\u4EF7" }) }),
7712
- /* @__PURE__ */ jsx13(Box13, { children: /* @__PURE__ */ jsxs13(Text14, { bold: true, color: colorCode, children: [
8683
+ /* @__PURE__ */ jsxs14(Box14, { children: [
8684
+ /* @__PURE__ */ jsx14(Box14, { width: 16, children: /* @__PURE__ */ jsx14(Text15, { bold: true, color: "#888888", children: "\u5F53\u524D\u4EF7" }) }),
8685
+ /* @__PURE__ */ jsx14(Box14, { children: /* @__PURE__ */ jsxs14(Text15, { bold: true, color: colorCode, children: [
7713
8686
  arrow,
7714
8687
  " ",
7715
8688
  formatPrice(stock.price)
7716
8689
  ] }) })
7717
8690
  ] }),
7718
- /* @__PURE__ */ jsxs13(Box13, { children: [
7719
- /* @__PURE__ */ jsx13(Box13, { width: 16, children: /* @__PURE__ */ jsx13(Text14, { color: "#888888", children: "\u6DA8\u8DCC\u5E45" }) }),
7720
- /* @__PURE__ */ jsx13(Box13, { children: /* @__PURE__ */ jsxs13(Text14, { color: colorCode, children: [
8691
+ /* @__PURE__ */ jsxs14(Box14, { children: [
8692
+ /* @__PURE__ */ jsx14(Box14, { width: 16, children: /* @__PURE__ */ jsx14(Text15, { color: "#888888", children: "\u6DA8\u8DCC\u5E45" }) }),
8693
+ /* @__PURE__ */ jsx14(Box14, { children: /* @__PURE__ */ jsxs14(Text15, { color: colorCode, children: [
7721
8694
  isUp ? "+" : "",
7722
8695
  stock.changePercent.toFixed(2),
7723
8696
  "%",
@@ -7726,8 +8699,8 @@ function renderDetail(stock, _onBack, prices, countdown = 10, currentTime) {
7726
8699
  stock.changeAmount.toFixed(3)
7727
8700
  ] }) })
7728
8701
  ] }),
7729
- chartLines.length > 0 && /* @__PURE__ */ jsx13(Box13, { marginTop: 1, flexDirection: "column", children: chartLines.map((line, i) => /* @__PURE__ */ jsx13(Box13, { children: /* @__PURE__ */ jsx13(Text14, { color: colorCode, children: line || " " }) }, i)) }),
7730
- /* @__PURE__ */ jsx13(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: " Space/q \u8FD4\u56DE\u5217\u8868" }) })
8702
+ chartLines.length > 0 && /* @__PURE__ */ jsx14(Box14, { marginTop: 1, flexDirection: "column", children: chartLines.map((line, i) => /* @__PURE__ */ jsx14(Box14, { children: /* @__PURE__ */ jsx14(Text15, { color: colorCode, children: line || " " }) }, i)) }),
8703
+ /* @__PURE__ */ jsx14(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: " Space/q \u8FD4\u56DE\u5217\u8868" }) })
7731
8704
  ] });
7732
8705
  }
7733
8706
 
@@ -7812,7 +8785,7 @@ async function scanProjectFiles(baseDir, dir) {
7812
8785
  }
7813
8786
 
7814
8787
  // src/cli/index.tsx
7815
- import { jsx as jsx14 } from "react/jsx-runtime";
8788
+ import { jsx as jsx15 } from "react/jsx-runtime";
7816
8789
  var SUBCOMMANDS = ["chat", "run", "setup", "init", "completion", "game", "stock"];
7817
8790
  function createCli() {
7818
8791
  const program2 = new Command();
@@ -7914,7 +8887,7 @@ compdef _dskcode_completion dskcode`);
7914
8887
  const freshResult = await loadAndValidate();
7915
8888
  const codeList = codes && codes.length > 0 ? codes : freshResult.config.stock?.symbols?.map((s) => s.code) ?? ["sh000001", "sz399006", "sh601688"];
7916
8889
  const app = renderApp(
7917
- /* @__PURE__ */ jsx14(
8890
+ /* @__PURE__ */ jsx15(
7918
8891
  StockList,
7919
8892
  {
7920
8893
  codes: codeList,
@@ -7943,7 +8916,7 @@ compdef _dskcode_completion dskcode`);
7943
8916
  }
7944
8917
  const selectedGame = await new Promise((resolve2) => {
7945
8918
  const { unmount } = render4(
7946
- /* @__PURE__ */ jsx14(
8919
+ /* @__PURE__ */ jsx15(
7947
8920
  GamePicker,
7948
8921
  {
7949
8922
  games,
@@ -7983,7 +8956,7 @@ async function startChat(ctx, costTracker) {
7983
8956
  );
7984
8957
  const model = defaultProvider?.model ?? "deepseek-v4-flash";
7985
8958
  const chatApp = renderApp(
7986
- /* @__PURE__ */ jsx14(
8959
+ /* @__PURE__ */ jsx15(
7987
8960
  ChatSession,
7988
8961
  {
7989
8962
  skillCount,
@@ -8001,7 +8974,7 @@ async function startChat(ctx, costTracker) {
8001
8974
  initGames();
8002
8975
  const games = listGames();
8003
8976
  const { unmount } = render4(
8004
- /* @__PURE__ */ jsx14(
8977
+ /* @__PURE__ */ jsx15(
8005
8978
  GamePicker,
8006
8979
  {
8007
8980
  games,
@@ -8025,7 +8998,7 @@ async function startChat(ctx, costTracker) {
8025
8998
  setImmediate(() => {
8026
8999
  const defaultStockCodes = ctx?.config.stock?.symbols?.map((s) => s.code) ?? ["sh000001", "sz399006", "sh601688"];
8027
9000
  const stockApp = renderApp(
8028
- /* @__PURE__ */ jsx14(
9001
+ /* @__PURE__ */ jsx15(
8029
9002
  StockList,
8030
9003
  {
8031
9004
  codes: defaultStockCodes,