dskcode 0.1.30 → 0.1.31

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";
@@ -1179,17 +1179,17 @@ function ToolCallBlock({ call, showPendingHint = true }) {
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 }) }),
1192
+ showPendingHint && /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Text4, { dimColor: true, children: "\u23F3 \u7B49\u5F85\u6267\u884C" }) })
1193
1193
  ] });
1194
1194
  }
1195
1195
 
@@ -1278,6 +1278,226 @@ function formatUsageSummary(usage) {
1278
1278
 
1279
1279
  // src/ui/HighlightedText.tsx
1280
1280
  import { Box as Box4, Text as Text5 } from "ink";
1281
+
1282
+ // src/ui/table-layout.ts
1283
+ var TABLE_MIN_COL_WIDTH = 3;
1284
+ var DEFAULT_TERM_WIDTH = 80;
1285
+ var TABLE_MARGIN = 2;
1286
+ function cpWidth(cp2) {
1287
+ if (cp2 >= 4352 && cp2 <= 4447 || // Hangul Jamo
1288
+ cp2 === 9001 || cp2 === 9002 || cp2 >= 11904 && cp2 <= 42191 || // CJK Radicals ~ Yi
1289
+ cp2 >= 43360 && cp2 <= 43391 || // Hangul Extended
1290
+ cp2 >= 44032 && cp2 <= 55215 || // Hangul Syllables
1291
+ cp2 >= 63744 && cp2 <= 64255 || // CJK Compat
1292
+ cp2 >= 65040 && cp2 <= 65055 || // Vertical forms
1293
+ cp2 >= 65281 && cp2 <= 65376 || // Fullwidth ASCII / 标点
1294
+ cp2 >= 65504 && cp2 <= 65510 || // Fullwidth signs
1295
+ cp2 >= 126976 && cp2 <= 131071 || // Emoji / Symbols
1296
+ cp2 >= 12288 && cp2 <= 12351 || // CJK Symbols & Punctuation(含「、。·」等)
1297
+ cp2 >= 131072 && cp2 <= 196607) {
1298
+ return 2;
1299
+ }
1300
+ if (cp2 < 32 || cp2 >= 127 && cp2 < 160) return 0;
1301
+ return 1;
1302
+ }
1303
+ function visualWidth(text) {
1304
+ let w = 0;
1305
+ for (const ch of text) {
1306
+ const cp2 = ch.codePointAt(0);
1307
+ if (cp2 === void 0) continue;
1308
+ if (cp2 === 8205) continue;
1309
+ if (cp2 >= 65024 && cp2 <= 65039) continue;
1310
+ w += cpWidth(cp2);
1311
+ }
1312
+ return w;
1313
+ }
1314
+ function* iterateChars(text) {
1315
+ for (const ch of text) {
1316
+ const cp2 = ch.codePointAt(0);
1317
+ if (cp2 === void 0) continue;
1318
+ if (cp2 === 8205) continue;
1319
+ if (cp2 >= 65024 && cp2 <= 65039) continue;
1320
+ yield { ch, width: cpWidth(cp2) };
1321
+ }
1322
+ }
1323
+ function wrapByWidth(text, maxWidth) {
1324
+ if (maxWidth <= 0) return [text];
1325
+ const out = [];
1326
+ for (const para of text.split("\n")) {
1327
+ if (para.length === 0) {
1328
+ out.push("");
1329
+ continue;
1330
+ }
1331
+ const wrapped = wrapOneLine(para, maxWidth);
1332
+ for (const w of wrapped) out.push(w);
1333
+ }
1334
+ return out.length === 0 ? [""] : out;
1335
+ }
1336
+ function wrapOneLine(text, maxWidth) {
1337
+ if (visualWidth(text) <= maxWidth) return [text];
1338
+ const lines = [];
1339
+ let current = "";
1340
+ let currentW = 0;
1341
+ let breakCandidate = -1;
1342
+ let breakCandidateW = 0;
1343
+ for (const { ch, width } of iterateChars(text)) {
1344
+ if (ch === " " || ch === " ") {
1345
+ breakCandidate = current.length;
1346
+ breakCandidateW = currentW + width;
1347
+ current += ch;
1348
+ currentW += width;
1349
+ continue;
1350
+ }
1351
+ if (currentW + width > maxWidth) {
1352
+ if (breakCandidate > 0) {
1353
+ const split = current.slice(0, breakCandidate).replace(/[ \t]+$/, "");
1354
+ lines.push(split);
1355
+ const rest = current.slice(breakCandidate).replace(/^[ \t]+/, "");
1356
+ current = rest + ch;
1357
+ currentW = visualWidth(current);
1358
+ breakCandidate = -1;
1359
+ breakCandidateW = 0;
1360
+ } else {
1361
+ lines.push(current);
1362
+ current = ch;
1363
+ currentW = width;
1364
+ }
1365
+ } else {
1366
+ current += ch;
1367
+ currentW += width;
1368
+ }
1369
+ }
1370
+ if (current.length > 0) lines.push(current);
1371
+ return lines;
1372
+ }
1373
+ function wrapRowCells(cells, colWidths, alignments) {
1374
+ return cells.map((cell, ci) => {
1375
+ const w = colWidths[ci] ?? TABLE_MIN_COL_WIDTH;
1376
+ const lines = wrapByWidth(cell, w);
1377
+ return lines.map((l) => padToWidth(l, w, alignments[ci] ?? "left"));
1378
+ });
1379
+ }
1380
+ function padToWidth(text, targetWidth, align = "left") {
1381
+ const vw = visualWidth(text);
1382
+ const delta = targetWidth - vw;
1383
+ if (delta <= 0) return text;
1384
+ const leftPad = align === "right" ? delta : align === "center" ? Math.floor(delta / 2) : 0;
1385
+ const rightPad = delta - leftPad;
1386
+ return " ".repeat(leftPad) + text + " ".repeat(rightPad);
1387
+ }
1388
+ function parseTableCells(line) {
1389
+ const t = line.trim();
1390
+ if (!t.startsWith("|") || !t.endsWith("|")) return [];
1391
+ const inner = t.slice(1, t.length - 1);
1392
+ return inner.split("|").map((c) => c.trim());
1393
+ }
1394
+ function parseAlignments(sepLine) {
1395
+ return parseTableCells(sepLine).map((c) => {
1396
+ const l = c.startsWith(":");
1397
+ const r = c.endsWith(":");
1398
+ if (l && r) return "center";
1399
+ if (r) return "right";
1400
+ return "left";
1401
+ });
1402
+ }
1403
+ function layoutTable(text, options = {}) {
1404
+ const termWidth = options.termWidth ?? DEFAULT_TERM_WIDTH;
1405
+ const outerMargin = options.outerMargin ?? 0;
1406
+ const usable = Math.max(termWidth - outerMargin, 20);
1407
+ const rawLines = text.split("\n").filter((l) => l.trim().length > 0);
1408
+ if (rawLines.length < 2) {
1409
+ return { lines: rawLines, colWidths: [], totalWidth: 0 };
1410
+ }
1411
+ let sepIdx = -1;
1412
+ for (let k = 0; k < rawLines.length; k++) {
1413
+ const t = (rawLines[k] ?? "").trim();
1414
+ if (/^\|[-: |]+\|$/.test(t) && t.includes("-")) {
1415
+ sepIdx = k;
1416
+ break;
1417
+ }
1418
+ }
1419
+ if (sepIdx === -1) {
1420
+ return { lines: rawLines, colWidths: [], totalWidth: 0 };
1421
+ }
1422
+ const headerCells = parseTableCells(rawLines.slice(0, sepIdx).join(""));
1423
+ const alignments = parseAlignments(rawLines[sepIdx] ?? "");
1424
+ const dataRows = rawLines.slice(sepIdx + 1).map(parseTableCells);
1425
+ const colCount = headerCells.length;
1426
+ if (colCount === 0) {
1427
+ return { lines: rawLines, colWidths: [], totalWidth: 0 };
1428
+ }
1429
+ const colMaxWidths = [];
1430
+ for (let ci = 0; ci < colCount; ci++) {
1431
+ let max = visualWidth(headerCells[ci] ?? "");
1432
+ for (const row of dataRows) {
1433
+ const w = visualWidth(row[ci] ?? "");
1434
+ if (w > max) max = w;
1435
+ }
1436
+ colMaxWidths.push(Math.max(max, TABLE_MIN_COL_WIDTH));
1437
+ }
1438
+ const margin = TABLE_MARGIN;
1439
+ const totalMinWidth = colMaxWidths.reduce((s, w) => s + w + margin, 1);
1440
+ let colWidths;
1441
+ if (totalMinWidth <= usable) {
1442
+ colWidths = colMaxWidths;
1443
+ } else {
1444
+ const available = usable - 1 - margin * colCount;
1445
+ const sum = colMaxWidths.reduce((s, w) => s + w, 0);
1446
+ colWidths = colMaxWidths.map(
1447
+ (w) => Math.max(TABLE_MIN_COL_WIDTH, Math.floor(w / sum * available))
1448
+ );
1449
+ let total = colWidths.reduce((s, w) => s + w + margin, 1);
1450
+ while (total > usable && colWidths.length > 0) {
1451
+ const last = colWidths.length - 1;
1452
+ const cur = colWidths[last] ?? TABLE_MIN_COL_WIDTH;
1453
+ if (cur <= TABLE_MIN_COL_WIDTH) break;
1454
+ colWidths[last] = cur - 1;
1455
+ total = colWidths.reduce((s, w) => s + w + margin, 1);
1456
+ }
1457
+ }
1458
+ const H = "\u2500";
1459
+ const makeSep = (l, m, r) => l + colWidths.map((w) => H.repeat(w + margin)).join(m) + r;
1460
+ const topBorder = makeSep("\u250C", "\u252C", "\u2510");
1461
+ const midBorder = makeSep("\u251C", "\u253C", "\u2524");
1462
+ const botBorder = makeSep("\u2514", "\u2534", "\u2518");
1463
+ function buildRow(cells) {
1464
+ const wrapped = wrapRowCells(cells, colWidths, alignments);
1465
+ const rowHeight = Math.max(1, ...wrapped.map((lines2) => lines2.length));
1466
+ const lines = [];
1467
+ for (let li = 0; li < rowHeight; li++) {
1468
+ let row = "\u2502";
1469
+ for (let ci = 0; ci < colCount; ci++) {
1470
+ const cellLines = wrapped[ci] ?? [];
1471
+ const segment = cellLines[li] ?? " ".repeat(colWidths[ci] ?? TABLE_MIN_COL_WIDTH);
1472
+ row += " " + segment + " ";
1473
+ if (ci < colCount - 1) row += "\u2502";
1474
+ }
1475
+ row += "\u2502";
1476
+ lines.push(row);
1477
+ }
1478
+ return lines;
1479
+ }
1480
+ const headerRow = buildRow(headerCells);
1481
+ const dataRowsOut = dataRows.map(buildRow);
1482
+ const totalWidth = colWidths.reduce((s, w) => s + w + margin, 1);
1483
+ return {
1484
+ lines: [
1485
+ topBorder,
1486
+ ...headerRow,
1487
+ midBorder,
1488
+ ...dataRowsOut.flat(),
1489
+ botBorder
1490
+ ],
1491
+ colWidths,
1492
+ totalWidth
1493
+ };
1494
+ }
1495
+ function detectTermWidth() {
1496
+ const c = process.stdout?.columns;
1497
+ return typeof c === "number" && c > 0 ? c : void 0;
1498
+ }
1499
+
1500
+ // src/ui/HighlightedText.tsx
1281
1501
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1282
1502
  function parseCodeBlocks(text) {
1283
1503
  const segments = [];
@@ -1450,111 +1670,19 @@ function CodeBlockRenderer({ code }) {
1450
1670
  })
1451
1671
  ] });
1452
1672
  }
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
1673
  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 });
1674
+ const termWidth = detectTermWidth();
1675
+ const layout = layoutTable(text, { termWidth });
1676
+ if (layout.colWidths.length === 0) {
1677
+ return /* @__PURE__ */ jsx4(Text5, { children: text });
1678
+ }
1679
+ return /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", marginTop: 1, children: layout.lines.map((line, i) => {
1680
+ const isFirst = i === 0;
1681
+ const isLast = i === layout.lines.length - 1;
1682
+ const isMid = !isFirst && !isLast && line.startsWith("\u251C");
1683
+ const color = isFirst || isLast || isMid ? "#888888" : void 0;
1684
+ return /* @__PURE__ */ jsx4(Text5, { color, children: line }, i);
1685
+ }) });
1558
1686
  }
1559
1687
  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
1688
  function isEmojiCluster(text) {
@@ -1674,6 +1802,23 @@ function HighlightedText({ children: text }) {
1674
1802
  return /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", children: rendered });
1675
1803
  }
1676
1804
 
1805
+ // src/ui/reasoning-utils.ts
1806
+ var DEFAULT_REASONING_MAX_LINES = 8;
1807
+ function joinReasoningSegments(segments) {
1808
+ return segments.map((s) => s.trim()).filter((s) => s.length > 0).join("\n");
1809
+ }
1810
+ function truncateReasoningLines(text, maxLines) {
1811
+ const lines = text.split("\n");
1812
+ if (lines.length <= maxLines) {
1813
+ return { visible: text, hiddenLines: 0, totalLines: lines.length };
1814
+ }
1815
+ return {
1816
+ visible: lines.slice(-maxLines).join("\n"),
1817
+ hiddenLines: lines.length - maxLines,
1818
+ totalLines: lines.length
1819
+ };
1820
+ }
1821
+
1677
1822
  // src/ui/AssistantMessage.tsx
1678
1823
  import { Fragment, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1679
1824
  function formatElapsed(ms) {
@@ -1707,10 +1852,7 @@ function AnimatedUsage({ usage, cost }) {
1707
1852
  }
1708
1853
  const tokensDelta = targetTokens - displayedTokens;
1709
1854
  const costDelta = targetCost - displayedCost;
1710
- const durationMs = Math.min(
1711
- 600,
1712
- Math.max(220, Math.max(tokensDelta, 0) * 1.2 + 220)
1713
- );
1855
+ const durationMs = Math.min(600, Math.max(220, Math.max(tokensDelta, 0) * 1.2 + 220));
1714
1856
  startRef.current = {
1715
1857
  fromTokens: displayedTokens,
1716
1858
  fromCost: displayedCost,
@@ -1761,6 +1903,7 @@ function AnimatedUsage({ usage, cost }) {
1761
1903
  }
1762
1904
  function AssistantMessage({
1763
1905
  content,
1906
+ reasoning,
1764
1907
  toolCalls,
1765
1908
  isStreaming = false,
1766
1909
  usage,
@@ -1768,21 +1911,43 @@ function AssistantMessage({
1768
1911
  cost,
1769
1912
  model: _model
1770
1913
  }) {
1771
- if (!content && (!toolCalls || toolCalls.length === 0) && !isStreaming) {
1914
+ if (!content && (!toolCalls || toolCalls.length === 0) && (!reasoning || reasoning.length === 0) && !isStreaming) {
1772
1915
  return null;
1773
1916
  }
1774
1917
  return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, children: [
1918
+ reasoning && reasoning.length > 0 && (() => {
1919
+ const merged = joinReasoningSegments(reasoning);
1920
+ if (!merged) return null;
1921
+ const { visible } = truncateReasoningLines(
1922
+ merged,
1923
+ DEFAULT_REASONING_MAX_LINES
1924
+ );
1925
+ return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "row", marginBottom: 1, children: [
1926
+ /* @__PURE__ */ jsx5(Box5, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx5(Text6, { dimColor: true, children: "\u{1F9E0}" }) }),
1927
+ /* @__PURE__ */ jsx5(
1928
+ Box5,
1929
+ {
1930
+ flexGrow: 1,
1931
+ flexDirection: "column",
1932
+ borderStyle: "single",
1933
+ borderColor: "#444444",
1934
+ paddingLeft: 1,
1935
+ paddingRight: 1,
1936
+ children: /* @__PURE__ */ jsx5(Text6, { dimColor: true, wrap: "wrap", children: visible })
1937
+ }
1938
+ )
1939
+ ] });
1940
+ })(),
1775
1941
  /* @__PURE__ */ jsxs5(Box5, { flexDirection: "row", children: [
1776
1942
  /* @__PURE__ */ jsx5(Box5, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx5(Text6, { bold: true, color: "#ff00ff", children: "\u{1F916}" }) }),
1777
1943
  /* @__PURE__ */ jsxs5(Box5, { flexGrow: 1, flexDirection: "column", children: [
1778
1944
  content && /* @__PURE__ */ jsx5(HighlightedText, { children: content }),
1779
- isStreaming && !content && /* @__PURE__ */ jsx5(Text6, { color: "#888888", children: "..." })
1945
+ isStreaming && !content && (!reasoning || reasoning.length === 0) && /* @__PURE__ */ jsx5(Text6, { color: "#888888", children: "..." })
1780
1946
  ] })
1781
1947
  ] }),
1782
1948
  toolCalls && toolCalls.length > 0 && /* @__PURE__ */ jsx5(Box5, { flexDirection: "column", children: toolCalls.map((tc, i) => /* @__PURE__ */ jsx5(ToolCallBlock, { call: tc }, tc.id ?? i)) }),
1783
1949
  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
- " ",
1950
+ "\u23F3 \u5DF2\u6D88\u8017 ",
1786
1951
  /* @__PURE__ */ jsx5(AnimatedUsage, { usage, cost })
1787
1952
  ] }) }),
1788
1953
  !isStreaming && (usage || elapsed !== void 0) && /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, marginLeft: 3, children: [
@@ -1891,6 +2056,49 @@ function FileSelector({ files, input, selectedIndex }) {
1891
2056
  ] });
1892
2057
  }
1893
2058
 
2059
+ // src/ui/TodoListPanel.tsx
2060
+ import { Box as Box9, Text as Text10 } from "ink";
2061
+ import InkSpinner2 from "ink-spinner";
2062
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
2063
+ function statusIcon(status) {
2064
+ switch (status) {
2065
+ case "pending":
2066
+ return "\u2610";
2067
+ case "done":
2068
+ return "\u2611";
2069
+ case "failed":
2070
+ return "\u2717";
2071
+ case "skipped":
2072
+ return "\u2298";
2073
+ // running 不走这里,由 TodoIcon 走 Spinner 组件
2074
+ case "running":
2075
+ return "\u25B6";
2076
+ }
2077
+ }
2078
+ function TodoIcon({ status }) {
2079
+ if (status === "running") {
2080
+ return /* @__PURE__ */ jsx9(Text10, { color: "cyan", children: /* @__PURE__ */ jsx9(InkSpinner2, { type: "dots" }) });
2081
+ }
2082
+ if (status === "done") {
2083
+ return /* @__PURE__ */ jsx9(Text10, { color: "green", children: "\u2705" });
2084
+ }
2085
+ if (status === "failed") {
2086
+ return /* @__PURE__ */ jsx9(Text10, { color: "red", children: statusIcon(status) });
2087
+ }
2088
+ return /* @__PURE__ */ jsx9(Text10, { dimColor: true, children: statusIcon(status) });
2089
+ }
2090
+ function TodoListPanel({ items }) {
2091
+ if (items.length === 0) return null;
2092
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginTop: 1, children: [
2093
+ /* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsx9(Text10, { dimColor: true, children: "\u{1F527} \u4EFB\u52A1\u8FDB\u5EA6" }) }),
2094
+ items.map((it) => /* @__PURE__ */ jsxs9(Box9, { children: [
2095
+ /* @__PURE__ */ jsx9(Text10, { dimColor: true, children: " " }),
2096
+ /* @__PURE__ */ jsx9(TodoIcon, { status: it.status }),
2097
+ /* @__PURE__ */ jsx9(Text10, { dimColor: true, children: ` ${it.content}` })
2098
+ ] }, it.id))
2099
+ ] });
2100
+ }
2101
+
1894
2102
  // src/provider/registry.ts
1895
2103
  var ProviderRegistry = class {
1896
2104
  #factories = /* @__PURE__ */ new Map();
@@ -1985,10 +2193,10 @@ var AlwaysAllowGate = class {
1985
2193
  import Handlebars from "handlebars";
1986
2194
 
1987
2195
  // 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';
2196
+ 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
2197
 
1990
2198
  // 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";
2199
+ 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
2200
 
1993
2201
  // src/agent/system-prompt.ts
1994
2202
  var compiledTemplate = Handlebars.compile(system_prompt_default);
@@ -2581,68 +2789,492 @@ function buildToolDefinitions(registry2, mode) {
2581
2789
  }));
2582
2790
  }
2583
2791
 
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 });
2792
+ // src/harness/todo-list.ts
2793
+ var TodoList = class {
2794
+ #items = [];
2795
+ #nextId = 0;
2796
+ /**
2797
+ * 新增一个 todo,返回分配的 id。
2798
+ *
2799
+ * @param content 步骤描述(中文)
2800
+ * @param deps — 依赖的 todo id 列表;为空表示无依赖,立即可 running
2801
+ * @returns 新 todo 的 id
2802
+ * @throws 当 deps 引用了不存在的 id 时抛出,避免 LLM 错把不存在的依赖塞进计划
2803
+ */
2804
+ add(content, deps = []) {
2805
+ for (const d of deps) {
2806
+ if (!this.#items.find((it) => it.id === d)) {
2807
+ 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`);
2808
+ }
2809
+ }
2810
+ const id = this.#nextId++;
2811
+ this.#items.push({
2812
+ id,
2813
+ content,
2814
+ status: "pending",
2815
+ deps: [...deps],
2816
+ updatedAt: Date.now()
2817
+ });
2818
+ return id;
2819
+ }
2820
+ /**
2821
+ * 把失败的 todo 重置回 pending(用于"重试"工作流)。
2822
+ *
2823
+ * 不允许把 done / skipped 重置(一旦完成不应反悔)。
2824
+ * 不允许把 running 重置(应该先 markFailed 再重试)。
2825
+ *
2826
+ * @param id — todo id
2827
+ * @returns 是否成功
2828
+ */
2829
+ resetForRetry(id) {
2830
+ const item = this.#find(id);
2831
+ if (!item) return false;
2832
+ if (item.status !== "failed") return false;
2833
+ item.status = "pending";
2834
+ item.evidence = void 0;
2835
+ item.updatedAt = Date.now();
2592
2836
  return true;
2593
- } catch {
2594
- return false;
2595
2837
  }
2596
- }
2597
- async function hasCommits(cwd) {
2598
- try {
2599
- await execFileAsync("git", ["rev-parse", "--verify", "HEAD"], { cwd, ...EXEC_OPTIONS });
2838
+ /**
2839
+ * todo 标记为 running。
2840
+ *
2841
+ * 前提:该 todo 存在 + 当前是 pending + 依赖全 done。
2842
+ * 不满足时静默返回 false(不抛错,避免污染 agent 主循环)。
2843
+ *
2844
+ * @param id — todo id
2845
+ * @returns 是否成功转换
2846
+ */
2847
+ markRunning(id) {
2848
+ const item = this.#find(id);
2849
+ if (!item) return false;
2850
+ if (item.status !== "pending") return false;
2851
+ if (!this.#depsAllDone(item.deps)) return false;
2852
+ item.status = "running";
2853
+ item.updatedAt = Date.now();
2600
2854
  return true;
2601
- } catch {
2602
- return false;
2603
2855
  }
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}`);
2856
+ /**
2857
+ * 标记完成。
2858
+ *
2859
+ * @param id — todo id
2860
+ * @param evidence — 完成证据(如 "读取成功"、"type-check 通过")
2861
+ * @returns 是否成功
2862
+ */
2863
+ markDone(id, evidence) {
2864
+ const item = this.#find(id);
2865
+ if (!item) return false;
2866
+ if (item.status !== "running" && item.status !== "pending") return false;
2867
+ item.status = "done";
2868
+ if (evidence !== void 0) item.evidence = evidence;
2869
+ item.updatedAt = Date.now();
2870
+ return true;
2616
2871
  }
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");
2639
- const { cwd, stashSha } = checkpoint;
2640
- const currentShas = await listStashShas(cwd);
2641
- if (!currentShas.includes(stashSha)) {
2642
- throw new Error("\u68C0\u67E5\u70B9\u5DF2\u5931\u6548\uFF08stash entry \u5DF2\u88AB\u6D88\u8D39\u6216 GC\uFF09\uFF0C\u65E0\u6CD5\u6062\u590D");
2872
+ /**
2873
+ * 标记失败。
2874
+ *
2875
+ * @param id — todo id
2876
+ * @param reason — 失败原因(人类可读)
2877
+ * @returns 是否成功
2878
+ */
2879
+ markFailed(id, reason) {
2880
+ const item = this.#find(id);
2881
+ if (!item) return false;
2882
+ if (item.status === "done") return false;
2883
+ item.status = "failed";
2884
+ item.evidence = reason;
2885
+ item.updatedAt = Date.now();
2886
+ return true;
2643
2887
  }
2644
- await git(["checkout", "--", "."], cwd);
2645
- await git(["clean", "-fd"], cwd);
2888
+ /**
2889
+ * 标记跳过。
2890
+ *
2891
+ * @param id — todo id
2892
+ * @param reason — 跳过原因(如 "项目无测试基建")
2893
+ * @returns 是否成功
2894
+ */
2895
+ markSkipped(id, reason) {
2896
+ const item = this.#find(id);
2897
+ if (!item) return false;
2898
+ if (item.status === "done" || item.status === "failed") return false;
2899
+ item.status = "skipped";
2900
+ item.evidence = reason;
2901
+ item.updatedAt = Date.now();
2902
+ return true;
2903
+ }
2904
+ /**
2905
+ * 取出所有"立即可做"的 todo(deps 全 done 且 pending)。
2906
+ *
2907
+ * @returns 可执行的 todo 列表
2908
+ */
2909
+ pending() {
2910
+ return this.#items.filter(
2911
+ (it) => it.status === "pending" && this.#depsAllDone(it.deps)
2912
+ );
2913
+ }
2914
+ /**
2915
+ * 取出所有未完成项(pending / running / failed)。
2916
+ */
2917
+ unfinished() {
2918
+ return this.#items.filter(
2919
+ (it) => it.status === "pending" || it.status === "running" || it.status === "failed"
2920
+ );
2921
+ }
2922
+ /**
2923
+ * 是否全部结束(done / failed / skipped)。
2924
+ */
2925
+ isAllTerminated() {
2926
+ return this.#items.every(
2927
+ (it) => it.status === "done" || it.status === "failed" || it.status === "skipped"
2928
+ );
2929
+ }
2930
+ /** 全部条目(只读快照) */
2931
+ get items() {
2932
+ return this.#items;
2933
+ }
2934
+ /**
2935
+ * 把 todo 列表拼成 markdown,用于注入 system prompt。
2936
+ *
2937
+ * @param maxItems — 最多展示多少条;超出时保留"最近 N 条 + 进行中",折叠已完成
2938
+ * @returns markdown 字符串
2939
+ */
2940
+ toMarkdown(maxItems = 20) {
2941
+ if (this.#items.length === 0) return "";
2942
+ const active = this.#items.filter(
2943
+ (it) => it.status === "pending" || it.status === "running" || it.status === "failed"
2944
+ );
2945
+ const finished = this.#items.filter(
2946
+ (it) => it.status === "done" || it.status === "skipped"
2947
+ );
2948
+ const finishedSorted = [...finished].sort((a, b) => b.updatedAt - a.updatedAt);
2949
+ const truncated = finishedSorted.length > maxItems;
2950
+ const finishedToShow = truncated ? finishedSorted.slice(0, Math.max(0, maxItems - active.length)) : finishedSorted;
2951
+ const lines = [...active, ...finishedToShow].map((it) => {
2952
+ const box = this.#statusBox(it.status);
2953
+ const depStr = it.deps.length > 0 ? ` (\u4F9D\u8D56: #${it.deps.join(", #")})` : "";
2954
+ const eviStr = it.evidence ? ` \u2014 ${it.evidence}` : "";
2955
+ return `- ${box} #${it.id} ${it.content}${depStr}${eviStr}`;
2956
+ });
2957
+ if (truncated) {
2958
+ const hidden = finishedSorted.length - finishedToShow.length;
2959
+ lines.push(`- ...\uFF08\u53E6\u6709 ${hidden} \u6761\u5DF2\u5B8C\u6210\u5DF2\u6298\u53E0\uFF09`);
2960
+ }
2961
+ return [
2962
+ "## \u{1F4CB} \u5F53\u524D\u4EFB\u52A1\u8FDB\u5EA6",
2963
+ ...lines,
2964
+ "",
2965
+ "\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"
2966
+ ].join("\n");
2967
+ }
2968
+ // -------------------------------------------------------------------------
2969
+ // 内部
2970
+ // -------------------------------------------------------------------------
2971
+ #find(id) {
2972
+ return this.#items.find((it) => it.id === id);
2973
+ }
2974
+ #depsAllDone(deps) {
2975
+ return deps.every((d) => {
2976
+ const item = this.#items.find((it) => it.id === d);
2977
+ return item?.status === "done" || item?.status === "skipped";
2978
+ });
2979
+ }
2980
+ #statusBox(status) {
2981
+ switch (status) {
2982
+ case "pending":
2983
+ return "\u2610";
2984
+ case "running":
2985
+ return "\u25B6";
2986
+ case "done":
2987
+ return "\u2705";
2988
+ case "failed":
2989
+ return "\u274C";
2990
+ case "skipped":
2991
+ return "\u23ED";
2992
+ }
2993
+ }
2994
+ };
2995
+
2996
+ // src/harness/tools.ts
2997
+ function createHarnessTools(todoList) {
2998
+ return [
2999
+ makeTodoAddTool(todoList),
3000
+ makeTodoMarkRunningTool(todoList),
3001
+ makeTodoMarkDoneTool(todoList),
3002
+ makeTodoMarkFailedTool(todoList),
3003
+ makeTodoRetryTool(todoList)
3004
+ ];
3005
+ }
3006
+ function makeTodoAddTool(todoList) {
3007
+ return {
3008
+ name: "todo_add",
3009
+ kind: "other" /* Other */,
3010
+ 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])',
3011
+ parameters: {
3012
+ type: "object",
3013
+ properties: {
3014
+ content: { type: "string", description: "\u6B65\u9AA4\u63CF\u8FF0\uFF08\u4E2D\u6587\uFF0C\u5EFA\u8BAE\u7B80\u6D01\uFF09" },
3015
+ deps: {
3016
+ type: "array",
3017
+ description: "\u4F9D\u8D56\u7684 todo id \u6570\u7EC4\uFF08\u8FD9\u4E9B\u90FD done \u540E\u672C\u9879\u624D\u53EF\u6267\u884C\uFF09",
3018
+ items: { type: "number" }
3019
+ }
3020
+ },
3021
+ required: ["content"],
3022
+ additionalProperties: false
3023
+ },
3024
+ async execute(args) {
3025
+ if (typeof args?.content !== "string" || args.content.length === 0) {
3026
+ return { success: false, data: "\u7F3A\u5C11 content \u53C2\u6570", error: "INVALID_ARGS" };
3027
+ }
3028
+ const deps = Array.isArray(args.deps) ? args.deps.filter((d) => typeof d === "number") : [];
3029
+ try {
3030
+ const id = todoList.add(args.content, deps);
3031
+ return {
3032
+ success: true,
3033
+ data: `\u5DF2\u6DFB\u52A0 todo #${id}\uFF1A${args.content}${deps.length > 0 ? `\uFF08\u4F9D\u8D56: ${deps.join(", ")})` : ""}`,
3034
+ summary: `\u{1F4CB} \u6DFB\u52A0 #${id}`
3035
+ };
3036
+ } catch (err) {
3037
+ return {
3038
+ success: false,
3039
+ data: err instanceof Error ? err.message : String(err),
3040
+ error: "TODO_DEPS_INVALID"
3041
+ };
3042
+ }
3043
+ }
3044
+ };
3045
+ }
3046
+ function makeTodoMarkRunningTool(todoList) {
3047
+ return {
3048
+ name: "todo_mark_running",
3049
+ kind: "other" /* Other */,
3050
+ 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",
3051
+ parameters: {
3052
+ type: "object",
3053
+ properties: {
3054
+ id: { type: "number", description: "todo \u7684 id" }
3055
+ },
3056
+ required: ["id"],
3057
+ additionalProperties: false
3058
+ },
3059
+ async execute(args) {
3060
+ if (typeof args?.id !== "number") {
3061
+ return { success: false, data: "\u7F3A\u5C11 id \u53C2\u6570", error: "INVALID_ARGS" };
3062
+ }
3063
+ const item = todoList.items.find((it) => it.id === args.id);
3064
+ if (!item) {
3065
+ return { success: false, data: `todo #${args.id} \u4E0D\u5B58\u5728`, error: "TODO_NOT_FOUND" };
3066
+ }
3067
+ if (item.status !== "pending") {
3068
+ return {
3069
+ success: false,
3070
+ data: `todo #${args.id} \u5F53\u524D\u72B6\u6001\u662F ${item.status}\uFF0C\u53EA\u6709 pending \u53EF\u4EE5\u63A8\u8FDB\u5230 running`,
3071
+ error: "TODO_INVALID_STATE"
3072
+ };
3073
+ }
3074
+ const ok = todoList.markRunning(args.id);
3075
+ if (!ok) {
3076
+ return {
3077
+ success: false,
3078
+ data: `todo #${args.id} \u4F9D\u8D56\u672A\u5168\u90E8\u5B8C\u6210\uFF08\u4F9D\u8D56: #${item.deps.join(", #")}\uFF09`,
3079
+ error: "TODO_DEPS_NOT_READY"
3080
+ };
3081
+ }
3082
+ return {
3083
+ success: true,
3084
+ data: `\u5DF2\u5F00\u59CB todo #${args.id}\uFF1A${item.content}`,
3085
+ summary: `\u25B6 \u5F00\u59CB #${args.id}`
3086
+ };
3087
+ }
3088
+ };
3089
+ }
3090
+ function makeTodoMarkDoneTool(todoList) {
3091
+ return {
3092
+ name: "todo_mark_done",
3093
+ kind: "other" /* Other */,
3094
+ 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",
3095
+ parameters: {
3096
+ type: "object",
3097
+ properties: {
3098
+ id: { type: "number", description: "todo \u7684 id" },
3099
+ evidence: { type: "string", description: "\u5B8C\u6210\u8BC1\u636E\uFF08\u4E00\u53E5\u8BDD\uFF0C\u53EF\u9009\uFF09" }
3100
+ },
3101
+ required: ["id"],
3102
+ additionalProperties: false
3103
+ },
3104
+ async execute(args) {
3105
+ if (typeof args?.id !== "number") {
3106
+ return { success: false, data: "\u7F3A\u5C11 id \u53C2\u6570", error: "INVALID_ARGS" };
3107
+ }
3108
+ const item = todoList.items.find((it) => it.id === args.id);
3109
+ if (!item) {
3110
+ return { success: false, data: `todo #${args.id} \u4E0D\u5B58\u5728`, error: "TODO_NOT_FOUND" };
3111
+ }
3112
+ if (item.status !== "running") {
3113
+ return {
3114
+ success: false,
3115
+ 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`,
3116
+ error: "TODO_INVALID_STATE"
3117
+ };
3118
+ }
3119
+ const ok = todoList.markDone(args.id, args.evidence);
3120
+ if (!ok) {
3121
+ return { success: false, data: `todo #${args.id} \u72B6\u6001\u8F6C\u6362\u5931\u8D25`, error: "TODO_INVALID" };
3122
+ }
3123
+ return {
3124
+ success: true,
3125
+ data: `\u5DF2\u6807\u8BB0 todo #${args.id} \u5B8C\u6210${args.evidence ? `\uFF1A${args.evidence}` : ""}`,
3126
+ summary: `\u2705 \u5B8C\u6210 #${args.id}`
3127
+ };
3128
+ }
3129
+ };
3130
+ }
3131
+ function makeTodoMarkFailedTool(todoList) {
3132
+ return {
3133
+ name: "todo_mark_failed",
3134
+ kind: "other" /* Other */,
3135
+ 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",
3136
+ parameters: {
3137
+ type: "object",
3138
+ properties: {
3139
+ id: { type: "number", description: "todo \u7684 id" },
3140
+ reason: { type: "string", description: "\u5931\u8D25\u539F\u56E0\uFF08\u4E00\u53E5\u8BDD\uFF09" }
3141
+ },
3142
+ required: ["id", "reason"],
3143
+ additionalProperties: false
3144
+ },
3145
+ async execute(args) {
3146
+ if (typeof args?.id !== "number") {
3147
+ return { success: false, data: "\u7F3A\u5C11 id \u53C2\u6570", error: "INVALID_ARGS" };
3148
+ }
3149
+ if (typeof args?.reason !== "string") {
3150
+ return { success: false, data: "\u7F3A\u5C11 reason \u53C2\u6570", error: "INVALID_ARGS" };
3151
+ }
3152
+ const item = todoList.items.find((it) => it.id === args.id);
3153
+ if (!item) {
3154
+ return { success: false, data: `todo #${args.id} \u4E0D\u5B58\u5728`, error: "TODO_NOT_FOUND" };
3155
+ }
3156
+ if (item.status !== "running") {
3157
+ return {
3158
+ success: false,
3159
+ data: `todo #${args.id} \u5F53\u524D\u72B6\u6001\u662F ${item.status}\uFF0C\u53EA\u6709 running \u53EF\u4EE5 mark_failed`,
3160
+ error: "TODO_INVALID_STATE"
3161
+ };
3162
+ }
3163
+ const ok = todoList.markFailed(args.id, args.reason);
3164
+ if (!ok) {
3165
+ return { success: false, data: `todo #${args.id} \u72B6\u6001\u8F6C\u6362\u5931\u8D25`, error: "TODO_INVALID" };
3166
+ }
3167
+ return {
3168
+ success: true,
3169
+ data: `\u5DF2\u6807\u8BB0 todo #${args.id} \u5931\u8D25\uFF1A${args.reason}`,
3170
+ summary: `\u274C \u5931\u8D25 #${args.id}`
3171
+ };
3172
+ }
3173
+ };
3174
+ }
3175
+ function makeTodoRetryTool(todoList) {
3176
+ return {
3177
+ name: "todo_retry",
3178
+ kind: "other" /* Other */,
3179
+ 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",
3180
+ parameters: {
3181
+ type: "object",
3182
+ properties: {
3183
+ id: { type: "number", description: "todo \u7684 id" }
3184
+ },
3185
+ required: ["id"],
3186
+ additionalProperties: false
3187
+ },
3188
+ async execute(args) {
3189
+ if (typeof args?.id !== "number") {
3190
+ return { success: false, data: "\u7F3A\u5C11 id \u53C2\u6570", error: "INVALID_ARGS" };
3191
+ }
3192
+ const item = todoList.items.find((it) => it.id === args.id);
3193
+ if (!item) {
3194
+ return { success: false, data: `todo #${args.id} \u4E0D\u5B58\u5728`, error: "TODO_NOT_FOUND" };
3195
+ }
3196
+ if (item.status !== "failed") {
3197
+ return {
3198
+ success: false,
3199
+ data: `todo #${args.id} \u5F53\u524D\u72B6\u6001\u662F ${item.status}\uFF0C\u53EA\u6709 failed \u53EF\u4EE5\u91CD\u8BD5`,
3200
+ error: "TODO_INVALID_STATE"
3201
+ };
3202
+ }
3203
+ const ok = todoList.resetForRetry(args.id);
3204
+ if (!ok) {
3205
+ return { success: false, data: `todo #${args.id} \u72B6\u6001\u8F6C\u6362\u5931\u8D25`, error: "TODO_INVALID" };
3206
+ }
3207
+ return {
3208
+ success: true,
3209
+ data: `\u5DF2\u91CD\u7F6E todo #${args.id} \u4E3A pending\uFF0C\u53EF\u91CD\u65B0\u8DD1`,
3210
+ summary: `\u{1F504} \u91CD\u8BD5 #${args.id}`
3211
+ };
3212
+ }
3213
+ };
3214
+ }
3215
+
3216
+ // src/checkpoint/git-checkpoint.ts
3217
+ import { execFile } from "child_process";
3218
+ import { promisify } from "util";
3219
+ var execFileAsync = promisify(execFile);
3220
+ var EXEC_OPTIONS = { windowsHide: true };
3221
+ async function isGitRepo(cwd) {
3222
+ try {
3223
+ await execFileAsync("git", ["rev-parse", "--is-inside-work-tree"], { cwd, ...EXEC_OPTIONS });
3224
+ return true;
3225
+ } catch {
3226
+ return false;
3227
+ }
3228
+ }
3229
+ async function hasCommits(cwd) {
3230
+ try {
3231
+ await execFileAsync("git", ["rev-parse", "--verify", "HEAD"], { cwd, ...EXEC_OPTIONS });
3232
+ return true;
3233
+ } catch {
3234
+ return false;
3235
+ }
3236
+ }
3237
+ async function hasWorkingChanges(cwd) {
3238
+ const out = await execFileAsync("git", ["status", "--porcelain"], { cwd, ...EXEC_OPTIONS });
3239
+ return out.stdout.trim().length > 0;
3240
+ }
3241
+ async function git(args, cwd) {
3242
+ try {
3243
+ const { stdout } = await execFileAsync("git", args, { cwd, ...EXEC_OPTIONS });
3244
+ return stdout;
3245
+ } catch (err) {
3246
+ const msg = err instanceof Error ? err.message : String(err);
3247
+ throw new Error(`git ${args.join(" ")} \u5931\u8D25: ${msg}`);
3248
+ }
3249
+ }
3250
+ async function listStashShas(cwd) {
3251
+ const out = await git(["stash", "list", "--format=%H"], cwd);
3252
+ return out.split("\n").map((l) => l.trim()).filter(Boolean);
3253
+ }
3254
+ async function createCheckpoint(cwd) {
3255
+ const timestamp = Date.now();
3256
+ const inRepo = await isGitRepo(cwd);
3257
+ if (!inRepo) return { stashSha: "", timestamp, cwd, isGitRepo: false };
3258
+ if (!await hasCommits(cwd)) return { stashSha: "", timestamp, cwd, isGitRepo: true };
3259
+ if (!await hasWorkingChanges(cwd)) return { stashSha: "", timestamp, cwd, isGitRepo: true };
3260
+ const beforeShas = await listStashShas(cwd);
3261
+ await git(["stash", "push", "-m", `dskcode-cp-${timestamp}`, "-u"], cwd);
3262
+ const newShas = await listStashShas(cwd);
3263
+ if (newShas.length === 0) throw new Error("git stash push \u672A\u80FD\u521B\u5EFA stash entry");
3264
+ const newSha = newShas.find((s) => !beforeShas.includes(s)) ?? newShas[0];
3265
+ await git(["stash", "apply", newSha], cwd);
3266
+ return { stashSha: newSha, timestamp, cwd, isGitRepo: true };
3267
+ }
3268
+ async function restoreCheckpointForce(checkpoint) {
3269
+ if (!checkpoint.isGitRepo) throw new Error("\u975E git \u4ED3\u5E93\uFF0C\u65E0\u6CD5\u6062\u590D\u6587\u4EF6\u72B6\u6001");
3270
+ 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");
3271
+ const { cwd, stashSha } = checkpoint;
3272
+ const currentShas = await listStashShas(cwd);
3273
+ if (!currentShas.includes(stashSha)) {
3274
+ throw new Error("\u68C0\u67E5\u70B9\u5DF2\u5931\u6548\uFF08stash entry \u5DF2\u88AB\u6D88\u8D39\u6216 GC\uFF09\uFF0C\u65E0\u6CD5\u6062\u590D");
3275
+ }
3276
+ await git(["checkout", "--", "."], cwd);
3277
+ await git(["clean", "-fd"], cwd);
2646
3278
  await git(["stash", "apply", stashSha], cwd);
2647
3279
  const refIndex = currentShas.indexOf(stashSha);
2648
3280
  if (refIndex >= 0) await git(["stash", "drop", `stash@{${refIndex}}`], cwd);
@@ -2849,6 +3481,10 @@ var ConversationLogger = class _ConversationLogger {
2849
3481
  logAssistantText(content, round) {
2850
3482
  this.log({ ts: Date.now(), type: "assistant_text", content, round });
2851
3483
  }
3484
+ /** 记录思考链(thinking 模式下模型的 CoT) */
3485
+ logReasoning(content, round) {
3486
+ this.log({ ts: Date.now(), type: "reasoning", content, round });
3487
+ }
2852
3488
  /** 记录工具调用 */
2853
3489
  logToolCall(name, callId, args, round) {
2854
3490
  this.log({ ts: Date.now(), type: "tool_call", name, callId, arguments: args, round });
@@ -2999,6 +3635,10 @@ var Session = class _Session {
2999
3635
  #reflector;
3000
3636
  /** 本轮工具执行结果(仅在 chat() 循环内使用;用于下一轮注入 reflection) */
3001
3637
  #lastRoundResults = null;
3638
+ /** Harness 任务列表(仅在 enableHarness 时非 null) */
3639
+ #todoList;
3640
+ /** 本会话内是否已发出过「未拆 todo」护栏提示(避免每轮骚扰) */
3641
+ #harnessHintEmitted = false;
3002
3642
  /**
3003
3643
  * 构造一个 Session。
3004
3644
  *
@@ -3028,7 +3668,8 @@ var Session = class _Session {
3028
3668
  writeRoots: options?.writeRoots ?? [options?.cwd ?? process.cwd()],
3029
3669
  enableCheckpoint: options?.enableCheckpoint ?? true,
3030
3670
  enableLog: options?.enableLog ?? true,
3031
- enableReflection: options?.enableReflection !== false
3671
+ enableReflection: options?.enableReflection !== false,
3672
+ enableHarness: options?.enableHarness !== false
3032
3673
  };
3033
3674
  this.#sessionId = options?.sessionId ?? SessionStore.newId();
3034
3675
  this.#store = options?.store === false ? null : options?.store ?? new SessionStore();
@@ -3036,9 +3677,23 @@ var Session = class _Session {
3036
3677
  this.#logger = new ConversationLogger(this.#sessionId, this.#options.cwd, {
3037
3678
  enabled: this.#options.enableLog
3038
3679
  });
3039
- this.#logger.logSessionStart(this.#sessionId, this.#options.cwd, this.#provider.model(), this.#mode);
3680
+ this.#logger.logSessionStart(
3681
+ this.#sessionId,
3682
+ this.#options.cwd,
3683
+ this.#provider.model(),
3684
+ this.#mode
3685
+ );
3040
3686
  this.#stormDetector = new StormDetector({ threshold: 3 });
3041
3687
  this.#reflector = this.#options.enableReflection ? new Reflector() : null;
3688
+ if (this.#options.enableHarness) {
3689
+ const todoList = new TodoList();
3690
+ this.#todoList = todoList;
3691
+ for (const t of createHarnessTools(todoList)) {
3692
+ this.#toolRegistry.registerErased(eraseTool(t));
3693
+ }
3694
+ } else {
3695
+ this.#todoList = null;
3696
+ }
3042
3697
  }
3043
3698
  /** 当前会话的完整消息历史(只读视图) */
3044
3699
  get messages() {
@@ -3118,6 +3773,7 @@ var Session = class _Session {
3118
3773
  const startTime = Date.now();
3119
3774
  let toolRounds = 0;
3120
3775
  const toolExecutor = this.#buildToolExecutor();
3776
+ this.#harnessHintEmitted = false;
3121
3777
  try {
3122
3778
  while (toolRounds < this.#options.maxToolRounds) {
3123
3779
  let systemPrompt = this.#buildSystemPrompt();
@@ -3129,20 +3785,21 @@ var Session = class _Session {
3129
3785
  if (reflections.length > 0) {
3130
3786
  systemPrompt = this.#reflector.injectIntoPrompt(systemPrompt, reflections);
3131
3787
  this.#logger.logReflections(
3132
- reflections.map((r) => ({ category: r.category, toolName: r.toolName, hint: r.hint }))
3788
+ reflections.map((r) => ({
3789
+ category: r.category,
3790
+ toolName: r.toolName,
3791
+ hint: r.hint
3792
+ }))
3133
3793
  );
3134
3794
  }
3135
3795
  this.#lastRoundResults = null;
3136
3796
  }
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
- );
3797
+ const [trimmed] = trimMessages([...this.#messages], {
3798
+ model: this.#provider.model(),
3799
+ reservedForOutput: this.#options.reservedForOutput,
3800
+ systemPrompt,
3801
+ preserveRecentRounds: this.#options.preserveRecentRounds
3802
+ });
3146
3803
  const apiMessages = buildApiMessages(systemPrompt, trimmed);
3147
3804
  const toolDefs = buildToolDefinitions(this.#toolRegistry, this.#mode);
3148
3805
  const stream = this.#provider.chat(apiMessages, {
@@ -3155,6 +3812,7 @@ var Session = class _Session {
3155
3812
  });
3156
3813
  const modelId = this.#provider.model();
3157
3814
  let accumulatedText = "";
3815
+ let accumulatedReasoning = "";
3158
3816
  let lastUsage;
3159
3817
  let lastToolCalls;
3160
3818
  let _lastFinishReason = null;
@@ -3165,6 +3823,10 @@ var Session = class _Session {
3165
3823
  let lastEmitMs = 0;
3166
3824
  const EST_EMIT_INTERVAL_MS = 300;
3167
3825
  for await (const chunk of stream) {
3826
+ if (chunk.reasoningContent) {
3827
+ accumulatedReasoning += chunk.reasoningContent;
3828
+ yield { type: "reasoning_delta", content: chunk.reasoningContent };
3829
+ }
3168
3830
  if (chunk.content) {
3169
3831
  accumulatedText += chunk.content;
3170
3832
  yield { type: "text_delta", content: chunk.content };
@@ -3186,7 +3848,8 @@ var Session = class _Session {
3186
3848
  };
3187
3849
  }
3188
3850
  }
3189
- if (chunk.toolCalls && chunk.toolCalls.length > 0) lastToolCalls = chunk.toolCalls;
3851
+ if (chunk.toolCalls && chunk.toolCalls.length > 0)
3852
+ lastToolCalls = chunk.toolCalls;
3190
3853
  if (chunk.usage) {
3191
3854
  lastUsage = chunk.usage;
3192
3855
  const realCost = calculateCost(chunk.usage, modelId);
@@ -3229,15 +3892,22 @@ var Session = class _Session {
3229
3892
  yield { type: "usage", usage: lastUsage, model: modelId };
3230
3893
  }
3231
3894
  const assistantMsg = { role: "assistant", content: accumulatedText };
3232
- if (lastToolCalls && lastToolCalls.length > 0) assistantMsg.toolCalls = lastToolCalls;
3895
+ if (lastToolCalls && lastToolCalls.length > 0)
3896
+ assistantMsg.toolCalls = lastToolCalls;
3233
3897
  this.#messages.push(assistantMsg);
3234
3898
  this.#logger.logAssistantText(accumulatedText, toolRounds);
3899
+ if (accumulatedReasoning) {
3900
+ this.#logger.logReasoning(accumulatedReasoning, toolRounds);
3901
+ }
3235
3902
  if (lastToolCalls && lastToolCalls.length > 0) {
3236
3903
  yield { type: "tool_calls", calls: lastToolCalls };
3237
3904
  for (const tc of lastToolCalls) {
3238
3905
  this.#logger.logToolCall(tc.name, tc.id, tc.arguments, toolRounds);
3239
3906
  }
3240
- const stormBroken = this.#stormDetector.shouldBreak(this.#stormRecords, lastToolCalls);
3907
+ const stormBroken = this.#stormDetector.shouldBreak(
3908
+ this.#stormRecords,
3909
+ lastToolCalls
3910
+ );
3241
3911
  if (stormBroken) {
3242
3912
  const stormMsg = "\n\u26A0\uFE0F \u540C\u4E00\u5DE5\u5177\u91CD\u590D\u51FA\u9519\uFF0C\u5DF2\u5F3A\u5236\u5207\u6362\u7B56\u7565\n";
3243
3913
  yield { type: "text_delta", content: stormMsg };
@@ -3249,7 +3919,6 @@ var Session = class _Session {
3249
3919
  }
3250
3920
  const results = await toolExecutor.executeBatch(lastToolCalls);
3251
3921
  this.#stormRecords = results.records;
3252
- console.log("results", results);
3253
3922
  this.#lastRoundResults = results.items.map((it) => {
3254
3923
  const tool = this.#toolRegistry.get(it.name);
3255
3924
  return {
@@ -3260,7 +3929,13 @@ var Session = class _Session {
3260
3929
  };
3261
3930
  });
3262
3931
  for (const item of results.items) {
3263
- yield { type: "tool_result", name: item.name, result: item.result };
3932
+ const todoSnapshot = item.name.startsWith("todo_") && this.#todoList ? this.#todoList.items : void 0;
3933
+ yield {
3934
+ type: "tool_result",
3935
+ name: item.name,
3936
+ result: item.result,
3937
+ todoSnapshot
3938
+ };
3264
3939
  this.#logger.logToolResult(
3265
3940
  item.name,
3266
3941
  item.callId,
@@ -3271,7 +3946,8 @@ var Session = class _Session {
3271
3946
  toolRounds
3272
3947
  );
3273
3948
  let toolContent = item.result.data;
3274
- if (item.result.diff && item.result.diff.patch) toolContent += `
3949
+ if (item.result.diff && item.result.diff.patch)
3950
+ toolContent += `
3275
3951
 
3276
3952
  ${item.result.diff.patch}`;
3277
3953
  this.#messages.push({
@@ -3281,9 +3957,16 @@ ${item.result.diff.patch}`;
3281
3957
  name: item.name
3282
3958
  });
3283
3959
  }
3960
+ this.#maybeEmitHarnessHint(results.items);
3284
3961
  toolRounds++;
3285
3962
  continue;
3286
3963
  }
3964
+ if (this.#todoList && this.#todoList.items.length > 0 && this.#todoList.isAllTerminated()) {
3965
+ this.#messages.push({
3966
+ role: "user",
3967
+ 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"
3968
+ });
3969
+ }
3287
3970
  break;
3288
3971
  }
3289
3972
  } catch (err) {
@@ -3439,7 +4122,11 @@ ${item.result.diff.patch}`;
3439
4122
  if (!store) throw new Error("resume \u9700\u8981\u542F\u7528\u6301\u4E45\u5316\uFF08options.store \u4E0D\u80FD\u4E3A false\uFF09");
3440
4123
  const stored = await store.load(id);
3441
4124
  if (!stored) throw new Error(`\u4F1A\u8BDD ${id} \u4E0D\u5B58\u5728`);
3442
- const session = new _Session(provider, tools, costTracker, { ...options, sessionId: id, store });
4125
+ const session = new _Session(provider, tools, costTracker, {
4126
+ ...options,
4127
+ sessionId: id,
4128
+ store
4129
+ });
3443
4130
  for (const m of stored.messages) {
3444
4131
  session.#messages.push({
3445
4132
  role: m.role,
@@ -3471,7 +4158,12 @@ ${item.result.diff.patch}`;
3471
4158
  for (const [index, checkpoint] of this.#checkpoints) {
3472
4159
  const msg = this.#messages[index];
3473
4160
  if (!msg || msg.role !== "user") continue;
3474
- result.push({ index, preview: msg.content.slice(0, 80), timestamp: checkpoint.timestamp, isGitRepo: checkpoint.isGitRepo });
4161
+ result.push({
4162
+ index,
4163
+ preview: msg.content.slice(0, 80),
4164
+ timestamp: checkpoint.timestamp,
4165
+ isGitRepo: checkpoint.isGitRepo
4166
+ });
3475
4167
  }
3476
4168
  return result.sort((a, b) => a.index - b.index);
3477
4169
  }
@@ -3599,8 +4291,38 @@ ${item.result.diff.patch}`;
3599
4291
  projectContext: this.#options.projectContext ?? void 0,
3600
4292
  cwd: this.#options.cwd
3601
4293
  };
3602
- if (this.#mode === "plan") return buildPlanSystemPrompt(opts);
3603
- return buildSystemPrompt(opts);
4294
+ let base;
4295
+ if (this.#mode === "plan") base = buildPlanSystemPrompt(opts);
4296
+ else base = buildSystemPrompt(opts);
4297
+ if (this.#todoList && this.#todoList.items.length > 0) {
4298
+ base = base + "\n\n" + this.#todoList.toMarkdown();
4299
+ }
4300
+ return base;
4301
+ }
4302
+ /**
4303
+ * Harness 护栏:模型跳过 todo_add 直接动手时,主动发一条提示让其重评。
4304
+ *
4305
+ * 触发条件(全部需满足):
4306
+ * 1. Harness 开启(#todoList 非空)
4307
+ * 2. todoList 仍为空(未拆 todo)
4308
+ * 3. 本会话内未提示过(#harnessHintEmitted 为 false)
4309
+ * 4. 本轮调了 ≥1 个**非 todo_* 的工具**(说明模型跳过了 todo_add 直接干活)
4310
+ *
4311
+ * 动作:push 一条 user role 消息到 #messages,让下一轮 LLM 看到;设 #harnessHintEmitted = true。
4312
+ *
4313
+ * @sideEffect 写 #messages / #harnessHintEmitted
4314
+ */
4315
+ #maybeEmitHarnessHint(items) {
4316
+ if (!this.#todoList) return;
4317
+ if (this.#todoList.items.length > 0) return;
4318
+ if (this.#harnessHintEmitted) return;
4319
+ const hasNonTodo = items.some((it) => !it.name.startsWith("todo_"));
4320
+ if (!hasNonTodo) return;
4321
+ this.#harnessHintEmitted = true;
4322
+ this.#messages.push({
4323
+ role: "user",
4324
+ 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"
4325
+ });
3604
4326
  }
3605
4327
  };
3606
4328
 
@@ -5104,7 +5826,7 @@ function getGradientColors(text, phase, stops) {
5104
5826
  }
5105
5827
 
5106
5828
  // src/ui/ChatSession.tsx
5107
- import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
5829
+ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
5108
5830
  var PHASE_CONFIG = {
5109
5831
  thinking: { icon: "\u{1F9E0}", label: "\u6DF1\u5EA6\u601D\u8003\u4E2D", color: "#ff9800" },
5110
5832
  generating: { icon: "\u2728", label: "\u751F\u6210\u4E2D", color: "#00ff41" },
@@ -5135,15 +5857,45 @@ registerCommand("/help", {
5135
5857
  }
5136
5858
  });
5137
5859
  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" }) });
5860
+ registerCommand("/version", {
5861
+ desc: "\u663E\u793A\u7248\u672C\u4FE1\u606F",
5862
+ handler: () => ({ kind: "text", content: "dskcode v0.1.10" })
5863
+ });
5864
+ registerCommand("/model", {
5865
+ desc: "\u5207\u6362\u6A21\u578B",
5866
+ handler: () => ({ kind: "text", content: "\u8BF7\u76F4\u63A5\u8F93\u5165 /model \u8FDB\u5165\u9009\u62E9\u754C\u9762" })
5867
+ });
5868
+ registerCommand("/thinking", {
5869
+ desc: "\u5207\u6362\u6DF1\u5EA6\u601D\u8003\u6A21\u5F0F",
5870
+ handler: () => ({ kind: "text", content: "\u8BF7\u76F4\u63A5\u8F93\u5165 /thinking \u5207\u6362" })
5871
+ });
5872
+ registerCommand("/effort", {
5873
+ desc: "\u5207\u6362\u63A8\u7406\u7B49\u7EA7 High/Max",
5874
+ handler: () => ({ kind: "text", content: "\u8BF7\u76F4\u63A5\u8F93\u5165 /effort \u5207\u6362" })
5875
+ });
5876
+ registerCommand("/game", {
5877
+ desc: "\u542F\u52A8\u6E38\u620F",
5878
+ handler: () => ({ kind: "navigate", target: "game" })
5879
+ });
5880
+ registerCommand("/stock", {
5881
+ desc: "\u67E5\u770B\u80A1\u7968\u884C\u60C5",
5882
+ handler: () => ({ kind: "navigate", target: "stock" })
5883
+ });
5884
+ registerCommand("/plan", {
5885
+ desc: "\u5207\u6362\u4E3A\u8BA1\u5212\u6A21\u5F0F\uFF08Shift+Tab\uFF09",
5886
+ handler: () => ({ kind: "text", content: "\u8F93\u5165 /plan \u6216\u6309 Shift+Tab \u5207\u6362\u4E3A\u8BA1\u5212\u6A21\u5F0F" })
5887
+ });
5888
+ registerCommand("/code", {
5889
+ desc: "\u5207\u6362\u56DE\u4EE3\u7801\u6A21\u5F0F\uFF08Shift+Tab\uFF09",
5890
+ handler: () => ({ kind: "text", content: "\u8F93\u5165 /code \u6216\u6309 Shift+Tab \u5207\u6362\u56DE\u4EE3\u7801\u6A21\u5F0F" })
5891
+ });
5892
+ registerCommand("/rewind", {
5893
+ desc: "\u56DE\u9000\u5230\u5386\u53F2\u68C0\u67E5\u70B9\uFF081 = \u6700\u65B0\uFF09",
5894
+ handler: () => ({
5895
+ kind: "text",
5896
+ 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"
5897
+ })
5898
+ });
5147
5899
  var STREAMING_PLACEHOLDERS = [
5148
5900
  "\u8BA9\u5B50\u5F39\u98DE\u4E00\u4F1A\u513F...",
5149
5901
  "\u9A6C\u4E0A\u5C31\u597D...",
@@ -5188,12 +5940,15 @@ function ChatSession({
5188
5940
  const [isStreaming, setIsStreaming] = useState4(false);
5189
5941
  const [streamingPhase, setStreamingPhase] = useState4(null);
5190
5942
  const [streamingPlaceholder, setStreamingPlaceholder] = useState4("");
5191
- const [idlePlaceholder, setIdlePlaceholder] = useState4(() => pickRandom(IDLE_PLACEHOLDERS));
5943
+ const [idlePlaceholder, setIdlePlaceholder] = useState4(
5944
+ () => pickRandom(IDLE_PLACEHOLDERS)
5945
+ );
5192
5946
  const [gradientColors, setGradientColors] = useState4([]);
5193
5947
  const gradientPhaseRef = useRef3(0);
5194
5948
  const [streamingGradientColors, setStreamingGradientColors] = useState4([]);
5195
5949
  const streamingPhaseRef = useRef3(0);
5196
5950
  const [currentContent, setCurrentContent] = useState4("");
5951
+ const [currentReasoning, setCurrentReasoning] = useState4([]);
5197
5952
  const [currentToolCalls, setCurrentToolCalls] = useState4([]);
5198
5953
  const [_currentUsage, setCurrentUsage] = useState4(void 0);
5199
5954
  const [_currentElapsed, setCurrentElapsed] = useState4(void 0);
@@ -5201,11 +5956,14 @@ function ChatSession({
5201
5956
  const [activeModel, setActiveModel] = useState4(model);
5202
5957
  const [_streamingModel, setStreamingModel] = useState4(void 0);
5203
5958
  const [streamError, setStreamError] = useState4(void 0);
5959
+ const [todoSnapshot, setTodoSnapshot] = useState4([]);
5204
5960
  const [sessionMode, setSessionMode] = useState4("code");
5205
5961
  const [thinkingEnabled, setThinkingEnabled] = useState4(true);
5206
5962
  const [thinkingEffort, setThinkingEffort] = useState4("high");
5207
5963
  const [responseFormat] = useState4("text");
5208
- const [toolChoice, setToolChoice] = useState4(void 0);
5964
+ const [toolChoice, setToolChoice] = useState4(
5965
+ void 0
5966
+ );
5209
5967
  const sessionCost = useMemo(() => {
5210
5968
  return displayMessages.reduce((sum, msg) => {
5211
5969
  if (msg.assistantDetail?.cost) {
@@ -5226,7 +5984,9 @@ function ChatSession({
5226
5984
  const [rewindSelectIndex, setRewindSelectIndex] = useState4(0);
5227
5985
  const [rewindList, setRewindList] = useState4([]);
5228
5986
  const [rewinding, setRewinding] = useState4(false);
5229
- const [rewindHintPhase, setRewindHintPhase] = useState4("idle");
5987
+ const [rewindHintPhase, setRewindHintPhase] = useState4(
5988
+ "idle"
5989
+ );
5230
5990
  const currentRoundModifiedRef = useRef3(false);
5231
5991
  const rewindHintTimerRef = useRef3(null);
5232
5992
  const [selectingModel, setSelectingModel] = useState4(false);
@@ -5235,6 +5995,7 @@ function ChatSession({
5235
5995
  const sessionRef = useRef3(null);
5236
5996
  const abortRef = useRef3(null);
5237
5997
  const currentContentRef = useRef3("");
5998
+ const currentReasoningRef = useRef3([]);
5238
5999
  const currentToolCallsRef = useRef3([]);
5239
6000
  const currentUsageRef = useRef3(void 0);
5240
6001
  const currentElapsedRef = useRef3(void 0);
@@ -5304,42 +6065,48 @@ function ChatSession({
5304
6065
  setDisplayMessages(next);
5305
6066
  setStaticKey((prev) => prev + 1);
5306
6067
  }
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 {
6068
+ const doRewind = useCallback2(
6069
+ async (target, displayNumber) => {
6070
+ const session = sessionRef.current;
6071
+ if (!session) return;
6072
+ setRewinding(true);
6073
+ setIsStreaming(true);
6074
+ setStreamingPhase("thinking");
6075
+ setStreamingPlaceholder("\u23EA \u6B63\u5728\u56DE\u9000\u5230\u68C0\u67E5\u70B9\u2026");
6076
+ setInput("");
6077
+ try {
6078
+ const r = await session.rewind(target.index);
6079
+ if (r.ok) {
6080
+ rebuildDisplayFromSession(session);
6081
+ 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";
6082
+ setDisplayMessages((prev) => [
6083
+ ...prev,
6084
+ {
6085
+ role: "assistant",
6086
+ content: `\u23EA \u5DF2\u56DE\u9000\u5230\u68C0\u67E5\u70B9 #${displayNumber}${tail}\u3002`
6087
+ }
6088
+ ]);
6089
+ } else {
6090
+ setDisplayMessages((prev) => [
6091
+ ...prev,
6092
+ { role: "assistant", content: `\u26A0 \u56DE\u9000\u5931\u8D25\uFF1A${r.error}` }
6093
+ ]);
6094
+ }
6095
+ } catch (err) {
6096
+ const msg = err instanceof Error ? err.message : String(err);
5325
6097
  setDisplayMessages((prev) => [
5326
6098
  ...prev,
5327
- { role: "assistant", content: `\u26A0 \u56DE\u9000\u5931\u8D25\uFF1A${r.error}` }
6099
+ { role: "assistant", content: `\u26A0 \u56DE\u9000\u5F02\u5E38\uFF1A${msg}` }
5328
6100
  ]);
6101
+ } finally {
6102
+ setRewinding(false);
6103
+ setIsStreaming(false);
6104
+ setStreamingPhase(null);
6105
+ setStreamingPlaceholder("");
5329
6106
  }
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
- }, []);
6107
+ },
6108
+ []
6109
+ );
5343
6110
  const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(() => {
5344
6111
  const s = sessionRef.current;
5345
6112
  if (s) {
@@ -5353,7 +6120,9 @@ function ChatSession({
5353
6120
  (_input, key) => {
5354
6121
  if (rewindSelecting) {
5355
6122
  if (key.upArrow) {
5356
- setRewindSelectIndex((prev) => (prev - 1 + rewindList.length) % rewindList.length);
6123
+ setRewindSelectIndex(
6124
+ (prev) => (prev - 1 + rewindList.length) % rewindList.length
6125
+ );
5357
6126
  } else if (key.downArrow) {
5358
6127
  setRewindSelectIndex((prev) => (prev + 1) % rewindList.length);
5359
6128
  } else if (key.return) {
@@ -5373,7 +6142,9 @@ function ChatSession({
5373
6142
  }
5374
6143
  if (selectingModel) {
5375
6144
  if (key.upArrow) {
5376
- setModelSelectIndex((prev) => (prev - 1 + modelOptions.length) % modelOptions.length);
6145
+ setModelSelectIndex(
6146
+ (prev) => (prev - 1 + modelOptions.length) % modelOptions.length
6147
+ );
5377
6148
  } else if (key.downArrow) {
5378
6149
  setModelSelectIndex((prev) => (prev + 1) % modelOptions.length);
5379
6150
  } else if (key.return) {
@@ -5381,7 +6152,10 @@ function ChatSession({
5381
6152
  if (selected === activeModel) {
5382
6153
  setDisplayMessages((prev) => [
5383
6154
  ...prev,
5384
- { role: "assistant", content: `\u5DF2\u7ECF\u5728\u4F7F\u7528 ${SUPPORTED_MODELS[selected].displayName}` }
6155
+ {
6156
+ role: "assistant",
6157
+ content: `\u5DF2\u7ECF\u5728\u4F7F\u7528 ${SUPPORTED_MODELS[selected].displayName}`
6158
+ }
5385
6159
  ]);
5386
6160
  } else {
5387
6161
  setActiveModel(selected);
@@ -5390,7 +6164,10 @@ function ChatSession({
5390
6164
  });
5391
6165
  setDisplayMessages((prev) => [
5392
6166
  ...prev,
5393
- { role: "assistant", content: `\u6A21\u578B\u5DF2\u5207\u6362\u4E3A ${SUPPORTED_MODELS[selected].displayName}\uFF08${selected}\uFF09` }
6167
+ {
6168
+ role: "assistant",
6169
+ content: `\u6A21\u578B\u5DF2\u5207\u6362\u4E3A ${SUPPORTED_MODELS[selected].displayName}\uFF08${selected}\uFF09`
6170
+ }
5394
6171
  ]);
5395
6172
  }
5396
6173
  setSelectingModel(false);
@@ -5424,7 +6201,9 @@ function ChatSession({
5424
6201
  }
5425
6202
  if (skillList.length > 0) {
5426
6203
  if (key.upArrow) {
5427
- setSkillSelectIndex((prev) => (prev - 1 + skillList.length) % skillList.length);
6204
+ setSkillSelectIndex(
6205
+ (prev) => (prev - 1 + skillList.length) % skillList.length
6206
+ );
5428
6207
  return;
5429
6208
  }
5430
6209
  if (key.downArrow) {
@@ -5477,7 +6256,26 @@ function ChatSession({
5477
6256
  setInput(_input);
5478
6257
  }
5479
6258
  },
5480
- [selectingModel, modelSelectIndex, modelOptions, activeModel, isStreaming, handleCtrlC, input, skills, skillSelectIndex, fileSelectIndex, getFilteredSkills, getFilteredFiles, sessionMode, setSessionMode, rewindSelecting, rewindSelectIndex, rewindList, doRewind]
6259
+ [
6260
+ selectingModel,
6261
+ modelSelectIndex,
6262
+ modelOptions,
6263
+ activeModel,
6264
+ isStreaming,
6265
+ handleCtrlC,
6266
+ input,
6267
+ skills,
6268
+ skillSelectIndex,
6269
+ fileSelectIndex,
6270
+ getFilteredSkills,
6271
+ getFilteredFiles,
6272
+ sessionMode,
6273
+ setSessionMode,
6274
+ rewindSelecting,
6275
+ rewindSelectIndex,
6276
+ rewindList,
6277
+ doRewind
6278
+ ]
5481
6279
  )
5482
6280
  );
5483
6281
  useEffect4(() => {
@@ -5498,7 +6296,9 @@ function ChatSession({
5498
6296
  setCmdTipGradientColors(getGradientColors(text, 1, CMD_TIP_GRADIENT_STOPS));
5499
6297
  const interval = setInterval(() => {
5500
6298
  cmdTipPhaseRef.current = (cmdTipPhaseRef.current + GRADIENT_ANIMATION.cmdTipPhaseStep) % 1;
5501
- setCmdTipGradientColors(getGradientColors(text, 1 - cmdTipPhaseRef.current, CMD_TIP_GRADIENT_STOPS));
6299
+ setCmdTipGradientColors(
6300
+ getGradientColors(text, 1 - cmdTipPhaseRef.current, CMD_TIP_GRADIENT_STOPS)
6301
+ );
5502
6302
  }, GRADIENT_ANIMATION.cmdTipInterval);
5503
6303
  return () => clearInterval(interval);
5504
6304
  }, [cmdTipIndex, cmdTips.length]);
@@ -5531,7 +6331,7 @@ function ChatSession({
5531
6331
  if (!apiKey || !baseUrl) return;
5532
6332
  let cancelled = false;
5533
6333
  setBalanceLoading(true);
5534
- import("./deepseek-PGX76BK5.js").then(({ DeepSeekProvider: DeepSeekProvider2 }) => {
6334
+ import("./deepseek-WFSVA5UQ.js").then(({ DeepSeekProvider: DeepSeekProvider2 }) => {
5535
6335
  const provider = new DeepSeekProvider2({
5536
6336
  apiKey,
5537
6337
  baseUrl,
@@ -5579,7 +6379,13 @@ function ChatSession({
5579
6379
  setGradientColors(getGradientColors(idlePlaceholder, 1, IDLE_GRADIENT_STOPS));
5580
6380
  const interval = setInterval(() => {
5581
6381
  gradientPhaseRef.current = (gradientPhaseRef.current + GRADIENT_ANIMATION.idlePhaseStep) % 1;
5582
- setGradientColors(getGradientColors(idlePlaceholder, 1 - gradientPhaseRef.current, IDLE_GRADIENT_STOPS));
6382
+ setGradientColors(
6383
+ getGradientColors(
6384
+ idlePlaceholder,
6385
+ 1 - gradientPhaseRef.current,
6386
+ IDLE_GRADIENT_STOPS
6387
+ )
6388
+ );
5583
6389
  }, GRADIENT_ANIMATION.idleInterval);
5584
6390
  return () => clearInterval(interval);
5585
6391
  }, [isStreaming, idlePlaceholder]);
@@ -5589,385 +6395,465 @@ function ChatSession({
5589
6395
  return;
5590
6396
  }
5591
6397
  streamingPhaseRef.current = 0;
5592
- setStreamingGradientColors(getGradientColors(streamingPlaceholder, 1, STREAMING_GRADIENT_STOPS));
6398
+ setStreamingGradientColors(
6399
+ getGradientColors(streamingPlaceholder, 1, STREAMING_GRADIENT_STOPS)
6400
+ );
5593
6401
  const interval = setInterval(() => {
5594
6402
  streamingPhaseRef.current = (streamingPhaseRef.current + GRADIENT_ANIMATION.streamingPhaseStep) % 1;
5595
- setStreamingGradientColors(getGradientColors(streamingPlaceholder, 1 - streamingPhaseRef.current, STREAMING_GRADIENT_STOPS));
6403
+ setStreamingGradientColors(
6404
+ getGradientColors(
6405
+ streamingPlaceholder,
6406
+ 1 - streamingPhaseRef.current,
6407
+ STREAMING_GRADIENT_STOPS
6408
+ )
6409
+ );
5596
6410
  }, GRADIENT_ANIMATION.streamingInterval);
5597
6411
  return () => clearInterval(interval);
5598
6412
  }, [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";
6413
+ const handleSubmit = useCallback2(
6414
+ async (value) => {
6415
+ const trimmed = value.trim();
6416
+ if (!trimmed) return;
6417
+ if (trimmed.startsWith("/") && trimmed.length > 1) {
6418
+ const cmdLower = trimmed.toLowerCase();
6419
+ if (cmdLower === "/rewind" || cmdLower.startsWith("/rewind ")) {
6420
+ if (isStreaming || rewinding) {
6421
+ const reason = rewinding ? "\u56DE\u9000\u4E2D" : "\u751F\u6210\u4E2D";
6422
+ setDisplayMessages((prev) => [
6423
+ ...prev,
6424
+ { role: "user", content: trimmed },
6425
+ { role: "assistant", content: `\u26A0 \u6B63\u5728${reason}\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5 /rewind\u3002` }
6426
+ ]);
6427
+ setInput("");
6428
+ return;
6429
+ }
6430
+ if (!sessionRef.current) {
6431
+ setDisplayMessages((prev) => [
6432
+ ...prev,
6433
+ { role: "user", content: trimmed },
6434
+ { role: "assistant", content: "\u26A0 Session \u672A\u5C31\u7EEA\uFF0C\u65E0\u6CD5\u56DE\u9000\u3002" }
6435
+ ]);
6436
+ setInput("");
6437
+ return;
6438
+ }
6439
+ const cps = sessionRef.current.listCheckpoints();
6440
+ if (cps.length === 0) {
6441
+ setDisplayMessages((prev) => [
6442
+ ...prev,
6443
+ { role: "user", content: trimmed },
6444
+ {
6445
+ role: "assistant",
6446
+ 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"
6447
+ }
6448
+ ]);
6449
+ setInput("");
6450
+ return;
6451
+ }
6452
+ const parts = trimmed.split(/\s+/);
6453
+ if (parts.length >= 2) {
6454
+ const n = Number(parts[1]);
6455
+ if (!Number.isInteger(n) || n < 1 || n > cps.length) {
6456
+ setDisplayMessages((prev) => [
6457
+ ...prev,
6458
+ { role: "user", content: trimmed },
6459
+ {
6460
+ role: "assistant",
6461
+ 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`
6462
+ }
6463
+ ]);
6464
+ setInput("");
6465
+ return;
6466
+ }
6467
+ const target = cps[cps.length - n];
6468
+ setInput("");
6469
+ await doRewind(target, n);
6470
+ return;
6471
+ }
6472
+ setRewindList([...cps].reverse());
6473
+ setRewindSelectIndex(0);
6474
+ setRewindSelecting(true);
5607
6475
  setDisplayMessages((prev) => [
5608
6476
  ...prev,
5609
6477
  { role: "user", content: trimmed },
5610
- { role: "assistant", content: `\u26A0 \u6B63\u5728${reason}\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5 /rewind\u3002` }
6478
+ {
6479
+ role: "assistant",
6480
+ 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`
6481
+ }
5611
6482
  ]);
5612
6483
  setInput("");
5613
6484
  return;
5614
6485
  }
5615
- if (!sessionRef.current) {
6486
+ if (cmdLower === "/model") {
6487
+ const curIdx = modelOptions.indexOf(activeModel);
6488
+ setModelSelectIndex(curIdx >= 0 ? curIdx : 0);
6489
+ setSelectingModel(true);
6490
+ setInput("");
6491
+ return;
6492
+ }
6493
+ if (cmdLower === "/thinking") {
6494
+ setThinkingEnabled((prev) => !prev);
5616
6495
  setDisplayMessages((prev) => [
5617
6496
  ...prev,
5618
6497
  { role: "user", content: trimmed },
5619
- { role: "assistant", content: "\u26A0 Session \u672A\u5C31\u7EEA\uFF0C\u65E0\u6CD5\u56DE\u9000\u3002" }
6498
+ {
6499
+ role: "assistant",
6500
+ content: `\u6DF1\u5EA6\u601D\u8003\u5DF2${thinkingEnabled ? "\u5173\u95ED" : "\u5F00\u542F"}`
6501
+ }
5620
6502
  ]);
5621
6503
  setInput("");
5622
6504
  return;
5623
6505
  }
5624
- const cps = sessionRef.current.listCheckpoints();
5625
- if (cps.length === 0) {
6506
+ if (cmdLower === "/effort") {
6507
+ const next = thinkingEffort === "high" ? "max" : "high";
6508
+ setThinkingEffort(next);
5626
6509
  setDisplayMessages((prev) => [
5627
6510
  ...prev,
5628
6511
  { 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" }
6512
+ {
6513
+ role: "assistant",
6514
+ content: `\u63A8\u7406\u7B49\u7EA7\u5DF2\u5207\u6362\u4E3A ${next === "high" ? "High" : "Max"}`
6515
+ }
5630
6516
  ]);
5631
6517
  setInput("");
5632
6518
  return;
5633
6519
  }
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) {
6520
+ if (cmdLower === "/plan") {
6521
+ if (sessionMode === "plan") {
5638
6522
  setDisplayMessages((prev) => [
5639
6523
  ...prev,
5640
6524
  { 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` }
6525
+ {
6526
+ role: "assistant",
6527
+ content: "\u5DF2\u7ECF\u5728\u8BA1\u5212\u6A21\u5F0F\u4E2D\u3002\u8F93\u5165 /code \u5207\u56DE\u4EE3\u7801\u6A21\u5F0F\u3002"
6528
+ }
5642
6529
  ]);
5643
- setInput("");
5644
- return;
6530
+ } else {
6531
+ setSessionMode("plan");
6532
+ sessionRef.current?.setMode("plan");
6533
+ setToolChoice(void 0);
6534
+ setDisplayMessages((prev) => [
6535
+ ...prev,
6536
+ { role: "user", content: trimmed },
6537
+ {
6538
+ role: "assistant",
6539
+ 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"
6540
+ }
6541
+ ]);
6542
+ sessionRef.current?.reset();
5645
6543
  }
5646
- const target = cps[cps.length - n];
5647
6544
  setInput("");
5648
- await doRewind(target, n);
5649
6545
  return;
5650
6546
  }
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();
6547
+ if (cmdLower === "/code") {
6548
+ if (sessionMode === "code") {
6549
+ setDisplayMessages((prev) => [
6550
+ ...prev,
6551
+ { role: "user", content: trimmed },
6552
+ {
6553
+ role: "assistant",
6554
+ content: "\u5DF2\u7ECF\u5728\u4EE3\u7801\u6A21\u5F0F\u4E2D\u3002\u8F93\u5165 /plan \u5207\u56DE\u8BA1\u5212\u6A21\u5F0F\u3002"
6555
+ }
6556
+ ]);
6557
+ } else {
6558
+ setSessionMode("code");
6559
+ sessionRef.current?.setMode("code");
6560
+ setToolChoice(void 0);
6561
+ setDisplayMessages((prev) => [
6562
+ ...prev,
6563
+ { role: "user", content: trimmed },
6564
+ {
6565
+ role: "assistant",
6566
+ 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"
6567
+ }
6568
+ ]);
6569
+ sessionRef.current?.reset();
6570
+ }
6571
+ setInput("");
6572
+ return;
5707
6573
  }
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);
6574
+ if (cmdLower === "/tools") {
6575
+ const next = toolChoice === void 0 ? "none" : toolChoice === "none" ? "required" : toolChoice === "required" ? "auto" : void 0;
6576
+ setToolChoice(next);
6577
+ 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
6578
  setDisplayMessages((prev) => [
5723
6579
  ...prev,
5724
6580
  { 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" }
6581
+ { role: "assistant", content: `\u5DE5\u5177\u8C03\u7528\u7B56\u7565\u5DF2\u5207\u6362\u4E3A ${label}` }
5726
6582
  ]);
5727
- sessionRef.current?.reset();
6583
+ setInput("");
6584
+ return;
6585
+ }
6586
+ const cmd = commandRegistry.get(cmdLower);
6587
+ if (cmd) {
6588
+ const result = cmd.handler();
6589
+ switch (result.kind) {
6590
+ case "exit":
6591
+ await sessionRef.current?.flushLog();
6592
+ process.exit(0);
6593
+ return;
6594
+ case "clear":
6595
+ setDisplayMessages([]);
6596
+ setStaticKey((prev) => prev + 1);
6597
+ setInput("");
6598
+ setStreamError(void 0);
6599
+ process.stdout.write("\x1B[2J\x1B[H\x1B[3J");
6600
+ sessionRef.current?.reset();
6601
+ return;
6602
+ case "navigate":
6603
+ setInput("");
6604
+ if (result.target === "game") {
6605
+ onLaunchGame?.();
6606
+ } else if (result.target === "stock") {
6607
+ onLaunchStock?.();
6608
+ }
6609
+ return;
6610
+ case "text":
6611
+ setDisplayMessages((prev) => [
6612
+ ...prev,
6613
+ { role: "user", content: trimmed },
6614
+ { role: "assistant", content: result.content }
6615
+ ]);
6616
+ setInput("");
6617
+ return;
6618
+ }
5728
6619
  }
6620
+ setDisplayMessages((prev) => [
6621
+ ...prev,
6622
+ { role: "user", content: trimmed },
6623
+ { role: "assistant", content: `\u672A\u77E5\u547D\u4EE4\uFF1A${trimmed}\u3002\u8F93\u5165 /help \u67E5\u770B\u3002` }
6624
+ ]);
5729
6625
  setInput("");
5730
6626
  return;
5731
6627
  }
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";
6628
+ if (!sessionRef.current) {
5736
6629
  setDisplayMessages((prev) => [
5737
6630
  ...prev,
5738
6631
  { role: "user", content: trimmed },
5739
- { role: "assistant", content: `\u5DE5\u5177\u8C03\u7528\u7B56\u7565\u5DF2\u5207\u6362\u4E3A ${label}` }
6632
+ {
6633
+ role: "assistant",
6634
+ content: "\u26A0 \u65E0\u6CD5\u8FDE\u63A5\u5230 Provider\u3002\u8BF7\u68C0\u67E5 API Key \u548C\u7F51\u7EDC\u914D\u7F6E\u3002"
6635
+ }
5740
6636
  ]);
5741
6637
  setInput("");
5742
6638
  return;
5743
6639
  }
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
- ]);
6640
+ setDisplayMessages((prev) => [...prev, { role: "user", content: trimmed }]);
5783
6641
  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;
6642
+ setIsStreaming(true);
6643
+ setStreamingPhase("thinking");
6644
+ setStreamingPlaceholder(pickRandom(STREAMING_PLACEHOLDERS));
6645
+ setCurrentContent("");
6646
+ setCurrentReasoning([]);
6647
+ setCurrentToolCalls([]);
6648
+ setCurrentUsage(void 0);
6649
+ setCurrentElapsed(void 0);
6650
+ setCurrentCost(void 0);
6651
+ setStreamingModel(void 0);
6652
+ setStreamError(void 0);
6653
+ currentContentRef.current = "";
6654
+ currentReasoningRef.current = [];
6655
+ currentToolCallsRef.current = [];
6656
+ currentUsageRef.current = void 0;
6657
+ currentElapsedRef.current = void 0;
6658
+ currentCostRef.current = void 0;
6659
+ currentModelRef.current = void 0;
6660
+ streamErrorRef.current = void 0;
6661
+ currentRoundModifiedRef.current = false;
6662
+ setRewindHintPhase("idle");
6663
+ if (rewindHintTimerRef.current) {
6664
+ clearTimeout(rewindHintTimerRef.current);
6665
+ rewindHintTimerRef.current = null;
6666
+ }
6667
+ const session = sessionRef.current;
6668
+ const abortController = new AbortController();
6669
+ abortRef.current = abortController;
6670
+ try {
6671
+ for await (const event of session.chat(trimmed, {
6672
+ thinkingAllowed: thinkingEnabled || void 0,
6673
+ thinkingEffort: thinkingEnabled ? thinkingEffort : void 0,
6674
+ responseFormat: responseFormat !== "text" ? responseFormat : void 0,
6675
+ toolChoice
6676
+ })) {
6677
+ if (abortController.signal.aborted) break;
6678
+ switch (event.type) {
6679
+ case "text_delta":
6680
+ setStreamingPhase("generating");
6681
+ setCurrentContent((prev) => {
6682
+ const next = prev + event.content;
6683
+ currentContentRef.current = next;
6684
+ return next;
6685
+ });
6686
+ break;
6687
+ case "reasoning_delta":
6688
+ setStreamingPhase("thinking");
6689
+ setCurrentReasoning((prev) => {
6690
+ const last = prev[prev.length - 1];
6691
+ const next = last !== void 0 ? [...prev.slice(0, -1), last + event.content] : [...prev, event.content];
6692
+ currentReasoningRef.current = next;
6693
+ return next;
6694
+ });
6695
+ break;
6696
+ case "tool_calls":
6697
+ setStreamingPhase("calling_tools");
6698
+ setCurrentToolCalls((prev) => {
6699
+ const next = [...prev, ...event.calls];
6700
+ currentToolCallsRef.current = next;
6701
+ return next;
6702
+ });
6703
+ for (const call of event.calls) {
6704
+ if (isFileMutatingTool(call.name)) {
6705
+ currentRoundModifiedRef.current = true;
6706
+ break;
6707
+ }
5854
6708
  }
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,
6709
+ break;
6710
+ case "tool_result":
6711
+ setStreamingPhase("executing_tools");
6712
+ setTimeout(() => setStreamingPhase("thinking"), 300);
6713
+ setCurrentContent("");
6714
+ currentContentRef.current = "";
6715
+ setCurrentReasoning((prev) => {
6716
+ const next = [...prev, ""];
6717
+ currentReasoningRef.current = next;
6718
+ return next;
6719
+ });
6720
+ setCurrentToolCalls([]);
6721
+ currentToolCallsRef.current = [];
6722
+ const r = event.result;
6723
+ if (event.name.startsWith("todo_") && event.todoSnapshot) {
6724
+ setTodoSnapshot(event.todoSnapshot);
6725
+ } else {
6726
+ 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"}`;
6727
+ setDisplayMessages((prev) => [
6728
+ ...prev,
6729
+ {
6730
+ role: "tool",
6731
+ content: line,
6732
+ diff: r.diff
6733
+ }
6734
+ ]);
6735
+ }
6736
+ break;
6737
+ case "usage":
6738
+ setCurrentUsage(event.usage);
6739
+ setStreamingModel(event.model);
6740
+ currentUsageRef.current = event.usage;
6741
+ currentModelRef.current = event.model;
5868
6742
  {
5869
- role: "tool",
5870
- content: line,
5871
- diff: r.diff
6743
+ const cost = calculateCost(
6744
+ event.usage,
6745
+ event.model
6746
+ );
6747
+ setCurrentCost(cost.totalCost);
6748
+ currentCostRef.current = cost.totalCost;
5872
6749
  }
5873
- ]);
5874
- break;
5875
- case "usage":
5876
- setCurrentUsage(event.usage);
5877
- setStreamingModel(event.model);
5878
- currentUsageRef.current = event.usage;
5879
- currentModelRef.current = event.model;
6750
+ break;
6751
+ case "done":
6752
+ setCurrentElapsed(event.elapsed);
6753
+ currentElapsedRef.current = event.elapsed;
6754
+ break;
6755
+ case "error":
6756
+ setStreamError(event.error.message);
6757
+ streamErrorRef.current = event.error.message;
6758
+ break;
6759
+ }
6760
+ }
6761
+ } catch (err) {
6762
+ const msg = err instanceof Error ? err.message : String(err);
6763
+ setStreamError(msg);
6764
+ streamErrorRef.current = msg;
6765
+ } finally {
6766
+ setIsStreaming(false);
6767
+ setStreamingPhase(null);
6768
+ setIdlePlaceholder(pickRandom(IDLE_PLACEHOLDERS));
6769
+ abortRef.current = null;
6770
+ const finContent = currentContentRef.current;
6771
+ const finReasoning = currentReasoningRef.current.map((s) => s.trim()).filter((s) => s.length > 0);
6772
+ const finToolCalls = currentToolCallsRef.current.length > 0 ? currentToolCallsRef.current : void 0;
6773
+ const finStreamError = streamErrorRef.current;
6774
+ if (finContent || finToolCalls || finStreamError) {
6775
+ const completed = {
6776
+ content: finStreamError ? `\u26A0 \u8BF7\u6C42\u51FA\u9519\uFF1A${finStreamError}` : finContent || "",
6777
+ ...finReasoning.length > 0 ? { reasoning: finReasoning } : {},
6778
+ toolCalls: finToolCalls,
6779
+ usage: currentUsageRef.current,
6780
+ elapsed: currentElapsedRef.current,
6781
+ cost: currentCostRef.current,
6782
+ model: currentModelRef.current
6783
+ };
6784
+ setDisplayMessages((prev) => [
6785
+ ...prev,
5880
6786
  {
5881
- const cost = calculateCost(event.usage, event.model);
5882
- setCurrentCost(cost.totalCost);
5883
- currentCostRef.current = cost.totalCost;
6787
+ role: "assistant",
6788
+ content: completed.content,
6789
+ assistantDetail: completed
5884
6790
  }
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;
6791
+ ]);
6792
+ }
6793
+ if (currentRoundModifiedRef.current && !finStreamError) {
6794
+ setRewindHintPhase("pending");
6795
+ if (rewindHintTimerRef.current) clearTimeout(rewindHintTimerRef.current);
6796
+ rewindHintTimerRef.current = setTimeout(() => {
6797
+ setRewindHintPhase((prev) => prev === "pending" ? "visible" : prev);
6798
+ rewindHintTimerRef.current = null;
6799
+ }, 2e3);
5894
6800
  }
5895
6801
  }
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]);
6802
+ },
6803
+ [
6804
+ onLaunchGame,
6805
+ onLaunchStock,
6806
+ currentContent,
6807
+ currentReasoning,
6808
+ currentToolCalls,
6809
+ skills,
6810
+ skillSelectIndex,
6811
+ getFilteredSkills,
6812
+ thinkingEnabled,
6813
+ thinkingEffort,
6814
+ responseFormat,
6815
+ toolChoice,
6816
+ activeModel,
6817
+ sessionMode,
6818
+ isStreaming,
6819
+ rewinding
6820
+ ]
6821
+ );
5936
6822
  useEffect4(() => {
5937
6823
  if (!isStreaming && externalCostTracker) {
5938
6824
  setTodayCost(externalCostTracker.todayTotalCost);
5939
6825
  }
5940
6826
  }, [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) => {
6827
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [
6828
+ !hasConversationStarted && /* @__PURE__ */ jsxs10(Box10, { flexDirection: "row", marginBottom: 1, children: [
6829
+ /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", marginRight: 4, children: LOGO_LINES.map((line, i) => {
5944
6830
  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);
6831
+ return /* @__PURE__ */ jsx10(Box10, { children: /* @__PURE__ */ jsx10(Text11, { bold: true, color: CYBER_PALETTE[colorIndex], children: line }) }, i);
5946
6832
  }) }),
5947
- /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", justifyContent: "center", children: [
5948
- /* @__PURE__ */ jsxs9(Text10, { color: "#00ff41", children: [
6833
+ /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", justifyContent: "center", children: [
6834
+ /* @__PURE__ */ jsxs10(Text11, { color: "#00ff41", children: [
5949
6835
  " \u2714 ",
5950
6836
  "\u5DF2\u5C31\u7EEA ",
5951
6837
  skillCount,
5952
6838
  " \u4E2A Skill"
5953
6839
  ] }),
5954
- /* @__PURE__ */ jsxs9(Text10, { color: "#00ffff", children: [
6840
+ /* @__PURE__ */ jsxs10(Text11, { color: "#00ffff", children: [
5955
6841
  " \u2139 ",
5956
6842
  "\u5DF2\u5C31\u7EEA ",
5957
6843
  toolCount,
5958
6844
  " \u4E2A\u5DE5\u5177"
5959
6845
  ] }),
5960
- /* @__PURE__ */ jsxs9(Text10, { color: "#00ffff", children: [
6846
+ /* @__PURE__ */ jsxs10(Text11, { color: "#00ffff", children: [
5961
6847
  " \u{1F527} \u6A21\u578B ",
5962
6848
  SUPPORTED_MODELS[activeModel]?.displayName ?? activeModel
5963
6849
  ] }),
5964
- thinkingEnabled && /* @__PURE__ */ jsxs9(Text10, { color: "#ff9800", children: [
6850
+ thinkingEnabled && /* @__PURE__ */ jsxs10(Text11, { color: "#ff9800", children: [
5965
6851
  " \u{1F9E0} \u6DF1\u5EA6\u601D\u8003 ",
5966
6852
  thinkingEffort === "max" ? "Max" : "High"
5967
6853
  ] }),
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: [
6854
+ sessionMode === "plan" && /* @__PURE__ */ jsx10(Text11, { color: "#ff69b4", bold: true, children: " \u{1F4CB} \u8BA1\u5212\u6A21\u5F0F" }),
6855
+ responseFormat === "json_object" && /* @__PURE__ */ jsx10(Text11, { color: "#4caf50", children: " \u{1F4C4} JSON" }),
6856
+ toolChoice !== void 0 && /* @__PURE__ */ jsxs10(Text11, { color: "#e91e63", children: [
5971
6857
  " \u{1F6E0} ",
5972
6858
  toolChoice === "none" ? "\u7981\u6B62\u5DE5\u5177" : toolChoice === "required" ? "\u5F3A\u5236\u5DE5\u5177" : ""
5973
6859
  ] }),
@@ -5975,52 +6861,62 @@ function ChatSession({
5975
6861
  const tip = cmdTips[cmdTipIndex % cmdTips.length];
5976
6862
  if (!tip) return null;
5977
6863
  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 })
6864
+ return /* @__PURE__ */ jsxs10(Text11, { children: [
6865
+ /* @__PURE__ */ jsx10(Text11, { color: "#808080", children: " \u{1F4A1} " }),
6866
+ 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
6867
  ] });
5982
6868
  })(),
5983
- verbose ? /* @__PURE__ */ jsx9(Text10, { color: "#ff1493", children: " \u26A1 Verbose" }) : null
6869
+ verbose ? /* @__PURE__ */ jsx10(Text11, { color: "#ff1493", children: " \u26A1 Verbose" }) : null
5984
6870
  ] }),
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
- ] })
6871
+ /* @__PURE__ */ jsxs10(
6872
+ Box10,
6873
+ {
6874
+ flexGrow: 1,
6875
+ flexDirection: "column",
6876
+ justifyContent: "center",
6877
+ alignItems: "flex-end",
6878
+ children: [
6879
+ balanceLoading && balance === null ? /* @__PURE__ */ jsx10(Text11, { color: "yellow", children: " \u23F3 \u67E5\u8BE2\u4F59\u989D..." }) : balance !== null ? /* @__PURE__ */ jsxs10(Box10, { flexDirection: "row", children: [
6880
+ /* @__PURE__ */ jsx10(Text11, { color: "yellow", children: "\u{1F4B0} " }),
6881
+ /* @__PURE__ */ jsxs10(Text11, { color: "yellow", children: [
6882
+ "\u4F59\u989D \xA5",
6883
+ balance.toFixed(2)
6884
+ ] })
6885
+ ] }) : null,
6886
+ todayCost !== null ? /* @__PURE__ */ jsxs10(Box10, { flexDirection: "row", children: [
6887
+ /* @__PURE__ */ jsx10(Text11, { color: "cyan", children: "\u{1F4CA} " }),
6888
+ /* @__PURE__ */ jsxs10(Text11, { color: "cyan", children: [
6889
+ "\u4ECA\u65E5 \xA5",
6890
+ todayCost.toFixed(2)
6891
+ ] })
6892
+ ] }) : null
6893
+ ]
6894
+ }
6895
+ )
6001
6896
  ] }),
6002
- /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginTop: 1, children: [
6003
- /* @__PURE__ */ jsx9(Static, { items: displayMessages, children: (msg, i) => {
6897
+ /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", marginTop: 1, children: [
6898
+ /* @__PURE__ */ jsx10(Static, { items: displayMessages, children: (msg, i) => {
6004
6899
  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 }) })
6900
+ return /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, children: [
6901
+ /* @__PURE__ */ jsx10(Box10, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx10(Text11, { bold: true, color: "#00ff41", children: "\u{1F464}" }) }),
6902
+ /* @__PURE__ */ jsx10(Box10, { flexGrow: 1, children: /* @__PURE__ */ jsx10(Text11, { wrap: "wrap", children: msg.content }) })
6008
6903
  ] }, i);
6009
6904
  }
6010
6905
  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 }) })
6906
+ return /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, flexDirection: "column", children: [
6907
+ /* @__PURE__ */ jsxs10(Box10, { flexDirection: "row", children: [
6908
+ /* @__PURE__ */ jsx10(Box10, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx10(Text11, { dimColor: true, children: "\u{1F527}" }) }),
6909
+ /* @__PURE__ */ jsx10(Box10, { flexGrow: 1, children: /* @__PURE__ */ jsx10(Text11, { dimColor: true, wrap: "wrap", children: msg.content }) })
6015
6910
  ] }),
6016
- msg.diff && /* @__PURE__ */ jsx9(DiffPreview, { diff: msg.diff })
6911
+ msg.diff && /* @__PURE__ */ jsx10(DiffPreview, { diff: msg.diff })
6017
6912
  ] }, i);
6018
6913
  }
6019
6914
  const detail = msg.assistantDetail;
6020
- return /* @__PURE__ */ jsx9(
6915
+ return /* @__PURE__ */ jsx10(
6021
6916
  AssistantMessage,
6022
6917
  {
6023
6918
  content: msg.content,
6919
+ reasoning: detail?.reasoning,
6024
6920
  toolCalls: detail?.toolCalls,
6025
6921
  isStreaming: false,
6026
6922
  usage: detail?.usage,
@@ -6031,10 +6927,11 @@ function ChatSession({
6031
6927
  i
6032
6928
  );
6033
6929
  } }, staticKey),
6034
- isStreaming && /* @__PURE__ */ jsx9(
6930
+ isStreaming && /* @__PURE__ */ jsx10(
6035
6931
  AssistantMessage,
6036
6932
  {
6037
6933
  content: currentContent,
6934
+ reasoning: currentReasoning,
6038
6935
  toolCalls: currentToolCalls.length > 0 ? currentToolCalls : void 0,
6039
6936
  isStreaming: true,
6040
6937
  usage: _currentUsage,
@@ -6042,106 +6939,100 @@ function ChatSession({
6042
6939
  model: _streamingModel
6043
6940
  }
6044
6941
  ),
6045
- !isStreaming && streamError && /* @__PURE__ */ jsx9(Box9, { marginTop: 1, marginLeft: 3, children: /* @__PURE__ */ jsxs9(Text10, { color: "red", children: [
6942
+ !isStreaming && streamError && /* @__PURE__ */ jsx10(Box10, { marginTop: 1, marginLeft: 3, children: /* @__PURE__ */ jsxs10(Text11, { color: "red", children: [
6046
6943
  "\u26A0 ",
6047
6944
  streamError
6048
6945
  ] }) })
6049
6946
  ] }),
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" }),
6947
+ todoSnapshot.length > 0 && /* @__PURE__ */ jsx10(TodoListPanel, { items: todoSnapshot }),
6948
+ selectingModel ? /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, flexDirection: "column", children: [
6949
+ /* @__PURE__ */ jsx10(Text11, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) }),
6950
+ /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", marginTop: 1, children: [
6951
+ /* @__PURE__ */ jsx10(Text11, { bold: true, color: "#00ffff", children: "\u9009\u62E9\u6A21\u578B\uFF1A" }),
6054
6952
  modelOptions.map((id, i) => {
6055
6953
  const meta = SUPPORTED_MODELS[id];
6056
6954
  const isCurrent = id === activeModel;
6057
6955
  const isSelected = i === modelSelectIndex;
6058
6956
  const marker = isSelected ? " > " : " ";
6059
6957
  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: [
6958
+ return /* @__PURE__ */ jsxs10(Box10, { children: [
6959
+ /* @__PURE__ */ jsxs10(Text11, { color: isSelected ? "#00ff41" : void 0, bold: isSelected, children: [
6960
+ marker,
6961
+ meta.displayName,
6962
+ suffix
6963
+ ] }),
6964
+ isSelected && /* @__PURE__ */ jsxs10(Text11, { color: "#808080", children: [
6074
6965
  " \u2014 ",
6075
6966
  id
6076
6967
  ] })
6077
6968
  ] }, id);
6078
6969
  }),
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" }) })
6970
+ /* @__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
6971
  ] }),
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" }),
6972
+ /* @__PURE__ */ jsx10(Text11, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) })
6973
+ ] }) : rewindSelecting ? /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, flexDirection: "column", children: [
6974
+ /* @__PURE__ */ jsx10(Text11, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) }),
6975
+ /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", marginTop: 1, children: [
6976
+ /* @__PURE__ */ jsx10(Text11, { bold: true, color: "#ff9800", children: "\u9009\u62E9\u8981\u56DE\u9000\u7684\u68C0\u67E5\u70B9\uFF1A" }),
6086
6977
  rewindList.map((cp2, i) => {
6087
6978
  const isSelected = i === rewindSelectIndex;
6088
6979
  const marker = isSelected ? " > " : " ";
6089
6980
  const time = new Date(cp2.timestamp).toLocaleTimeString();
6090
6981
  const preview = cp2.preview || "(\u7A7A)";
6091
6982
  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);
6983
+ return /* @__PURE__ */ jsx10(Box10, { children: /* @__PURE__ */ jsxs10(Text11, { color: isSelected ? "#ff9800" : void 0, bold: isSelected, children: [
6984
+ marker,
6985
+ "#",
6986
+ i + 1,
6987
+ " ",
6988
+ time,
6989
+ " `",
6990
+ preview,
6991
+ tag,
6992
+ "`"
6993
+ ] }) }, cp2.index);
6110
6994
  }),
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" }) })
6995
+ /* @__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
6996
  ] }),
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: [
6997
+ /* @__PURE__ */ jsx10(Text11, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) })
6998
+ ] }) : /* @__PURE__ */ jsxs10(Fragment2, { children: [
6999
+ (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
7000
  PHASE_CONFIG[streamingPhase].icon,
6117
7001
  " ",
6118
7002
  PHASE_CONFIG[streamingPhase].label,
6119
7003
  " ",
6120
- /* @__PURE__ */ jsx9(InkSpinner2, { type: "dots" })
7004
+ /* @__PURE__ */ jsx10(InkSpinner3, { type: "dots" })
6121
7005
  ] }) }) : rewindHintPhase === "visible" ? (
6122
7006
  // 本轮修改了文件时,流式结束后 2s 在原位置展示 /rewind 提示
6123
7007
  // 一直保留到下次对话开始,与 /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" }) })
7008
+ /* @__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
7009
  ) : 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(
7010
+ /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: hasConversationStarted || sessionMode === "plan" ? /* @__PURE__ */ jsxs10(
7011
+ Text11,
7012
+ {
7013
+ color: sessionMode === "plan" ? "#ff69b4" : "#00ffff",
7014
+ dimColor: sessionMode !== "plan",
7015
+ children: [
7016
+ /* @__PURE__ */ jsx10(Text11, { children: sessionMode === "plan" ? "\u2500".repeat(Math.max(dividerWidth - 48, 1)) : "\u2500".repeat(Math.max(dividerWidth - 35, 10)) }),
7017
+ balance !== null && /* @__PURE__ */ jsxs10(Text11, { color: "yellow", children: [
7018
+ " \u{1F4B0} \u4F59\u989D \xA5",
7019
+ balance.toFixed(2)
7020
+ ] }),
7021
+ isStreaming ? /* @__PURE__ */ jsxs10(Text11, { color: "cyan", children: [
7022
+ " \u{1F4CA} \u672C\u6B21 \xA5",
7023
+ sessionCost > 0 ? sessionCost.toFixed(4) + " " : "",
7024
+ /* @__PURE__ */ jsx10(InkSpinner3, { type: "dots" })
7025
+ ] }) : sessionCost > 0 ? /* @__PURE__ */ jsxs10(Text11, { color: "cyan", children: [
7026
+ " \u{1F4CA} \u672C\u6B21 \xA5",
7027
+ sessionCost.toFixed(4)
7028
+ ] }) : null,
7029
+ sessionMode === "plan" && /* @__PURE__ */ jsx10(Text11, { color: "#ff69b4", bold: true, children: " \u{1F4CB} \u8BA1\u5212\u6A21\u5F0F" })
7030
+ ]
7031
+ }
7032
+ ) : /* @__PURE__ */ jsx10(Text11, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) }) }),
7033
+ /* @__PURE__ */ jsxs10(Box10, { children: [
7034
+ /* @__PURE__ */ jsx10(Box10, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx10(Text11, { bold: true, color: "#00ff41", children: "\u26A1" }) }),
7035
+ /* @__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
7036
  TextInput,
6146
7037
  {
6147
7038
  value: input,
@@ -6152,19 +7043,19 @@ function ChatSession({
6152
7043
  inputKey
6153
7044
  ) })
6154
7045
  ] }),
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 })
7046
+ /* @__PURE__ */ jsx10(Box10, { children: /* @__PURE__ */ jsx10(Text11, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) }) }),
7047
+ /* @__PURE__ */ jsx10(SkillSelector, { skills, input, selectedIndex: skillSelectIndex }),
7048
+ /* @__PURE__ */ jsx10(FileSelector, { files, input, selectedIndex: fileSelectIndex })
6158
7049
  ] }),
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" }) })
7050
+ 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" }) }),
7051
+ 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
7052
  ] });
6162
7053
  }
6163
7054
 
6164
7055
  // src/ui/GamePicker.tsx
6165
- import { Box as Box10, Text as Text11, useInput as useInput2 } from "ink";
7056
+ import { Box as Box11, Text as Text12, useInput as useInput2 } from "ink";
6166
7057
  import { useState as useState5, useCallback as useCallback3 } from "react";
6167
- import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
7058
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
6168
7059
  function GamePicker({ games, onSelect, onExit, onBackToChat }) {
6169
7060
  const [selectedIndex, setSelectedIndex] = useState5(0);
6170
7061
  const exitAction = onBackToChat ?? onExit ?? (() => process.exit(0));
@@ -6192,18 +7083,18 @@ function GamePicker({ games, onSelect, onExit, onBackToChat }) {
6192
7083
  [games, selectedIndex, onSelect, onExit, onBackToChat, handleCtrlC]
6193
7084
  )
6194
7085
  );
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) => {
7086
+ return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
7087
+ /* @__PURE__ */ jsx11(Box11, { marginBottom: 1, children: /* @__PURE__ */ jsx11(Text12, { bold: true, color: "#00ffff", children: "\u{1F3AE} \u6E38\u620F\u5217\u8868" }) }),
7088
+ /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", children: games.map((game, index) => {
6198
7089
  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 }) })
7090
+ return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "row", children: [
7091
+ /* @__PURE__ */ jsx11(Box11, { width: 3, flexShrink: 0, children: isSelected ? /* @__PURE__ */ jsx11(Text12, { bold: true, color: "#00ff41", children: "\u25B8 " }) : /* @__PURE__ */ jsx11(Text12, { children: " " }) }),
7092
+ /* @__PURE__ */ jsx11(Box11, { width: 20, flexShrink: 0, children: /* @__PURE__ */ jsx11(Text12, { bold: true, color: isSelected ? "#00ff41" : "#ffffff", children: game.name }) }),
7093
+ /* @__PURE__ */ jsx11(Box11, { children: /* @__PURE__ */ jsx11(Text12, { color: "#888888", children: game.description }) })
6203
7094
  ] }, game.id);
6204
7095
  }) }),
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" }) })
7096
+ /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text12, { dimColor: true, children: " \u2191/\u2193 \u9009\u62E9 Enter \u542F\u52A8 q \u8FD4\u56DE" }) }),
7097
+ 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
7098
  ] });
6208
7099
  }
6209
7100
 
@@ -6220,9 +7111,9 @@ function listGames() {
6220
7111
  }
6221
7112
 
6222
7113
  // src/game/brick-breaker/index.tsx
6223
- import { Box as Box11, Text as Text12, useInput as useInput3, render as render2 } from "ink";
7114
+ import { Box as Box12, Text as Text13, useInput as useInput3, render as render2 } from "ink";
6224
7115
  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";
7116
+ import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
6226
7117
  var GAME_WIDTH = 40;
6227
7118
  var GAME_HEIGHT = 18;
6228
7119
  var PADDLE_WIDTH = 9;
@@ -6412,49 +7303,49 @@ function BrickBreakerGame({ onExit: _onExit }) {
6412
7303
  const board = buildBoard(s);
6413
7304
  const def = getLevel(s.level);
6414
7305
  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: [
7306
+ return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
7307
+ /* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", children: [
7308
+ /* @__PURE__ */ jsx12(Box12, { width: 20, children: /* @__PURE__ */ jsxs12(Text13, { children: [
6418
7309
  "\u5173\u5361 ",
6419
7310
  s.level,
6420
7311
  ": ",
6421
- /* @__PURE__ */ jsx11(Text12, { color: "cyan", children: def.desc })
7312
+ /* @__PURE__ */ jsx12(Text13, { color: "cyan", children: def.desc })
6422
7313
  ] }) }),
6423
- /* @__PURE__ */ jsx11(Box11, { width: 12, children: /* @__PURE__ */ jsxs11(Text12, { children: [
7314
+ /* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsxs12(Text13, { children: [
6424
7315
  "\u5206\u6570: ",
6425
- /* @__PURE__ */ jsx11(Text12, { color: "yellow", children: String(s.score).padStart(3, "0") })
7316
+ /* @__PURE__ */ jsx12(Text13, { color: "yellow", children: String(s.score).padStart(3, "0") })
6426
7317
  ] }) }),
6427
- /* @__PURE__ */ jsx11(Box11, { width: 12, children: /* @__PURE__ */ jsxs11(Text12, { children: [
7318
+ /* @__PURE__ */ jsx12(Box12, { width: 12, children: /* @__PURE__ */ jsxs12(Text13, { children: [
6428
7319
  "\u751F\u547D: ",
6429
- /* @__PURE__ */ jsx11(Text12, { color: "red", children: "\u2665".repeat(Math.max(0, s.lives)) })
7320
+ /* @__PURE__ */ jsx12(Text13, { color: "red", children: "\u2665".repeat(Math.max(0, s.lives)) })
6430
7321
  ] }) }),
6431
- /* @__PURE__ */ jsx11(Box11, { width: 10, children: /* @__PURE__ */ jsxs11(Text12, { children: [
7322
+ /* @__PURE__ */ jsx12(Box12, { width: 10, children: /* @__PURE__ */ jsxs12(Text13, { children: [
6432
7323
  "\u7816\u5757: ",
6433
- /* @__PURE__ */ jsx11(Text12, { color: "cyan", children: aliveCount })
7324
+ /* @__PURE__ */ jsx12(Text13, { color: "cyan", children: aliveCount })
6434
7325
  ] }) }),
6435
- /* @__PURE__ */ jsx11(Box11, { children: /* @__PURE__ */ jsxs11(Text12, { color: s.paused ? "gray" : "green", children: [
7326
+ /* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsxs12(Text13, { color: s.paused ? "gray" : "green", children: [
6436
7327
  "[",
6437
7328
  s.paused ? "\u6682\u505C" : "\u8FD0\u884C\u4E2D",
6438
7329
  "]"
6439
7330
  ] }) })
6440
7331
  ] }),
6441
- /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
6442
- /* @__PURE__ */ jsxs11(Text12, { children: [
7332
+ /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
7333
+ /* @__PURE__ */ jsxs12(Text13, { children: [
6443
7334
  "\u250C",
6444
7335
  "\u2500".repeat(GAME_WIDTH),
6445
7336
  "\u2510"
6446
7337
  ] }),
6447
- /* @__PURE__ */ jsx11(Text12, { children: selectingLevel ? " \x1B[90m\u9009\u62E9\u5173\u5361\u540E\u5F00\u59CB...\x1B[0m" : board }),
6448
- /* @__PURE__ */ jsxs11(Text12, { children: [
7338
+ /* @__PURE__ */ jsx12(Text13, { children: selectingLevel ? " \x1B[90m\u9009\u62E9\u5173\u5361\u540E\u5F00\u59CB...\x1B[0m" : board }),
7339
+ /* @__PURE__ */ jsxs12(Text13, { children: [
6449
7340
  "\u2514",
6450
7341
  "\u2500".repeat(GAME_WIDTH),
6451
7342
  "\u2518"
6452
7343
  ] })
6453
7344
  ] }),
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) }),
7345
+ selectingLevel && /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, flexDirection: "column", children: [
7346
+ /* @__PURE__ */ jsx12(Text13, { bold: true, color: "yellow", children: "\u9009\u62E9\u5173\u5361" }),
7347
+ /* @__PURE__ */ jsx12(Box12, { flexDirection: "row", flexWrap: "wrap", children: LEVELS.map((lv, i) => /* @__PURE__ */ jsx12(Box12, { width: 22, children: /* @__PURE__ */ jsxs12(Text13, { children: [
7348
+ /* @__PURE__ */ jsx12(Text13, { color: initialLevel === i + 1 ? "green" : "white", children: i + 1 === 10 ? "0" : String(i + 1) }),
6458
7349
  ". ",
6459
7350
  lv.desc,
6460
7351
  " (",
@@ -6463,16 +7354,16 @@ function BrickBreakerGame({ onExit: _onExit }) {
6463
7354
  lv.cols,
6464
7355
  ")"
6465
7356
  ] }) }, i)) }),
6466
- /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text12, { dimColor: true, children: "\u6309\u6570\u5B57\u9009\u5173 q \u9000\u51FA" }) })
7357
+ /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text13, { dimColor: true, children: "\u6309\u6570\u5B57\u9009\u5173 q \u9000\u51FA" }) })
6467
7358
  ] }),
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: [
7359
+ !selectingLevel && (s.gameOver || s.win) && /* @__PURE__ */ jsxs12(Box12, { marginTop: 1, children: [
7360
+ /* @__PURE__ */ jsx12(Text13, { bold: true, color: s.gameOver ? "red" : "green", children: s.gameOver ? "\u6E38\u620F\u7ED3\u675F\uFF01" : "\u606D\u559C\u901A\u5173\uFF01" }),
7361
+ /* @__PURE__ */ jsxs12(Text13, { children: [
6471
7362
  " \u5206\u6570: ",
6472
- /* @__PURE__ */ jsx11(Text12, { color: "yellow", children: s.score })
7363
+ /* @__PURE__ */ jsx12(Text13, { color: "yellow", children: s.score })
6473
7364
  ] })
6474
7365
  ] }),
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" }) })
7366
+ !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
7367
  ] });
6477
7368
  }
6478
7369
  var brick_breaker_default = {
@@ -6482,7 +7373,7 @@ var brick_breaker_default = {
6482
7373
  play: async () => {
6483
7374
  await new Promise((resolve2) => {
6484
7375
  const { unmount } = render2(
6485
- /* @__PURE__ */ jsx11(BrickBreakerGame, { onExit: () => {
7376
+ /* @__PURE__ */ jsx12(BrickBreakerGame, { onExit: () => {
6486
7377
  unmount();
6487
7378
  resolve2();
6488
7379
  } })
@@ -6492,9 +7383,9 @@ var brick_breaker_default = {
6492
7383
  };
6493
7384
 
6494
7385
  // src/game/coder-check/index.tsx
6495
- import { Box as Box12, Text as Text13, useInput as useInput4, render as render3 } from "ink";
7386
+ import { Box as Box13, Text as Text14, useInput as useInput4, render as render3 } from "ink";
6496
7387
  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";
7388
+ import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
6498
7389
  var GAME_W = 66;
6499
7390
  var GAME_H = 20;
6500
7391
  var SCORE_H = 6;
@@ -6965,44 +7856,44 @@ function CoderCheck({ onExit: _onExit }) {
6965
7856
  const scoreLines = buildScoreLines(scoreStr);
6966
7857
  const view = buildGameView(s, scoreLines, scoreColor, s.message);
6967
7858
  void tick2;
6968
- return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", paddingX: 1, children: [
6969
- /* @__PURE__ */ jsx12(Box12, { flexDirection: "row", children: /* @__PURE__ */ jsxs12(Text13, { children: [
7859
+ return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", paddingX: 1, children: [
7860
+ /* @__PURE__ */ jsx13(Box13, { flexDirection: "row", children: /* @__PURE__ */ jsxs13(Text14, { children: [
6970
7861
  "\u751F\u547D ",
6971
- /* @__PURE__ */ jsx12(Text13, { color: "red", children: "\u2665".repeat(Math.max(0, s.lives)) }),
7862
+ /* @__PURE__ */ jsx13(Text14, { color: "red", children: "\u2665".repeat(Math.max(0, s.lives)) }),
6972
7863
  " ",
6973
7864
  "\u901F\u5EA6 ",
6974
- /* @__PURE__ */ jsxs12(Text13, { color: "cyan", children: [
7865
+ /* @__PURE__ */ jsxs13(Text14, { color: "cyan", children: [
6975
7866
  "Lv.",
6976
7867
  Math.floor(s.speed * 10)
6977
7868
  ] })
6978
7869
  ] }) }),
6979
- !s.gameOver && s.target && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text13, { children: [
7870
+ !s.gameOver && s.target && /* @__PURE__ */ jsx13(Box13, { marginTop: 1, children: /* @__PURE__ */ jsxs13(Text14, { children: [
6980
7871
  "\u6253\u5B57: ",
6981
- /* @__PURE__ */ jsx12(Text13, { color: "green", children: s.typed }),
6982
- /* @__PURE__ */ jsx12(Text13, { color: "white", children: s.target.slice(s.typed.length) })
7872
+ /* @__PURE__ */ jsx13(Text14, { color: "green", children: s.typed }),
7873
+ /* @__PURE__ */ jsx13(Text14, { color: "white", children: s.target.slice(s.typed.length) })
6983
7874
  ] }) }),
6984
- /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginTop: 1, children: [
6985
- /* @__PURE__ */ jsxs12(Text13, { children: [
7875
+ /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", marginTop: 1, children: [
7876
+ /* @__PURE__ */ jsxs13(Text14, { children: [
6986
7877
  "\u250C",
6987
7878
  "\u2500".repeat(GAME_W),
6988
7879
  "\u2510"
6989
7880
  ] }),
6990
- view.map((row, i) => /* @__PURE__ */ jsx12(Text13, { children: `\u2502${row}\u2502` }, i)),
6991
- /* @__PURE__ */ jsxs12(Text13, { children: [
7881
+ view.map((row, i) => /* @__PURE__ */ jsx13(Text14, { children: `\u2502${row}\u2502` }, i)),
7882
+ /* @__PURE__ */ jsxs13(Text14, { children: [
6992
7883
  "\u2514",
6993
7884
  "\u2500".repeat(GAME_W),
6994
7885
  "\u2518"
6995
7886
  ] })
6996
7887
  ] }),
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: [
7888
+ s.gameOver && /* @__PURE__ */ jsxs13(Box13, { marginTop: 1, children: [
7889
+ /* @__PURE__ */ jsx13(Text14, { bold: true, color: "red", children: "\u6E38\u620F\u7ED3\u675F\uFF01" }),
7890
+ /* @__PURE__ */ jsxs13(Text14, { children: [
7000
7891
  " \u5F97\u5206: ",
7001
- /* @__PURE__ */ jsx12(Text13, { color: "yellow", children: s.score }),
7892
+ /* @__PURE__ */ jsx13(Text14, { color: "yellow", children: s.score }),
7002
7893
  " r \u91CD\u5F00 q \u9000\u51FA"
7003
7894
  ] })
7004
7895
  ] }),
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" }) })
7896
+ /* @__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
7897
  ] });
7007
7898
  }
7008
7899
  var coder_check_default = {
@@ -7012,7 +7903,7 @@ var coder_check_default = {
7012
7903
  play: async () => {
7013
7904
  await new Promise((resolve2) => {
7014
7905
  const { unmount } = render3(
7015
- /* @__PURE__ */ jsx12(CoderCheck, { onExit: () => {
7906
+ /* @__PURE__ */ jsx13(CoderCheck, { onExit: () => {
7016
7907
  unmount();
7017
7908
  resolve2();
7018
7909
  } })
@@ -7392,12 +8283,12 @@ import { render as render4 } from "ink";
7392
8283
  import chalk5 from "chalk";
7393
8284
 
7394
8285
  // src/stock/StockList.tsx
7395
- import { Box as Box13, Text as Text14, useInput as useInput5 } from "ink";
8286
+ import { Box as Box14, Text as Text15, useInput as useInput5 } from "ink";
7396
8287
  import { useState as useState8, useCallback as useCallback6, useEffect as useEffect7, useMemo as useMemo2 } from "react";
7397
8288
  import asciichart from "asciichart";
7398
8289
  import os from "os";
7399
8290
  import { join as join10 } from "path";
7400
- import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
8291
+ import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
7401
8292
  var SETTINGS_PATH = join10(os.homedir(), ".dskcode", "settings.json");
7402
8293
  function toFileUrl(p) {
7403
8294
  const norm = p.replace(/\\/g, "/");
@@ -7616,64 +8507,64 @@ function StockList({ codes, onExit, onBackToChat }) {
7616
8507
  );
7617
8508
  if (detailView) {
7618
8509
  if (detailLoading) {
7619
- return /* @__PURE__ */ jsx13(Box13, { paddingLeft: 1, children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: " \u27F3 \u52A0\u8F7D\u5206\u65F6\u6570\u636E..." }) });
8510
+ return /* @__PURE__ */ jsx14(Box14, { paddingLeft: 1, children: /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: " \u27F3 \u52A0\u8F7D\u5206\u65F6\u6570\u636E..." }) });
7620
8511
  }
7621
8512
  return renderDetail(detailView, () => setDetailView(null), detailPrices ?? void 0, detailCountdown, currentTime);
7622
8513
  }
7623
8514
  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: [
8515
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", children: [
8516
+ /* @__PURE__ */ jsxs14(Box14, { marginBottom: 1, justifyContent: "space-between", children: [
8517
+ /* @__PURE__ */ jsx14(Text15, { bold: true, ...cp2("#00ffff"), children: " \u{1F4C8} \u81EA\u9009\u80A1\u76D1\u63A7" }),
8518
+ /* @__PURE__ */ jsxs14(Text15, { dimColor: true, children: [
7628
8519
  " \u{1F550} ",
7629
8520
  currentTime
7630
8521
  ] }),
7631
- /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: loading ? " \u27F3 \u5237\u65B0\u4E2D..." : ` ${countdown}s \u540E\u81EA\u52A8\u5237\u65B0` })
8522
+ /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: loading ? " \u27F3 \u5237\u65B0\u4E2D..." : ` ${countdown}s \u540E\u81EA\u52A8\u5237\u65B0` })
7632
8523
  ] }),
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: [
8524
+ /* @__PURE__ */ jsxs14(Box14, { children: [
8525
+ /* @__PURE__ */ jsx14(Box14, { width: 3 }),
8526
+ /* @__PURE__ */ jsx14(Box14, { width: 9, children: /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: "\u4EE3\u7801" }) }),
8527
+ /* @__PURE__ */ jsx14(Box14, { width: 16, children: /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: "\u540D\u79F0" }) }),
8528
+ /* @__PURE__ */ jsx14(Box14, { width: 12, children: /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: "\u6700\u65B0\u4EF7" }) }),
8529
+ /* @__PURE__ */ jsx14(Box14, { width: 12, children: /* @__PURE__ */ jsxs14(Text15, { dimColor: true, children: [
7639
8530
  "\u6DA8\u8DCC\u5E45",
7640
8531
  sortOrder === "desc" ? " \u25BC" : sortOrder === "asc" ? " \u25B2" : ""
7641
8532
  ] }) }),
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" }) })
8533
+ /* @__PURE__ */ jsx14(Box14, { width: 12, children: /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: "\u6DA8\u8DCC\u989D" }) }),
8534
+ /* @__PURE__ */ jsx14(Box14, { width: 12, children: /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: "\u6700\u9AD8" }) }),
8535
+ /* @__PURE__ */ jsx14(Box14, { width: 12, children: /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: "\u6700\u4F4E" }) }),
8536
+ /* @__PURE__ */ jsx14(Box14, { children: /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: "\u6210\u4EA4\u989D" }) })
7646
8537
  ] }),
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) => {
8538
+ /* @__PURE__ */ jsx14(Box14, { children: /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: " " + "\u2500".repeat(100) }) }),
8539
+ /* @__PURE__ */ jsx14(Box14, { flexDirection: "column", children: sortedStocks.map((stock, index) => {
7649
8540
  const isSelected = index === selectedIndex;
7650
8541
  const isUp = stock.changePercent >= 0;
7651
8542
  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: [
8543
+ return /* @__PURE__ */ jsxs14(Box14, { children: [
8544
+ /* @__PURE__ */ jsx14(Box14, { width: 3, flexShrink: 0, children: isSelected ? /* @__PURE__ */ jsx14(Text15, { bold: true, ...cp2("#00ffff"), children: "\u25B8 " }) : /* @__PURE__ */ jsx14(Text15, { children: " " }) }),
8545
+ /* @__PURE__ */ jsx14(Box14, { width: 9, children: /* @__PURE__ */ jsx14(Text15, { bold: true, ...cp2(isSelected ? "#00ffff" : "#ffffff"), children: stock.code }) }),
8546
+ /* @__PURE__ */ jsx14(Box14, { width: 16, children: /* @__PURE__ */ jsx14(Text15, { ...cp2(isSelected ? "#ffffff" : "#cccccc"), children: stock.name }) }),
8547
+ /* @__PURE__ */ jsx14(Box14, { width: 12, children: /* @__PURE__ */ jsx14(Text15, { bold: true, ...cp2(color), children: formatPrice(stock.price) }) }),
8548
+ /* @__PURE__ */ jsx14(Box14, { width: 12, children: /* @__PURE__ */ jsxs14(Text15, { ...cp2(color), children: [
7658
8549
  isUp ? "+" : "",
7659
8550
  stock.changePercent.toFixed(2),
7660
8551
  "%"
7661
8552
  ] }) }),
7662
- /* @__PURE__ */ jsx13(Box13, { width: 12, children: /* @__PURE__ */ jsxs13(Text14, { ...cp2(color), children: [
8553
+ /* @__PURE__ */ jsx14(Box14, { width: 12, children: /* @__PURE__ */ jsxs14(Text15, { ...cp2(color), children: [
7663
8554
  isUp ? "+" : "",
7664
8555
  stock.changeAmount.toFixed(3)
7665
8556
  ] }) }),
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) }) })
8557
+ /* @__PURE__ */ jsx14(Box14, { width: 12, children: /* @__PURE__ */ jsx14(Text15, { ...cp2("#cccccc"), children: formatPrice(stock.high) }) }),
8558
+ /* @__PURE__ */ jsx14(Box14, { width: 12, children: /* @__PURE__ */ jsx14(Text15, { ...cp2("#888888"), children: formatPrice(stock.low) }) }),
8559
+ /* @__PURE__ */ jsx14(Box14, { children: /* @__PURE__ */ jsx14(Text15, { ...cp2("#888888"), children: formatAmount(stock.amount) }) })
7669
8560
  ] }, stock.code);
7670
8561
  }) }),
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) })
8562
+ /* @__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` }) }),
8563
+ /* @__PURE__ */ jsxs14(Box14, { children: [
8564
+ /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: ` \u6700\u540E\u66F4\u65B0: ${lastUpdate} \u7F16\u8F91\u81EA\u9009\u80A1: ` }),
8565
+ /* @__PURE__ */ jsx14(Text15, { ...cp2("#c792ea"), children: osc8Link(toFileUrl(SETTINGS_PATH), SETTINGS_PATH) })
7675
8566
  ] }),
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" }) })
8567
+ 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
8568
  ] });
7678
8569
  }
7679
8570
  function renderDetail(stock, _onBack, prices, countdown = 10, currentTime) {
@@ -7691,33 +8582,33 @@ function renderDetail(stock, _onBack, prices, countdown = 10, currentTime) {
7691
8582
  raw = raw.replaceAll("\u256D", "\u250C").replaceAll("\u256E", "\u2510").replaceAll("\u2570", "\u2514").replaceAll("\u256F", "\u2518");
7692
8583
  chartLines = raw.split("\n");
7693
8584
  }
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: [
8585
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", paddingLeft: 1, children: [
8586
+ /* @__PURE__ */ jsxs14(Box14, { marginBottom: 1, justifyContent: "space-between", children: [
8587
+ /* @__PURE__ */ jsxs14(Box14, { children: [
8588
+ /* @__PURE__ */ jsxs14(Text15, { bold: true, color: "#00ffff", children: [
7698
8589
  " \u{1F4CA} ",
7699
8590
  stock.name,
7700
8591
  " "
7701
8592
  ] }),
7702
- /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: stock.code }),
7703
- currentTime && /* @__PURE__ */ jsxs13(Text14, { dimColor: true, children: [
8593
+ /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: stock.code }),
8594
+ currentTime && /* @__PURE__ */ jsxs14(Text15, { dimColor: true, children: [
7704
8595
  " \u{1F550} ",
7705
8596
  currentTime
7706
8597
  ] })
7707
8598
  ] }),
7708
- /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: `${countdown}s \u540E\u5237\u65B0` })
8599
+ /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: `${countdown}s \u540E\u5237\u65B0` })
7709
8600
  ] }),
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: [
8601
+ /* @__PURE__ */ jsxs14(Box14, { children: [
8602
+ /* @__PURE__ */ jsx14(Box14, { width: 16, children: /* @__PURE__ */ jsx14(Text15, { bold: true, color: "#888888", children: "\u5F53\u524D\u4EF7" }) }),
8603
+ /* @__PURE__ */ jsx14(Box14, { children: /* @__PURE__ */ jsxs14(Text15, { bold: true, color: colorCode, children: [
7713
8604
  arrow,
7714
8605
  " ",
7715
8606
  formatPrice(stock.price)
7716
8607
  ] }) })
7717
8608
  ] }),
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: [
8609
+ /* @__PURE__ */ jsxs14(Box14, { children: [
8610
+ /* @__PURE__ */ jsx14(Box14, { width: 16, children: /* @__PURE__ */ jsx14(Text15, { color: "#888888", children: "\u6DA8\u8DCC\u5E45" }) }),
8611
+ /* @__PURE__ */ jsx14(Box14, { children: /* @__PURE__ */ jsxs14(Text15, { color: colorCode, children: [
7721
8612
  isUp ? "+" : "",
7722
8613
  stock.changePercent.toFixed(2),
7723
8614
  "%",
@@ -7726,8 +8617,8 @@ function renderDetail(stock, _onBack, prices, countdown = 10, currentTime) {
7726
8617
  stock.changeAmount.toFixed(3)
7727
8618
  ] }) })
7728
8619
  ] }),
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" }) })
8620
+ 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)) }),
8621
+ /* @__PURE__ */ jsx14(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text15, { dimColor: true, children: " Space/q \u8FD4\u56DE\u5217\u8868" }) })
7731
8622
  ] });
7732
8623
  }
7733
8624
 
@@ -7812,7 +8703,7 @@ async function scanProjectFiles(baseDir, dir) {
7812
8703
  }
7813
8704
 
7814
8705
  // src/cli/index.tsx
7815
- import { jsx as jsx14 } from "react/jsx-runtime";
8706
+ import { jsx as jsx15 } from "react/jsx-runtime";
7816
8707
  var SUBCOMMANDS = ["chat", "run", "setup", "init", "completion", "game", "stock"];
7817
8708
  function createCli() {
7818
8709
  const program2 = new Command();
@@ -7914,7 +8805,7 @@ compdef _dskcode_completion dskcode`);
7914
8805
  const freshResult = await loadAndValidate();
7915
8806
  const codeList = codes && codes.length > 0 ? codes : freshResult.config.stock?.symbols?.map((s) => s.code) ?? ["sh000001", "sz399006", "sh601688"];
7916
8807
  const app = renderApp(
7917
- /* @__PURE__ */ jsx14(
8808
+ /* @__PURE__ */ jsx15(
7918
8809
  StockList,
7919
8810
  {
7920
8811
  codes: codeList,
@@ -7943,7 +8834,7 @@ compdef _dskcode_completion dskcode`);
7943
8834
  }
7944
8835
  const selectedGame = await new Promise((resolve2) => {
7945
8836
  const { unmount } = render4(
7946
- /* @__PURE__ */ jsx14(
8837
+ /* @__PURE__ */ jsx15(
7947
8838
  GamePicker,
7948
8839
  {
7949
8840
  games,
@@ -7983,7 +8874,7 @@ async function startChat(ctx, costTracker) {
7983
8874
  );
7984
8875
  const model = defaultProvider?.model ?? "deepseek-v4-flash";
7985
8876
  const chatApp = renderApp(
7986
- /* @__PURE__ */ jsx14(
8877
+ /* @__PURE__ */ jsx15(
7987
8878
  ChatSession,
7988
8879
  {
7989
8880
  skillCount,
@@ -8001,7 +8892,7 @@ async function startChat(ctx, costTracker) {
8001
8892
  initGames();
8002
8893
  const games = listGames();
8003
8894
  const { unmount } = render4(
8004
- /* @__PURE__ */ jsx14(
8895
+ /* @__PURE__ */ jsx15(
8005
8896
  GamePicker,
8006
8897
  {
8007
8898
  games,
@@ -8025,7 +8916,7 @@ async function startChat(ctx, costTracker) {
8025
8916
  setImmediate(() => {
8026
8917
  const defaultStockCodes = ctx?.config.stock?.symbols?.map((s) => s.code) ?? ["sh000001", "sz399006", "sh601688"];
8027
8918
  const stockApp = renderApp(
8028
- /* @__PURE__ */ jsx14(
8919
+ /* @__PURE__ */ jsx15(
8029
8920
  StockList,
8030
8921
  {
8031
8922
  codes: defaultStockCodes,