dskcode 0.1.23 → 0.1.24

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
@@ -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";
@@ -1559,8 +1550,12 @@ import Handlebars from "handlebars";
1559
1550
  // src/agent/prompts/system-prompt.hbs
1560
1551
  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
1552
 
1553
+ // src/agent/prompts/plan-prompt.hbs
1554
+ 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";
1555
+
1562
1556
  // src/agent/system-prompt.ts
1563
1557
  var compiledTemplate = Handlebars.compile(system_prompt_default);
1558
+ var compiledPlanTemplate = Handlebars.compile(plan_prompt_default);
1564
1559
  function formatTools(tools) {
1565
1560
  return tools.map((t) => {
1566
1561
  const params = t.parameters && Object.keys(t.parameters.properties ?? {}).join(", ");
@@ -1571,7 +1566,7 @@ function formatTools(tools) {
1571
1566
  };
1572
1567
  });
1573
1568
  }
1574
- function buildSystemPrompt(opts) {
1569
+ function buildTemplateVars(opts) {
1575
1570
  const now = /* @__PURE__ */ new Date();
1576
1571
  const date = now.toLocaleDateString("zh-CN", {
1577
1572
  year: "numeric",
@@ -1583,7 +1578,7 @@ function buildSystemPrompt(opts) {
1583
1578
  hour: "2-digit",
1584
1579
  minute: "2-digit"
1585
1580
  });
1586
- return compiledTemplate({
1581
+ return {
1587
1582
  model: opts.model,
1588
1583
  maxToolRounds: opts.maxToolRounds,
1589
1584
  cwd: opts.cwd,
@@ -1591,7 +1586,13 @@ function buildSystemPrompt(opts) {
1591
1586
  time,
1592
1587
  tools: opts.tools ? formatTools(opts.tools) : void 0,
1593
1588
  projectContext: opts.projectContext ?? void 0
1594
- });
1589
+ };
1590
+ }
1591
+ function buildSystemPrompt(opts) {
1592
+ return compiledTemplate(buildTemplateVars(opts));
1593
+ }
1594
+ function buildPlanSystemPrompt(opts) {
1595
+ return compiledPlanTemplate(buildTemplateVars(opts));
1595
1596
  }
1596
1597
 
1597
1598
  // src/tool/registry.ts
@@ -1770,6 +1771,8 @@ var Session = class {
1770
1771
  #abortController = new AbortController();
1771
1772
  // 风暴检测:记录每轮的工具调用错误
1772
1773
  #stormRecords = [];
1774
+ /** 当前会话模式:code(代码模式)或 plan(计划模式) */
1775
+ #mode = "code";
1773
1776
  constructor(provider, tools = [], costTracker, options) {
1774
1777
  this.#provider = provider;
1775
1778
  if (tools instanceof ToolRegistry) {
@@ -1808,6 +1811,15 @@ var Session = class {
1808
1811
  get toolRegistry() {
1809
1812
  return this.#toolRegistry;
1810
1813
  }
1814
+ /** 获取当前会话模式 */
1815
+ get mode() {
1816
+ return this.#mode;
1817
+ }
1818
+ /** 切换会话模式,返回新模式 */
1819
+ setMode(mode) {
1820
+ this.#mode = mode;
1821
+ return this.#mode;
1822
+ }
1811
1823
  // -------------------------------------------------------------------------
1812
1824
  // 流式对话 — Agent 主循环
1813
1825
  // -------------------------------------------------------------------------
@@ -2080,11 +2092,18 @@ ${item.result.diff.patch}`;
2080
2092
  projectContext: this.#options.projectContext ?? void 0,
2081
2093
  cwd: this.#options.cwd
2082
2094
  };
2095
+ if (this.#mode === "plan") {
2096
+ return buildPlanSystemPrompt(opts);
2097
+ }
2083
2098
  return buildSystemPrompt(opts);
2084
2099
  }
2085
- /** 将注册的工具转为 ToolDefinition 格式(预留给 function calling) */
2100
+ /**
2101
+ * 将注册的工具转为 ToolDefinition 格式(预留给 function calling)。
2102
+ * 计划模式下只返回读工具,禁止非读工具暴露给 LLM。
2103
+ */
2086
2104
  #buildToolDefinitions() {
2087
- return this.#toolRegistry.list().map((t) => ({
2105
+ const tools = this.#mode === "plan" ? this.#toolRegistry.listReadTools() : this.#toolRegistry.list();
2106
+ return tools.map((t) => ({
2088
2107
  type: "function",
2089
2108
  function: {
2090
2109
  name: t.name,
@@ -3548,6 +3567,12 @@ function getGradientColors(text, phase, stops) {
3548
3567
 
3549
3568
  // src/ui/ChatSession.tsx
3550
3569
  import { Fragment, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
3570
+ var PHASE_CONFIG = {
3571
+ thinking: { icon: "\u{1F9E0}", label: "\u601D\u8003\u4E2D", color: "#ff9800" },
3572
+ generating: { icon: "\u2728", label: "\u751F\u6210\u4E2D", color: "#00ff41" },
3573
+ calling_tools: { icon: "\u{1F6E0}", label: "\u8C03\u7528\u5DE5\u5177", color: "#f59e0b" },
3574
+ executing_tools: { icon: "\u26A1", label: "\u6267\u884C\u5DE5\u5177", color: "#00ffff" }
3575
+ };
3551
3576
  var commandRegistry = /* @__PURE__ */ new Map();
3552
3577
  function registerCommand(name, cmd) {
3553
3578
  commandRegistry.set(name, cmd);
@@ -3575,6 +3600,8 @@ registerCommand("/thinking", { desc: "\u5207\u6362\u6DF1\u5EA6\u601D\u8003\u6A21
3575
3600
  registerCommand("/effort", { desc: "\u5207\u6362\u63A8\u7406\u7B49\u7EA7 High/Max", handler: () => ({ kind: "text", content: "\u8BF7\u76F4\u63A5\u8F93\u5165 /effort \u5207\u6362" }) });
3576
3601
  registerCommand("/game", { desc: "\u542F\u52A8\u6E38\u620F", handler: () => ({ kind: "navigate", target: "game" }) });
3577
3602
  registerCommand("/stock", { desc: "\u67E5\u770B\u80A1\u7968\u884C\u60C5", handler: () => ({ kind: "navigate", target: "stock" }) });
3603
+ 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" }) });
3604
+ 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
3605
  var STREAMING_PLACEHOLDERS = [
3579
3606
  "\u8BA9\u5B50\u5F39\u98DE\u4E00\u4F1A\u513F...",
3580
3607
  "\u9A6C\u4E0A\u5C31\u597D...",
@@ -3617,6 +3644,7 @@ function ChatSession({
3617
3644
  const [balanceLoading, setBalanceLoading] = useState3(false);
3618
3645
  const [todayCost, setTodayCost] = useState3(null);
3619
3646
  const [isStreaming, setIsStreaming] = useState3(false);
3647
+ const [streamingPhase, setStreamingPhase] = useState3(null);
3620
3648
  const [streamingPlaceholder, setStreamingPlaceholder] = useState3("");
3621
3649
  const [idlePlaceholder, setIdlePlaceholder] = useState3(() => pickRandom(IDLE_PLACEHOLDERS));
3622
3650
  const [gradientColors, setGradientColors] = useState3([]);
@@ -3631,6 +3659,7 @@ function ChatSession({
3631
3659
  const [activeModel, setActiveModel] = useState3(model);
3632
3660
  const [streamingModel, setStreamingModel] = useState3(void 0);
3633
3661
  const [streamError, setStreamError] = useState3(void 0);
3662
+ const [sessionMode, setSessionMode] = useState3("code");
3634
3663
  const [thinkingEnabled, setThinkingEnabled] = useState3(true);
3635
3664
  const [thinkingEffort, setThinkingEffort] = useState3("high");
3636
3665
  const [responseFormat, setResponseFormat] = useState3("text");
@@ -3788,11 +3817,28 @@ function ChatSession({
3788
3817
  }
3789
3818
  return;
3790
3819
  }
3820
+ if (key.shift && key.tab) {
3821
+ if (!isStreaming) {
3822
+ const newMode = sessionMode === "plan" ? "code" : "plan";
3823
+ setSessionMode(newMode);
3824
+ sessionRef.current?.setMode(newMode);
3825
+ setToolChoice(void 0);
3826
+ sessionRef.current?.reset();
3827
+ setDisplayMessages((prev) => [
3828
+ ...prev,
3829
+ {
3830
+ role: "assistant",
3831
+ 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"
3832
+ }
3833
+ ]);
3834
+ }
3835
+ return;
3836
+ }
3791
3837
  if (!input && !isStreaming && _input) {
3792
3838
  setInput(_input);
3793
3839
  }
3794
3840
  },
3795
- [selectingModel, modelSelectIndex, modelOptions, activeModel, isStreaming, handleCtrlC, input, skills, skillSelectIndex, fileSelectIndex, getFilteredSkills, getFilteredFiles]
3841
+ [selectingModel, modelSelectIndex, modelOptions, activeModel, isStreaming, handleCtrlC, input, skills, skillSelectIndex, fileSelectIndex, getFilteredSkills, getFilteredFiles, sessionMode, setSessionMode]
3796
3842
  )
3797
3843
  );
3798
3844
  useEffect3(() => {
@@ -3944,6 +3990,48 @@ function ChatSession({
3944
3990
  setInput("");
3945
3991
  return;
3946
3992
  }
3993
+ if (cmdLower === "/plan") {
3994
+ if (sessionMode === "plan") {
3995
+ setDisplayMessages((prev) => [
3996
+ ...prev,
3997
+ { role: "user", content: trimmed },
3998
+ { role: "assistant", content: "\u5DF2\u7ECF\u5728\u8BA1\u5212\u6A21\u5F0F\u4E2D\u3002\u8F93\u5165 /code \u5207\u56DE\u4EE3\u7801\u6A21\u5F0F\u3002" }
3999
+ ]);
4000
+ } else {
4001
+ setSessionMode("plan");
4002
+ sessionRef.current?.setMode("plan");
4003
+ setToolChoice(void 0);
4004
+ setDisplayMessages((prev) => [
4005
+ ...prev,
4006
+ { role: "user", content: trimmed },
4007
+ { 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" }
4008
+ ]);
4009
+ sessionRef.current?.reset();
4010
+ }
4011
+ setInput("");
4012
+ return;
4013
+ }
4014
+ if (cmdLower === "/code") {
4015
+ if (sessionMode === "code") {
4016
+ setDisplayMessages((prev) => [
4017
+ ...prev,
4018
+ { role: "user", content: trimmed },
4019
+ { role: "assistant", content: "\u5DF2\u7ECF\u5728\u4EE3\u7801\u6A21\u5F0F\u4E2D\u3002\u8F93\u5165 /plan \u5207\u56DE\u8BA1\u5212\u6A21\u5F0F\u3002" }
4020
+ ]);
4021
+ } else {
4022
+ setSessionMode("code");
4023
+ sessionRef.current?.setMode("code");
4024
+ setToolChoice(void 0);
4025
+ setDisplayMessages((prev) => [
4026
+ ...prev,
4027
+ { role: "user", content: trimmed },
4028
+ { 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" }
4029
+ ]);
4030
+ sessionRef.current?.reset();
4031
+ }
4032
+ setInput("");
4033
+ return;
4034
+ }
3947
4035
  if (cmdLower === "/tools") {
3948
4036
  const next = toolChoice === void 0 ? "none" : toolChoice === "none" ? "required" : toolChoice === "required" ? "auto" : void 0;
3949
4037
  setToolChoice(next);
@@ -4012,6 +4100,7 @@ function ChatSession({
4012
4100
  ]);
4013
4101
  setInput("");
4014
4102
  setIsStreaming(true);
4103
+ setStreamingPhase("thinking");
4015
4104
  setStreamingPlaceholder(pickRandom(STREAMING_PLACEHOLDERS));
4016
4105
  setCurrentContent("");
4017
4106
  setCurrentToolCalls([]);
@@ -4040,6 +4129,7 @@ function ChatSession({
4040
4129
  if (abortController.signal.aborted) break;
4041
4130
  switch (event.type) {
4042
4131
  case "text_delta":
4132
+ setStreamingPhase("generating");
4043
4133
  setCurrentContent((prev) => {
4044
4134
  const next = prev + event.content;
4045
4135
  currentContentRef.current = next;
@@ -4047,6 +4137,7 @@ function ChatSession({
4047
4137
  });
4048
4138
  break;
4049
4139
  case "tool_calls":
4140
+ setStreamingPhase("calling_tools");
4050
4141
  setCurrentToolCalls((prev) => {
4051
4142
  const next = [...prev, ...event.calls];
4052
4143
  currentToolCallsRef.current = next;
@@ -4054,6 +4145,8 @@ function ChatSession({
4054
4145
  });
4055
4146
  break;
4056
4147
  case "tool_result":
4148
+ setStreamingPhase("executing_tools");
4149
+ setTimeout(() => setStreamingPhase("thinking"), 300);
4057
4150
  setCurrentContent("");
4058
4151
  currentContentRef.current = "";
4059
4152
  setCurrentToolCalls([]);
@@ -4096,6 +4189,7 @@ function ChatSession({
4096
4189
  streamErrorRef.current = msg;
4097
4190
  } finally {
4098
4191
  setIsStreaming(false);
4192
+ setStreamingPhase(null);
4099
4193
  setIdlePlaceholder(pickRandom(IDLE_PLACEHOLDERS));
4100
4194
  abortRef.current = null;
4101
4195
  const finContent = currentContentRef.current;
@@ -4120,7 +4214,7 @@ function ChatSession({
4120
4214
  ]);
4121
4215
  }
4122
4216
  }
4123
- }, [onLaunchGame, onLaunchStock, currentContent, currentToolCalls, skills, skillSelectIndex, getFilteredSkills, thinkingEnabled, thinkingEffort, responseFormat, toolChoice, activeModel]);
4217
+ }, [onLaunchGame, onLaunchStock, currentContent, currentToolCalls, skills, skillSelectIndex, getFilteredSkills, thinkingEnabled, thinkingEffort, responseFormat, toolChoice, activeModel, sessionMode]);
4124
4218
  useEffect3(() => {
4125
4219
  if (!isStreaming && externalCostTracker) {
4126
4220
  setTodayCost(externalCostTracker.todayTotalCost);
@@ -4153,6 +4247,7 @@ function ChatSession({
4153
4247
  " \u{1F9E0} \u6DF1\u5EA6\u601D\u8003 ",
4154
4248
  thinkingEffort === "max" ? "Max" : "High"
4155
4249
  ] }),
4250
+ sessionMode === "plan" && /* @__PURE__ */ jsx9(Text10, { color: "#ff69b4", bold: true, children: " \u{1F4CB} \u8BA1\u5212\u6A21\u5F0F" }),
4156
4251
  responseFormat === "json_object" && /* @__PURE__ */ jsx9(Text10, { color: "#4caf50", children: " \u{1F4C4} JSON" }),
4157
4252
  toolChoice !== void 0 && /* @__PURE__ */ jsxs8(Text10, { color: "#e91e63", children: [
4158
4253
  " \u{1F6E0} ",
@@ -4226,7 +4321,6 @@ function ChatSession({
4226
4321
  isStreaming: true
4227
4322
  }
4228
4323
  ),
4229
- isStreaming && !currentContent && currentToolCalls.length === 0 && /* @__PURE__ */ jsx9(Box8, { marginTop: 1, marginLeft: 4, children: /* @__PURE__ */ jsx9(Spinner, { type: "dots", label: "\u601D\u8003\u4E2D..." }) }),
4230
4324
  !isStreaming && streamError && /* @__PURE__ */ jsx9(Box8, { marginTop: 1, marginLeft: 3, children: /* @__PURE__ */ jsxs8(Text10, { color: "red", children: [
4231
4325
  "\u26A0 ",
4232
4326
  streamError
@@ -4265,8 +4359,15 @@ function ChatSession({
4265
4359
  ] }),
4266
4360
  /* @__PURE__ */ jsx9(Text10, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) })
4267
4361
  ] }) : /* @__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)),
4362
+ (hasConversationStarted || sessionMode === "plan") && isStreaming && streamingPhase ? /* @__PURE__ */ jsx9(Box8, { marginTop: 1, justifyContent: "center", children: /* @__PURE__ */ jsxs8(Text10, { bold: true, color: PHASE_CONFIG[streamingPhase].color, children: [
4363
+ PHASE_CONFIG[streamingPhase].icon,
4364
+ " ",
4365
+ PHASE_CONFIG[streamingPhase].label,
4366
+ " ",
4367
+ /* @__PURE__ */ jsx9(InkSpinner2, { type: "dots" })
4368
+ ] }) }) : null,
4369
+ /* @__PURE__ */ jsx9(Box8, { marginTop: 1, children: hasConversationStarted || sessionMode === "plan" ? /* @__PURE__ */ jsxs8(Text10, { color: sessionMode === "plan" ? "#ff69b4" : "#00ffff", dimColor: sessionMode !== "plan", children: [
4370
+ /* @__PURE__ */ jsx9(Text10, { children: sessionMode === "plan" ? "\u2500".repeat(Math.max(dividerWidth - 48, 1)) : "\u2500".repeat(Math.max(dividerWidth - 35, 10)) }),
4270
4371
  balance !== null && /* @__PURE__ */ jsxs8(Text10, { color: "yellow", children: [
4271
4372
  " \u{1F4B0} \u4F59\u989D \xA5",
4272
4373
  balance.toFixed(2)
@@ -4278,7 +4379,8 @@ function ChatSession({
4278
4379
  ] }) : sessionCost > 0 ? /* @__PURE__ */ jsxs8(Text10, { color: "cyan", children: [
4279
4380
  " \u{1F4CA} \u672C\u6B21 \xA5",
4280
4381
  sessionCost.toFixed(4)
4281
- ] }) : null
4382
+ ] }) : null,
4383
+ sessionMode === "plan" && /* @__PURE__ */ jsx9(Text10, { color: "#ff69b4", bold: true, children: " \u{1F4CB} \u8BA1\u5212\u6A21\u5F0F" })
4282
4384
  ] }) : /* @__PURE__ */ jsx9(Text10, { color: "#00ffff", dimColor: true, children: "\u2500".repeat(dividerWidth) }) }),
4283
4385
  /* @__PURE__ */ jsxs8(Box8, { children: [
4284
4386
  /* @__PURE__ */ jsx9(Box8, { width: 4, flexShrink: 0, children: /* @__PURE__ */ jsx9(Text10, { bold: true, color: "#00ff41", children: "\u26A1" }) }),