dskcode 0.1.23 → 0.1.25

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-I6HEIOSU.js";
10
+ } from "./chunk-EF6NIFOH.js";
11
11
 
12
12
  // src/cli/index.tsx
13
13
  import { Command } from "commander";
@@ -785,15 +785,6 @@ function renderApp(node) {
785
785
  import { Text } from "ink";
786
786
  import InkSpinner from "ink-spinner";
787
787
  import { jsx, jsxs } from "react/jsx-runtime";
788
- function Spinner({ type = "dots", label }) {
789
- return /* @__PURE__ */ jsxs(Text, { children: [
790
- /* @__PURE__ */ jsx(Text, { color: "cyan", children: /* @__PURE__ */ jsx(InkSpinner, { type }) }),
791
- label ? /* @__PURE__ */ jsxs(Text, { children: [
792
- " ",
793
- label
794
- ] }) : null
795
- ] });
796
- }
797
788
 
798
789
  // src/ui/StatusMessage.tsx
799
790
  import { Box, Text as Text2 } from "ink";
@@ -1344,7 +1335,7 @@ function AssistantMessage({
1344
1335
  usage,
1345
1336
  elapsed,
1346
1337
  cost,
1347
- model
1338
+ model: _model
1348
1339
  }) {
1349
1340
  if (!content && (!toolCalls || toolCalls.length === 0) && !isStreaming) {
1350
1341
  return null;
@@ -1430,7 +1421,7 @@ function SkillSelector({ skills, input, selectedIndex }) {
1430
1421
  const query = match[1].toLowerCase().trim();
1431
1422
  if (skills.length === 0) return null;
1432
1423
  const matched = !query && input.startsWith("/") ? skills.slice(0, 3) : skills.filter((s) => s.name.toLowerCase().includes(query)).slice(0, 3);
1433
- if (query && matched.length > 0 && matched.some((s) => s.name.toLowerCase() === query)) return null;
1424
+ if (query && matched.some((s) => s.name.toLowerCase() === query)) return null;
1434
1425
  if (matched.length === 0) return null;
1435
1426
  return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginLeft: 4, marginTop: 1, children: [
1436
1427
  /* @__PURE__ */ jsx7(Text8, { color: "#808080", dimColor: true, children: "\u652F\u6301\u7684 Skill\uFF1A" }),
@@ -1508,6 +1499,7 @@ defaultRegistry.register("deepseek", (config) => {
1508
1499
  return new DeepSeekProvider({
1509
1500
  apiKey: config.apiKey,
1510
1501
  baseUrl: config.baseUrl,
1502
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1511
1503
  model: config.model
1512
1504
  });
1513
1505
  });
@@ -1540,7 +1532,7 @@ function eraseTool(tool) {
1540
1532
  return tool.execute(args, ctx);
1541
1533
  },
1542
1534
  initialTitle(args) {
1543
- return tool.initialTitle?.(args) ?? `${tool.name}`;
1535
+ return tool.initialTitle?.(args) ?? tool.name;
1544
1536
  }
1545
1537
  };
1546
1538
  }
@@ -1559,8 +1551,12 @@ import Handlebars from "handlebars";
1559
1551
  // src/agent/prompts/system-prompt.hbs
1560
1552
  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\uFF0C\u4E0D\u8981\u7528\u6807\u9898\u7B26\u53F7\r\n\r\n\u6B63\u786E\u793A\u4F8B\uFF1A\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';
1561
1553
 
1554
+ // src/agent/prompts/plan-prompt.hbs
1555
+ 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\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";
1556
+
1562
1557
  // src/agent/system-prompt.ts
1563
1558
  var compiledTemplate = Handlebars.compile(system_prompt_default);
1559
+ var compiledPlanTemplate = Handlebars.compile(plan_prompt_default);
1564
1560
  function formatTools(tools) {
1565
1561
  return tools.map((t) => {
1566
1562
  const params = t.parameters && Object.keys(t.parameters.properties ?? {}).join(", ");
@@ -1571,7 +1567,7 @@ function formatTools(tools) {
1571
1567
  };
1572
1568
  });
1573
1569
  }
1574
- function buildSystemPrompt(opts) {
1570
+ function buildTemplateVars(opts) {
1575
1571
  const now = /* @__PURE__ */ new Date();
1576
1572
  const date = now.toLocaleDateString("zh-CN", {
1577
1573
  year: "numeric",
@@ -1583,7 +1579,7 @@ function buildSystemPrompt(opts) {
1583
1579
  hour: "2-digit",
1584
1580
  minute: "2-digit"
1585
1581
  });
1586
- return compiledTemplate({
1582
+ return {
1587
1583
  model: opts.model,
1588
1584
  maxToolRounds: opts.maxToolRounds,
1589
1585
  cwd: opts.cwd,
@@ -1591,7 +1587,13 @@ function buildSystemPrompt(opts) {
1591
1587
  time,
1592
1588
  tools: opts.tools ? formatTools(opts.tools) : void 0,
1593
1589
  projectContext: opts.projectContext ?? void 0
1594
- });
1590
+ };
1591
+ }
1592
+ function buildSystemPrompt(opts) {
1593
+ return compiledTemplate(buildTemplateVars(opts));
1594
+ }
1595
+ function buildPlanSystemPrompt(opts) {
1596
+ return compiledPlanTemplate(buildTemplateVars(opts));
1595
1597
  }
1596
1598
 
1597
1599
  // src/tool/registry.ts
@@ -1770,6 +1772,8 @@ var Session = class {
1770
1772
  #abortController = new AbortController();
1771
1773
  // 风暴检测:记录每轮的工具调用错误
1772
1774
  #stormRecords = [];
1775
+ /** 当前会话模式:code(代码模式)或 plan(计划模式) */
1776
+ #mode = "code";
1773
1777
  constructor(provider, tools = [], costTracker, options) {
1774
1778
  this.#provider = provider;
1775
1779
  if (tools instanceof ToolRegistry) {
@@ -1808,6 +1812,15 @@ var Session = class {
1808
1812
  get toolRegistry() {
1809
1813
  return this.#toolRegistry;
1810
1814
  }
1815
+ /** 获取当前会话模式 */
1816
+ get mode() {
1817
+ return this.#mode;
1818
+ }
1819
+ /** 切换会话模式,返回新模式 */
1820
+ setMode(mode) {
1821
+ this.#mode = mode;
1822
+ return this.#mode;
1823
+ }
1811
1824
  // -------------------------------------------------------------------------
1812
1825
  // 流式对话 — Agent 主循环
1813
1826
  // -------------------------------------------------------------------------
@@ -1833,6 +1846,7 @@ var Session = class {
1833
1846
  const [trimmed] = trimMessages(
1834
1847
  [...this.#messages],
1835
1848
  {
1849
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1836
1850
  model: this.#provider.model(),
1837
1851
  reservedForOutput: this.#options.reservedForOutput,
1838
1852
  systemPrompt,
@@ -1852,7 +1866,7 @@ var Session = class {
1852
1866
  let accumulatedText = "";
1853
1867
  let lastUsage;
1854
1868
  let lastToolCalls;
1855
- let lastFinishReason = null;
1869
+ let _lastFinishReason = null;
1856
1870
  for await (const chunk of stream) {
1857
1871
  if (chunk.content) {
1858
1872
  accumulatedText += chunk.content;
@@ -1865,7 +1879,7 @@ var Session = class {
1865
1879
  lastUsage = chunk.usage;
1866
1880
  }
1867
1881
  if (chunk.finishReason) {
1868
- lastFinishReason = chunk.finishReason;
1882
+ _lastFinishReason = chunk.finishReason;
1869
1883
  }
1870
1884
  }
1871
1885
  if (lastUsage) {
@@ -2071,6 +2085,7 @@ ${item.result.diff.patch}`;
2071
2085
  const toolDescs = enabledTools.map((t) => ({
2072
2086
  name: t.name,
2073
2087
  description: t.description,
2088
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2074
2089
  parameters: t.parameters
2075
2090
  }));
2076
2091
  const opts = {
@@ -2080,15 +2095,23 @@ ${item.result.diff.patch}`;
2080
2095
  projectContext: this.#options.projectContext ?? void 0,
2081
2096
  cwd: this.#options.cwd
2082
2097
  };
2098
+ if (this.#mode === "plan") {
2099
+ return buildPlanSystemPrompt(opts);
2100
+ }
2083
2101
  return buildSystemPrompt(opts);
2084
2102
  }
2085
- /** 将注册的工具转为 ToolDefinition 格式(预留给 function calling) */
2103
+ /**
2104
+ * 将注册的工具转为 ToolDefinition 格式(预留给 function calling)。
2105
+ * 计划模式下只返回读工具,禁止非读工具暴露给 LLM。
2106
+ */
2086
2107
  #buildToolDefinitions() {
2087
- return this.#toolRegistry.list().map((t) => ({
2108
+ const tools = this.#mode === "plan" ? this.#toolRegistry.listReadTools() : this.#toolRegistry.list();
2109
+ return tools.map((t) => ({
2088
2110
  type: "function",
2089
2111
  function: {
2090
2112
  name: t.name,
2091
2113
  description: t.description,
2114
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2092
2115
  parameters: t.parameters
2093
2116
  }
2094
2117
  }));
@@ -2152,7 +2175,7 @@ function getDefaultTimeout() {
2152
2175
  return DEFAULT_TIMEOUT_MS;
2153
2176
  }
2154
2177
  async function execCommand(command, args, cwd, timeoutMs = DEFAULT_TIMEOUT_MS, signal, isShellCommand) {
2155
- return new Promise((resolve2) => {
2178
+ return new Promise((_resolve) => {
2156
2179
  let spawnCmd;
2157
2180
  let spawnArgs;
2158
2181
  let useShell;
@@ -2195,13 +2218,13 @@ async function execCommand(command, args, cwd, timeoutMs = DEFAULT_TIMEOUT_MS, s
2195
2218
  }
2196
2219
  child.on("close", (code) => {
2197
2220
  clearTimeout(timeout);
2198
- resolve2({ stdout, stderr, exitCode: code });
2221
+ _resolve({ stdout, stderr, exitCode: code });
2199
2222
  });
2200
2223
  child.on("error", (err) => {
2201
2224
  clearTimeout(timeout);
2202
2225
  stderr += `
2203
2226
  \u8FDB\u7A0B\u542F\u52A8\u5931\u8D25\uFF1A${err.message}`;
2204
- resolve2({ stdout, stderr, exitCode: null });
2227
+ _resolve({ stdout, stderr, exitCode: null });
2205
2228
  });
2206
2229
  });
2207
2230
  }
@@ -2244,42 +2267,41 @@ function computeLineDiff(oldLines, newLines) {
2244
2267
  const V = new Array(2 * maxD + 2).fill(-1);
2245
2268
  const offset = maxD;
2246
2269
  V[offset + 1] = 0;
2247
- outer:
2248
- for (let d = 0; d <= maxD; d++) {
2249
- const snapshot = /* @__PURE__ */ new Map();
2250
- const kStart = -d;
2251
- const kEnd = d;
2252
- for (let k = kStart; k <= kEnd; k += 2) {
2253
- const ki = k + offset;
2254
- let x;
2255
- if (k === -d || k !== d && V[ki - 1] < V[ki + 1]) {
2256
- x = V[ki + 1];
2257
- } else {
2258
- x = V[ki - 1] + 1;
2259
- }
2260
- let y = x - k;
2261
- while (x < N && y < M && oldLines[x] === newLines[y]) {
2262
- x++;
2263
- y++;
2264
- }
2265
- V[ki] = x;
2266
- snapshot.set(k, x);
2267
- if (x >= N && y >= M) {
2268
- trace.push(snapshot);
2269
- return backtrack(trace, N, M, oldLines, newLines, offset);
2270
- }
2270
+ for (let d = 0; d <= maxD; d++) {
2271
+ const snapshot = /* @__PURE__ */ new Map();
2272
+ const kStart = -d;
2273
+ const kEnd = d;
2274
+ for (let k = kStart; k <= kEnd; k += 2) {
2275
+ const ki = k + offset;
2276
+ let x;
2277
+ if (k === -d || k !== d && V[ki - 1] < V[ki + 1]) {
2278
+ x = V[ki + 1];
2279
+ } else {
2280
+ x = V[ki - 1] + 1;
2281
+ }
2282
+ let y = x - k;
2283
+ while (x < N && y < M && oldLines[x] === newLines[y]) {
2284
+ x++;
2285
+ y++;
2286
+ }
2287
+ V[ki] = x;
2288
+ snapshot.set(k, x);
2289
+ if (x >= N && y >= M) {
2290
+ trace.push(snapshot);
2291
+ return backtrack(trace, N, M, oldLines, newLines, offset);
2271
2292
  }
2272
- trace.push(snapshot);
2273
2293
  }
2294
+ trace.push(snapshot);
2295
+ }
2274
2296
  return oldLines.map((line) => ({ op: "remove", line })).concat(newLines.map((line) => ({ op: "add", line })));
2275
2297
  }
2276
- function backtrack(trace, N, M, oldLines, newLines, offset) {
2298
+ function backtrack(trace, N, M, oldLines, newLines, _offset) {
2277
2299
  const result = [];
2278
2300
  let x = N;
2279
2301
  let y = M;
2280
2302
  for (let d = trace.length - 1; d >= 1; d--) {
2281
2303
  const k = x - y;
2282
- const prevSnapshot = d > 0 ? trace[d - 1] : /* @__PURE__ */ new Map();
2304
+ const prevSnapshot = trace[d - 1];
2283
2305
  const prevKDown = prevSnapshot.get(k - 1);
2284
2306
  const prevKUp = prevSnapshot.get(k + 1);
2285
2307
  let prevK;
@@ -2668,7 +2690,7 @@ var writeFileTool = {
2668
2690
  } catch {
2669
2691
  }
2670
2692
  await mkdir4(dirname(filePath), { recursive: true });
2671
- const content = String(args.content);
2693
+ const content = args.content;
2672
2694
  await writeFileWithEol(filePath, oldContent, content);
2673
2695
  const diff = computeFileDiff(oldContent, content, filePath);
2674
2696
  diff.existedBefore = existedBefore;
@@ -3187,7 +3209,7 @@ var globTool = {
3187
3209
  // src/tool/builtins/grep.ts
3188
3210
  import { readdir as readdir3, readFile as readFile9, stat as stat3 } from "fs/promises";
3189
3211
  import { join as join5, relative as relative5, isAbsolute as isAbsolute3 } from "path";
3190
- async function collectFiles(dir, baseDir, extension, maxFiles = 200) {
3212
+ async function collectFiles(dir, extension, maxFiles = 200) {
3191
3213
  const results = [];
3192
3214
  let entries;
3193
3215
  try {
@@ -3202,7 +3224,7 @@ async function collectFiles(dir, baseDir, extension, maxFiles = 200) {
3202
3224
  }
3203
3225
  const fullPath = join5(dir, entry.name);
3204
3226
  if (entry.isDirectory()) {
3205
- results.push(...await collectFiles(fullPath, baseDir, extension, maxFiles - results.length));
3227
+ results.push(...await collectFiles(fullPath, extension, maxFiles - results.length));
3206
3228
  } else {
3207
3229
  if (extension && !entry.name.endsWith(`.${extension}`)) {
3208
3230
  continue;
@@ -3256,7 +3278,7 @@ var grepTool = {
3256
3278
  if (!dirStat.isDirectory()) {
3257
3279
  return { success: false, data: `\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${searchDir}`, error: "NOT_DIRECTORY" };
3258
3280
  }
3259
- const files = await collectFiles(searchDir, searchDir, args.include, maxFiles);
3281
+ const files = await collectFiles(searchDir, args.include, maxFiles);
3260
3282
  const matches = [];
3261
3283
  for (const filePath of files) {
3262
3284
  try {
@@ -3264,7 +3286,7 @@ var grepTool = {
3264
3286
  const lines = content.split("\n");
3265
3287
  const relPath = relative5(searchDir, filePath);
3266
3288
  for (let i = 0; i < lines.length; i++) {
3267
- if (regex.test(lines[i])) {
3289
+ if (regex.test(lines[i] ?? "")) {
3268
3290
  regex.lastIndex = 0;
3269
3291
  matches.push({
3270
3292
  file: relPath,
@@ -3333,7 +3355,7 @@ var lsTool = {
3333
3355
  try {
3334
3356
  const entries = await readdir4(dirPath, { withFileTypes: true });
3335
3357
  const lines = [];
3336
- const sorted = [...entries].sort((a, b) => {
3358
+ const sorted = [...entries].toSorted((a, b) => {
3337
3359
  if (a.isDirectory() !== b.isDirectory()) {
3338
3360
  return a.isDirectory() ? -1 : 1;
3339
3361
  }
@@ -3548,6 +3570,12 @@ function getGradientColors(text, phase, stops) {
3548
3570
 
3549
3571
  // src/ui/ChatSession.tsx
3550
3572
  import { Fragment, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
3573
+ var PHASE_CONFIG = {
3574
+ thinking: { icon: "\u{1F9E0}", label: "\u601D\u8003\u4E2D", color: "#ff9800" },
3575
+ generating: { icon: "\u2728", label: "\u751F\u6210\u4E2D", color: "#00ff41" },
3576
+ calling_tools: { icon: "\u{1F6E0}", label: "\u8C03\u7528\u5DE5\u5177", color: "#f59e0b" },
3577
+ executing_tools: { icon: "\u26A1", label: "\u6267\u884C\u5DE5\u5177", color: "#00ffff" }
3578
+ };
3551
3579
  var commandRegistry = /* @__PURE__ */ new Map();
3552
3580
  function registerCommand(name, cmd) {
3553
3581
  commandRegistry.set(name, cmd);
@@ -3575,6 +3603,8 @@ registerCommand("/thinking", { desc: "\u5207\u6362\u6DF1\u5EA6\u601D\u8003\u6A21
3575
3603
  registerCommand("/effort", { desc: "\u5207\u6362\u63A8\u7406\u7B49\u7EA7 High/Max", handler: () => ({ kind: "text", content: "\u8BF7\u76F4\u63A5\u8F93\u5165 /effort \u5207\u6362" }) });
3576
3604
  registerCommand("/game", { desc: "\u542F\u52A8\u6E38\u620F", handler: () => ({ kind: "navigate", target: "game" }) });
3577
3605
  registerCommand("/stock", { desc: "\u67E5\u770B\u80A1\u7968\u884C\u60C5", handler: () => ({ kind: "navigate", target: "stock" }) });
3606
+ 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" }) });
3607
+ 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" }) });
3578
3608
  var STREAMING_PLACEHOLDERS = [
3579
3609
  "\u8BA9\u5B50\u5F39\u98DE\u4E00\u4F1A\u513F...",
3580
3610
  "\u9A6C\u4E0A\u5C31\u597D...",
@@ -3617,6 +3647,7 @@ function ChatSession({
3617
3647
  const [balanceLoading, setBalanceLoading] = useState3(false);
3618
3648
  const [todayCost, setTodayCost] = useState3(null);
3619
3649
  const [isStreaming, setIsStreaming] = useState3(false);
3650
+ const [streamingPhase, setStreamingPhase] = useState3(null);
3620
3651
  const [streamingPlaceholder, setStreamingPlaceholder] = useState3("");
3621
3652
  const [idlePlaceholder, setIdlePlaceholder] = useState3(() => pickRandom(IDLE_PLACEHOLDERS));
3622
3653
  const [gradientColors, setGradientColors] = useState3([]);
@@ -3625,15 +3656,16 @@ function ChatSession({
3625
3656
  const streamingPhaseRef = useRef2(0);
3626
3657
  const [currentContent, setCurrentContent] = useState3("");
3627
3658
  const [currentToolCalls, setCurrentToolCalls] = useState3([]);
3628
- const [currentUsage, setCurrentUsage] = useState3(void 0);
3629
- const [currentElapsed, setCurrentElapsed] = useState3(void 0);
3630
- const [currentCost, setCurrentCost] = useState3(void 0);
3659
+ const [_currentUsage, setCurrentUsage] = useState3(void 0);
3660
+ const [_currentElapsed, setCurrentElapsed] = useState3(void 0);
3661
+ const [_currentCost, setCurrentCost] = useState3(void 0);
3631
3662
  const [activeModel, setActiveModel] = useState3(model);
3632
- const [streamingModel, setStreamingModel] = useState3(void 0);
3663
+ const [_streamingModel, setStreamingModel] = useState3(void 0);
3633
3664
  const [streamError, setStreamError] = useState3(void 0);
3665
+ const [sessionMode, setSessionMode] = useState3("code");
3634
3666
  const [thinkingEnabled, setThinkingEnabled] = useState3(true);
3635
3667
  const [thinkingEffort, setThinkingEffort] = useState3("high");
3636
- const [responseFormat, setResponseFormat] = useState3("text");
3668
+ const [responseFormat] = useState3("text");
3637
3669
  const [toolChoice, setToolChoice] = useState3(void 0);
3638
3670
  const sessionCost = useMemo(() => {
3639
3671
  return displayMessages.reduce((sum, msg) => {
@@ -3788,11 +3820,28 @@ function ChatSession({
3788
3820
  }
3789
3821
  return;
3790
3822
  }
3823
+ if (key.shift && key.tab) {
3824
+ if (!isStreaming) {
3825
+ const newMode = sessionMode === "plan" ? "code" : "plan";
3826
+ setSessionMode(newMode);
3827
+ sessionRef.current?.setMode(newMode);
3828
+ setToolChoice(void 0);
3829
+ sessionRef.current?.reset();
3830
+ setDisplayMessages((prev) => [
3831
+ ...prev,
3832
+ {
3833
+ role: "assistant",
3834
+ content: newMode === "plan" ? "\u{1F4CB} \u5DF2\u5207\u6362\u4E3A **\u8BA1\u5212\u6A21\u5F0F**\uFF08Shift+Tab\uFF09\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" : "\u{1F6E0} \u5DF2\u5207\u6362\u4E3A **\u4EE3\u7801\u6A21\u5F0F**\uFF08Shift+Tab\uFF09\n\n\u73B0\u5728\u53EF\u4EE5\u6B63\u5E38\u8BFB\u53D6\u548C\u4FEE\u6539\u4EE3\u7801\u4E86\u3002"
3835
+ }
3836
+ ]);
3837
+ }
3838
+ return;
3839
+ }
3791
3840
  if (!input && !isStreaming && _input) {
3792
3841
  setInput(_input);
3793
3842
  }
3794
3843
  },
3795
- [selectingModel, modelSelectIndex, modelOptions, activeModel, isStreaming, handleCtrlC, input, skills, skillSelectIndex, fileSelectIndex, getFilteredSkills, getFilteredFiles]
3844
+ [selectingModel, modelSelectIndex, modelOptions, activeModel, isStreaming, handleCtrlC, input, skills, skillSelectIndex, fileSelectIndex, getFilteredSkills, getFilteredFiles, sessionMode, setSessionMode]
3796
3845
  )
3797
3846
  );
3798
3847
  useEffect3(() => {
@@ -3846,7 +3895,7 @@ function ChatSession({
3846
3895
  if (!apiKey || !baseUrl) return;
3847
3896
  let cancelled = false;
3848
3897
  setBalanceLoading(true);
3849
- import("./deepseek-YTT76XDE.js").then(({ DeepSeekProvider: DeepSeekProvider2 }) => {
3898
+ import("./deepseek-MG4NT5YC.js").then(({ DeepSeekProvider: DeepSeekProvider2 }) => {
3850
3899
  const provider = new DeepSeekProvider2({
3851
3900
  apiKey,
3852
3901
  baseUrl,
@@ -3944,6 +3993,48 @@ function ChatSession({
3944
3993
  setInput("");
3945
3994
  return;
3946
3995
  }
3996
+ if (cmdLower === "/plan") {
3997
+ if (sessionMode === "plan") {
3998
+ setDisplayMessages((prev) => [
3999
+ ...prev,
4000
+ { role: "user", content: trimmed },
4001
+ { role: "assistant", content: "\u5DF2\u7ECF\u5728\u8BA1\u5212\u6A21\u5F0F\u4E2D\u3002\u8F93\u5165 /code \u5207\u56DE\u4EE3\u7801\u6A21\u5F0F\u3002" }
4002
+ ]);
4003
+ } else {
4004
+ setSessionMode("plan");
4005
+ sessionRef.current?.setMode("plan");
4006
+ setToolChoice(void 0);
4007
+ setDisplayMessages((prev) => [
4008
+ ...prev,
4009
+ { role: "user", content: trimmed },
4010
+ { 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" }
4011
+ ]);
4012
+ sessionRef.current?.reset();
4013
+ }
4014
+ setInput("");
4015
+ return;
4016
+ }
4017
+ if (cmdLower === "/code") {
4018
+ if (sessionMode === "code") {
4019
+ setDisplayMessages((prev) => [
4020
+ ...prev,
4021
+ { role: "user", content: trimmed },
4022
+ { role: "assistant", content: "\u5DF2\u7ECF\u5728\u4EE3\u7801\u6A21\u5F0F\u4E2D\u3002\u8F93\u5165 /plan \u5207\u56DE\u8BA1\u5212\u6A21\u5F0F\u3002" }
4023
+ ]);
4024
+ } else {
4025
+ setSessionMode("code");
4026
+ sessionRef.current?.setMode("code");
4027
+ setToolChoice(void 0);
4028
+ setDisplayMessages((prev) => [
4029
+ ...prev,
4030
+ { role: "user", content: trimmed },
4031
+ { 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" }
4032
+ ]);
4033
+ sessionRef.current?.reset();
4034
+ }
4035
+ setInput("");
4036
+ return;
4037
+ }
3947
4038
  if (cmdLower === "/tools") {
3948
4039
  const next = toolChoice === void 0 ? "none" : toolChoice === "none" ? "required" : toolChoice === "required" ? "auto" : void 0;
3949
4040
  setToolChoice(next);
@@ -4012,6 +4103,7 @@ function ChatSession({
4012
4103
  ]);
4013
4104
  setInput("");
4014
4105
  setIsStreaming(true);
4106
+ setStreamingPhase("thinking");
4015
4107
  setStreamingPlaceholder(pickRandom(STREAMING_PLACEHOLDERS));
4016
4108
  setCurrentContent("");
4017
4109
  setCurrentToolCalls([]);
@@ -4040,6 +4132,7 @@ function ChatSession({
4040
4132
  if (abortController.signal.aborted) break;
4041
4133
  switch (event.type) {
4042
4134
  case "text_delta":
4135
+ setStreamingPhase("generating");
4043
4136
  setCurrentContent((prev) => {
4044
4137
  const next = prev + event.content;
4045
4138
  currentContentRef.current = next;
@@ -4047,6 +4140,7 @@ function ChatSession({
4047
4140
  });
4048
4141
  break;
4049
4142
  case "tool_calls":
4143
+ setStreamingPhase("calling_tools");
4050
4144
  setCurrentToolCalls((prev) => {
4051
4145
  const next = [...prev, ...event.calls];
4052
4146
  currentToolCallsRef.current = next;
@@ -4054,6 +4148,8 @@ function ChatSession({
4054
4148
  });
4055
4149
  break;
4056
4150
  case "tool_result":
4151
+ setStreamingPhase("executing_tools");
4152
+ setTimeout(() => setStreamingPhase("thinking"), 300);
4057
4153
  setCurrentContent("");
4058
4154
  currentContentRef.current = "";
4059
4155
  setCurrentToolCalls([]);
@@ -4096,6 +4192,7 @@ function ChatSession({
4096
4192
  streamErrorRef.current = msg;
4097
4193
  } finally {
4098
4194
  setIsStreaming(false);
4195
+ setStreamingPhase(null);
4099
4196
  setIdlePlaceholder(pickRandom(IDLE_PLACEHOLDERS));
4100
4197
  abortRef.current = null;
4101
4198
  const finContent = currentContentRef.current;
@@ -4120,7 +4217,7 @@ function ChatSession({
4120
4217
  ]);
4121
4218
  }
4122
4219
  }
4123
- }, [onLaunchGame, onLaunchStock, currentContent, currentToolCalls, skills, skillSelectIndex, getFilteredSkills, thinkingEnabled, thinkingEffort, responseFormat, toolChoice, activeModel]);
4220
+ }, [onLaunchGame, onLaunchStock, currentContent, currentToolCalls, skills, skillSelectIndex, getFilteredSkills, thinkingEnabled, thinkingEffort, responseFormat, toolChoice, activeModel, sessionMode]);
4124
4221
  useEffect3(() => {
4125
4222
  if (!isStreaming && externalCostTracker) {
4126
4223
  setTodayCost(externalCostTracker.todayTotalCost);
@@ -4153,6 +4250,7 @@ function ChatSession({
4153
4250
  " \u{1F9E0} \u6DF1\u5EA6\u601D\u8003 ",
4154
4251
  thinkingEffort === "max" ? "Max" : "High"
4155
4252
  ] }),
4253
+ sessionMode === "plan" && /* @__PURE__ */ jsx9(Text10, { color: "#ff69b4", bold: true, children: " \u{1F4CB} \u8BA1\u5212\u6A21\u5F0F" }),
4156
4254
  responseFormat === "json_object" && /* @__PURE__ */ jsx9(Text10, { color: "#4caf50", children: " \u{1F4C4} JSON" }),
4157
4255
  toolChoice !== void 0 && /* @__PURE__ */ jsxs8(Text10, { color: "#e91e63", children: [
4158
4256
  " \u{1F6E0} ",
@@ -4226,7 +4324,6 @@ function ChatSession({
4226
4324
  isStreaming: true
4227
4325
  }
4228
4326
  ),
4229
- isStreaming && !currentContent && currentToolCalls.length === 0 && /* @__PURE__ */ jsx9(Box8, { marginTop: 1, marginLeft: 4, children: /* @__PURE__ */ jsx9(Spinner, { type: "dots", label: "\u601D\u8003\u4E2D..." }) }),
4230
4327
  !isStreaming && streamError && /* @__PURE__ */ jsx9(Box8, { marginTop: 1, marginLeft: 3, children: /* @__PURE__ */ jsxs8(Text10, { color: "red", children: [
4231
4328
  "\u26A0 ",
4232
4329
  streamError
@@ -4265,8 +4362,15 @@ function ChatSession({
4265
4362
  ] }),
4266
4363
  /* @__PURE__ */ jsx9(Text10, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) })
4267
4364
  ] }) : /* @__PURE__ */ jsxs8(Fragment, { children: [
4268
- /* @__PURE__ */ jsx9(Box8, { marginTop: 1, children: hasConversationStarted && (balance !== null || sessionCost > 0 || isStreaming) ? /* @__PURE__ */ jsxs8(Text10, { color: "#00ffff", dimColor: true, children: [
4269
- "\u2500".repeat(Math.max(dividerWidth - 35, 10)),
4365
+ (hasConversationStarted || sessionMode === "plan") && isStreaming && streamingPhase ? /* @__PURE__ */ jsx9(Box8, { marginTop: 1, justifyContent: "center", children: /* @__PURE__ */ jsxs8(Text10, { bold: true, color: PHASE_CONFIG[streamingPhase].color, children: [
4366
+ PHASE_CONFIG[streamingPhase].icon,
4367
+ " ",
4368
+ PHASE_CONFIG[streamingPhase].label,
4369
+ " ",
4370
+ /* @__PURE__ */ jsx9(InkSpinner2, { type: "dots" })
4371
+ ] }) }) : null,
4372
+ /* @__PURE__ */ jsx9(Box8, { marginTop: 1, children: hasConversationStarted || sessionMode === "plan" ? /* @__PURE__ */ jsxs8(Text10, { color: sessionMode === "plan" ? "#ff69b4" : "#00ffff", dimColor: sessionMode !== "plan", children: [
4373
+ /* @__PURE__ */ jsx9(Text10, { children: sessionMode === "plan" ? "\u2500".repeat(Math.max(dividerWidth - 48, 1)) : "\u2500".repeat(Math.max(dividerWidth - 35, 10)) }),
4270
4374
  balance !== null && /* @__PURE__ */ jsxs8(Text10, { color: "yellow", children: [
4271
4375
  " \u{1F4B0} \u4F59\u989D \xA5",
4272
4376
  balance.toFixed(2)
@@ -4278,7 +4382,8 @@ function ChatSession({
4278
4382
  ] }) : sessionCost > 0 ? /* @__PURE__ */ jsxs8(Text10, { color: "cyan", children: [
4279
4383
  " \u{1F4CA} \u672C\u6B21 \xA5",
4280
4384
  sessionCost.toFixed(4)
4281
- ] }) : null
4385
+ ] }) : null,
4386
+ sessionMode === "plan" && /* @__PURE__ */ jsx9(Text10, { color: "#ff69b4", bold: true, children: " \u{1F4CB} \u8BA1\u5212\u6A21\u5F0F" })
4282
4387
  ] }) : /* @__PURE__ */ jsx9(Text10, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) }) }),
4283
4388
  /* @__PURE__ */ jsxs8(Box8, { children: [
4284
4389
  /* @__PURE__ */ jsx9(Box8, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx9(Text10, { bold: true, color: "#00ff41", children: "\u26A1" }) }),
@@ -4407,7 +4512,7 @@ function createBricks(level) {
4407
4512
  function createInitialState(level) {
4408
4513
  const def = getLevel(level);
4409
4514
  const totalW = def.cols * def.bw + (def.cols - 1) * 2;
4410
- const startX = Math.floor((GAME_WIDTH - totalW) / 2);
4515
+ const _startX = Math.floor((GAME_WIDTH - totalW) / 2);
4411
4516
  return {
4412
4517
  level,
4413
4518
  bricks: createBricks(level),
@@ -5534,7 +5639,7 @@ import chalk5 from "chalk";
5534
5639
 
5535
5640
  // src/stock/StockList.tsx
5536
5641
  import { Box as Box12, Text as Text14, useInput as useInput5 } from "ink";
5537
- import { useState as useState7, useCallback as useCallback6, useEffect as useEffect6 } from "react";
5642
+ import { useState as useState7, useCallback as useCallback6, useEffect as useEffect6, useMemo as useMemo2 } from "react";
5538
5643
  import asciichart from "asciichart";
5539
5644
  import os from "os";
5540
5645
  import { join as join7 } from "path";
@@ -5654,6 +5759,13 @@ function StockList({ codes, onExit, onBackToChat }) {
5654
5759
  () => (/* @__PURE__ */ new Date()).toLocaleTimeString("zh-CN", { hour12: false })
5655
5760
  );
5656
5761
  const [dimMode, setDimMode] = useState7(false);
5762
+ const [sortOrder, setSortOrder] = useState7("default");
5763
+ const sortedStocks = useMemo2(() => {
5764
+ if (sortOrder === "default") return stocks;
5765
+ return [...stocks].toSorted(
5766
+ (a, b) => sortOrder === "desc" ? b.changePercent - a.changePercent : a.changePercent - b.changePercent
5767
+ );
5768
+ }, [stocks, sortOrder]);
5657
5769
  const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(onExit);
5658
5770
  useEffect6(() => {
5659
5771
  const timer = setInterval(() => {
@@ -5672,13 +5784,13 @@ function StockList({ codes, onExit, onBackToChat }) {
5672
5784
  setLoading(false);
5673
5785
  }, [codes]);
5674
5786
  useEffect6(() => {
5675
- loadData();
5787
+ void loadData();
5676
5788
  }, [loadData]);
5677
5789
  useEffect6(() => {
5678
5790
  const interval = setInterval(() => {
5679
5791
  setCountdown((prev) => {
5680
5792
  if (prev <= 1) {
5681
- loadData();
5793
+ void loadData();
5682
5794
  return 5;
5683
5795
  }
5684
5796
  return prev - 1;
@@ -5687,24 +5799,23 @@ function StockList({ codes, onExit, onBackToChat }) {
5687
5799
  return () => clearInterval(interval);
5688
5800
  }, [loadData]);
5689
5801
  useEffect6(() => {
5690
- if (!detailView) {
5691
- setDetailPrices(null);
5692
- setDetailLoading(false);
5693
- return;
5802
+ if (detailView) {
5803
+ const loadDetail = () => {
5804
+ minuteCache.delete(detailView.code);
5805
+ void fetchStockMinute(detailView.code).then((data) => {
5806
+ if (data && data.prices.length > 0) {
5807
+ cacheMinute(detailView.code, data.prices);
5808
+ setDetailPrices(data.prices);
5809
+ }
5810
+ });
5811
+ };
5812
+ loadDetail();
5813
+ setDetailCountdown(10);
5814
+ const timer = setInterval(loadDetail, 1e4);
5815
+ return () => clearInterval(timer);
5694
5816
  }
5695
- const loadDetail = () => {
5696
- minuteCache.delete(detailView.code);
5697
- fetchStockMinute(detailView.code).then((data) => {
5698
- if (data && data.prices.length > 0) {
5699
- cacheMinute(detailView.code, data.prices);
5700
- setDetailPrices(data.prices);
5701
- }
5702
- });
5703
- };
5704
- loadDetail();
5705
- setDetailCountdown(10);
5706
- const timer = setInterval(loadDetail, 1e4);
5707
- return () => clearInterval(timer);
5817
+ setDetailPrices(null);
5818
+ setDetailLoading(false);
5708
5819
  }, [detailView]);
5709
5820
  useEffect6(() => {
5710
5821
  if (!detailView) return;
@@ -5739,7 +5850,9 @@ function StockList({ codes, onExit, onBackToChat }) {
5739
5850
  else onExit();
5740
5851
  } else if (input === "r") {
5741
5852
  setCountdown(5);
5742
- loadData();
5853
+ void loadData();
5854
+ } else if (input === "o") {
5855
+ setSortOrder((prev) => prev === "default" ? "desc" : prev === "desc" ? "asc" : "default");
5743
5856
  } else if (input === "h") {
5744
5857
  setDimMode((v) => !v);
5745
5858
  }
@@ -5768,14 +5881,17 @@ function StockList({ codes, onExit, onBackToChat }) {
5768
5881
  /* @__PURE__ */ jsx13(Box12, { width: 9, children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: "\u4EE3\u7801" }) }),
5769
5882
  /* @__PURE__ */ jsx13(Box12, { width: 16, children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: "\u540D\u79F0" }) }),
5770
5883
  /* @__PURE__ */ jsx13(Box12, { width: 12, children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: "\u6700\u65B0\u4EF7" }) }),
5771
- /* @__PURE__ */ jsx13(Box12, { width: 12, children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: "\u6DA8\u8DCC\u5E45" }) }),
5884
+ /* @__PURE__ */ jsx13(Box12, { width: 12, children: /* @__PURE__ */ jsxs12(Text14, { dimColor: true, children: [
5885
+ "\u6DA8\u8DCC\u5E45",
5886
+ sortOrder === "desc" ? " \u25BC" : sortOrder === "asc" ? " \u25B2" : ""
5887
+ ] }) }),
5772
5888
  /* @__PURE__ */ jsx13(Box12, { width: 12, children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: "\u6DA8\u8DCC\u989D" }) }),
5773
5889
  /* @__PURE__ */ jsx13(Box12, { width: 12, children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: "\u6700\u9AD8" }) }),
5774
5890
  /* @__PURE__ */ jsx13(Box12, { width: 12, children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: "\u6700\u4F4E" }) }),
5775
5891
  /* @__PURE__ */ jsx13(Box12, { children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: "\u6210\u4EA4\u989D" }) })
5776
5892
  ] }),
5777
5893
  /* @__PURE__ */ jsx13(Box12, { children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: " " + "\u2500".repeat(100) }) }),
5778
- /* @__PURE__ */ jsx13(Box12, { flexDirection: "column", children: stocks.map((stock, index) => {
5894
+ /* @__PURE__ */ jsx13(Box12, { flexDirection: "column", children: sortedStocks.map((stock, index) => {
5779
5895
  const isSelected = index === selectedIndex;
5780
5896
  const isUp = stock.changePercent >= 0;
5781
5897
  const color = isUp ? "#ff1493" : "#00ff41";
@@ -5798,7 +5914,7 @@ function StockList({ codes, onExit, onBackToChat }) {
5798
5914
  /* @__PURE__ */ jsx13(Box12, { children: /* @__PURE__ */ jsx13(Text14, { ...cp2("#888888"), children: formatAmount(stock.amount) }) })
5799
5915
  ] }, stock.code);
5800
5916
  }) }),
5801
- /* @__PURE__ */ jsx13(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: ` \u2191/\u2193 \u9009\u62E9 Enter \u8BE6\u60C5 r \u624B\u52A8\u5237\u65B0 h \u7F6E\u7070/\u6062\u590D q \u8FD4\u56DE` }) }),
5917
+ /* @__PURE__ */ jsx13(Box12, { 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` }) }),
5802
5918
  /* @__PURE__ */ jsxs12(Box12, { children: [
5803
5919
  /* @__PURE__ */ jsx13(Text14, { dimColor: true, children: ` \u6700\u540E\u66F4\u65B0: ${lastUpdate} \u7F16\u8F91\u81EA\u9009\u80A1: ` }),
5804
5920
  /* @__PURE__ */ jsx13(Text14, { ...cp2("#c792ea"), children: osc8Link(toFileUrl(SETTINGS_PATH), SETTINGS_PATH) })
@@ -5973,81 +6089,8 @@ function createCli() {
5973
6089
  budgetLimit: ctx?.config.budgetLimit ?? 0,
5974
6090
  tokenBudgetLimit: ctx?.config.tokenBudgetLimit ?? 0
5975
6091
  });
5976
- startChat(ctx, costTracker);
6092
+ void startChat(ctx, costTracker);
5977
6093
  });
5978
- async function startChat(ctx, costTracker) {
5979
- const [globalSkillCount, localSkillCount, skills, files] = await Promise.all([
5980
- countDskcodeSkills(),
5981
- countProjectLocalSkills(process.cwd()),
5982
- getAllSkills(process.cwd()),
5983
- scanProjectFiles(process.cwd())
5984
- ]);
5985
- const skillCount = globalSkillCount + localSkillCount;
5986
- const defaultProvider = ctx?.config.providers.find(
5987
- (p) => p.name === (ctx?.config.defaultProvider ?? "deepseek")
5988
- );
5989
- const model = defaultProvider?.model ?? "deepseek-v4-flash";
5990
- const chatApp = renderApp(
5991
- /* @__PURE__ */ jsx14(
5992
- ChatSession,
5993
- {
5994
- skillCount,
5995
- skills,
5996
- files,
5997
- toolCount: ctx?.config.tools.length ?? 0,
5998
- verbose: ctx?.verbose ?? false,
5999
- apiKey: defaultProvider?.apiKey,
6000
- baseUrl: defaultProvider?.baseUrl ?? "https://api.deepseek.com",
6001
- costTracker,
6002
- model,
6003
- onLaunchGame: () => {
6004
- chatApp.unmount();
6005
- setImmediate(() => {
6006
- initGames();
6007
- const games = listGames();
6008
- const { unmount } = render4(
6009
- /* @__PURE__ */ jsx14(
6010
- GamePicker,
6011
- {
6012
- games,
6013
- onSelect: async (game) => {
6014
- unmount();
6015
- await game.play();
6016
- startChat(ctx, costTracker);
6017
- },
6018
- onBackToChat: () => {
6019
- unmount();
6020
- setImmediate(() => startChat(ctx, costTracker));
6021
- }
6022
- }
6023
- ),
6024
- { exitOnCtrlC: false }
6025
- );
6026
- });
6027
- },
6028
- onLaunchStock: () => {
6029
- chatApp.unmount();
6030
- setImmediate(() => {
6031
- const defaultStockCodes = ctx?.config.stock?.symbols?.map((s) => s.code) ?? ["sh000001", "sz399006", "sh601688"];
6032
- const stockApp = renderApp(
6033
- /* @__PURE__ */ jsx14(
6034
- StockList,
6035
- {
6036
- codes: defaultStockCodes,
6037
- onBackToChat: () => {
6038
- stockApp.unmount();
6039
- setImmediate(() => startChat(ctx, costTracker));
6040
- },
6041
- onExit: () => process.exit(0)
6042
- }
6043
- )
6044
- );
6045
- });
6046
- }
6047
- }
6048
- )
6049
- );
6050
- }
6051
6094
  program2.command("run").description("\u6267\u884C\u4E00\u6B21\u6027\u4EFB\u52A1").argument("[prompt...]", "\u4EFB\u52A1\u63CF\u8FF0").option("--model <name>", "\u6307\u5B9A\u4F7F\u7528\u7684\u6A21\u578B").action(async function(_prompt) {
6052
6095
  console.log("dskcode run \u2014 \u5F85\u5B9E\u73B0\uFF08\u7B2C07\u7AE0\uFF09");
6053
6096
  });
@@ -6092,7 +6135,7 @@ compdef _dskcode_completion dskcode`);
6092
6135
  }
6093
6136
  });
6094
6137
  program2.command("stock").description("\u67E5\u770B\u81EA\u9009\u80A1\u5B9E\u65F6\u884C\u60C5").argument("[codes...]", "\u80A1\u7968\u4EE3\u7801\uFF08\u7A7A\u683C\u5206\u9694\uFF09\uFF0C\u5982 513090 600519").action(async function(codes) {
6095
- const ctx = this.dskcodeCtx;
6138
+ const _ctx = this.dskcodeCtx;
6096
6139
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
6097
6140
  const globalConfigPath = join9(home, ".dskcode", "settings.json");
6098
6141
  let globalConfigHasStock = false;
@@ -6173,6 +6216,79 @@ compdef _dskcode_completion dskcode`);
6173
6216
  });
6174
6217
  return program2;
6175
6218
  }
6219
+ async function startChat(ctx, costTracker) {
6220
+ const [globalSkillCount, localSkillCount, skills, files] = await Promise.all([
6221
+ countDskcodeSkills(),
6222
+ countProjectLocalSkills(process.cwd()),
6223
+ getAllSkills(process.cwd()),
6224
+ scanProjectFiles(process.cwd())
6225
+ ]);
6226
+ const skillCount = globalSkillCount + localSkillCount;
6227
+ const defaultProvider = ctx?.config.providers.find(
6228
+ (p) => p.name === (ctx?.config.defaultProvider ?? "deepseek")
6229
+ );
6230
+ const model = defaultProvider?.model ?? "deepseek-v4-flash";
6231
+ const chatApp = renderApp(
6232
+ /* @__PURE__ */ jsx14(
6233
+ ChatSession,
6234
+ {
6235
+ skillCount,
6236
+ skills,
6237
+ files,
6238
+ toolCount: ctx?.config.tools.length ?? 0,
6239
+ verbose: ctx?.verbose ?? false,
6240
+ apiKey: defaultProvider?.apiKey,
6241
+ baseUrl: defaultProvider?.baseUrl ?? "https://api.deepseek.com",
6242
+ costTracker,
6243
+ model,
6244
+ onLaunchGame: () => {
6245
+ chatApp.unmount();
6246
+ setImmediate(() => {
6247
+ initGames();
6248
+ const games = listGames();
6249
+ const { unmount } = render4(
6250
+ /* @__PURE__ */ jsx14(
6251
+ GamePicker,
6252
+ {
6253
+ games,
6254
+ onSelect: async (game) => {
6255
+ unmount();
6256
+ await game.play();
6257
+ void startChat(ctx, costTracker);
6258
+ },
6259
+ onBackToChat: () => {
6260
+ unmount();
6261
+ setImmediate(() => startChat(ctx, costTracker));
6262
+ }
6263
+ }
6264
+ ),
6265
+ { exitOnCtrlC: false }
6266
+ );
6267
+ });
6268
+ },
6269
+ onLaunchStock: () => {
6270
+ chatApp.unmount();
6271
+ setImmediate(() => {
6272
+ const defaultStockCodes = ctx?.config.stock?.symbols?.map((s) => s.code) ?? ["sh000001", "sz399006", "sh601688"];
6273
+ const stockApp = renderApp(
6274
+ /* @__PURE__ */ jsx14(
6275
+ StockList,
6276
+ {
6277
+ codes: defaultStockCodes,
6278
+ onBackToChat: () => {
6279
+ stockApp.unmount();
6280
+ setImmediate(() => startChat(ctx, costTracker));
6281
+ },
6282
+ onExit: () => process.exit(0)
6283
+ }
6284
+ )
6285
+ );
6286
+ });
6287
+ }
6288
+ }
6289
+ )
6290
+ );
6291
+ }
6176
6292
 
6177
6293
  // src/cli/exit-codes.ts
6178
6294
  var ExitCode = {