dskcode 0.1.31 → 0.1.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +116 -34
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/index.tsx","../src/config/loader.ts","../src/cli/middleware.ts","../src/cli/help.ts","../src/cli/api-key-setup.ts","../src/cli/skill-import.ts","../src/ui/RenderScope.tsx","../src/ui/Spinner.tsx","../src/ui/StatusMessage.tsx","../src/ui/DskcodeSplash.tsx","../src/ui/ChatSession.tsx","../src/ui/useDoubleCtrlC.ts","../src/ui/AssistantMessage.tsx","../src/provider/cost-tracker.ts","../src/ui/ToolCallBlock.tsx","../src/agent/message-builder.ts","../src/ui/HighlightedText.tsx","../src/ui/table-layout.ts","../src/ui/reasoning-utils.ts","../src/ui/DiffPreview.tsx","../src/ui/SkillSelector.tsx","../src/ui/FileSelector.tsx","../src/ui/TodoListPanel.tsx","../src/provider/registry.ts","../src/tool/types.ts","../src/agent/system-prompt.ts","../src/agent/prompts/system-prompt.hbs","../src/agent/prompts/plan-prompt.hbs","../src/tool/registry.ts","../src/agent/tool-executor.ts","../src/agent/storm-detector.ts","../src/agent/reflector.ts","../src/agent/tool-definitions.ts","../src/harness/todo-list.ts","../src/harness/tools.ts","../src/checkpoint/git-checkpoint.ts","../src/session-store/session-store.ts","../src/logger/logger.ts","../src/agent/index.ts","../src/tool/sandbox.ts","../src/tool/diff.ts","../src/tool/eol.ts","../src/tool/builtins/read-file.ts","../src/tool/builtins/write-file.ts","../src/tool/builtins/edit-file.ts","../src/tool/builtins/multi-edit.ts","../src/tool/builtins/delete-range.ts","../src/tool/builtins/bash.ts","../src/tool/builtins/glob.ts","../src/tool/builtins/grep.ts","../src/tool/builtins/ls.ts","../src/tool/builtins/fetch.ts","../src/tool/builtins/index.ts","../src/utils/gradient.ts","../src/ui/GamePicker.tsx","../src/game/index.ts","../src/game/brick-breaker/index.tsx","../src/game/coder-check/index.tsx","../src/game/snake/engine.ts","../src/game/snake/renderer.ts","../src/game/snake/input.ts","../src/game/snake/index.ts","../src/game/registry.ts","../src/stock/StockList.tsx","../src/utils/scan-files.ts","../src/cli/exit-codes.ts","../src/index.ts"],"sourcesContent":["import { Command } from \"commander\";\r\nimport { loadConfigMiddleware } from \"./middleware.js\";\r\nimport type { DskcodeContext } from \"./middleware.js\";\r\nimport { customHelp } from \"./help.js\";\r\nimport { hasApiKey, promptForApiKey } from \"./api-key-setup.js\";\r\nimport { promptImportClaudeSkills, countDskcodeSkills, countProjectLocalSkills, getAllSkills } from \"./skill-import.js\";\r\nimport { saveApiKey, loadAndValidate, saveStockConfig } from \"../config/index.js\";\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { join } from \"node:path\";\r\nimport { renderApp, ChatSession } from \"../ui/index.js\";\r\nimport { initGames } from \"../game/registry.js\";\r\nimport { listGames, getGame } from \"../game/index.js\";\r\nimport type { Game } from \"../game/index.js\";\r\nimport { GamePicker } from \"../ui/GamePicker.js\";\r\nimport { render } from \"ink\";\r\nimport chalk from \"chalk\";\r\nimport { StockList } from \"../stock/index.js\";\r\nimport { CostTracker } from \"../provider/cost-tracker.js\";\r\nimport { scanProjectFiles } from \"../utils/scan-files.js\";\r\n\r\nconst SUBCOMMANDS = [\"chat\", \"run\", \"setup\", \"init\", \"completion\", \"game\", \"stock\"];\r\n\r\nexport function createCli(): Command {\r\n const program = new Command();\r\n program.exitOverride();\r\n\r\n program\r\n .name(\"dskcode\")\r\n .description(\"基于 DeepSeek 的 AI 编程助手终端工具\")\r\n .version(\"0.0.0\", \"-V, --version\", \"显示版本号\")\r\n .option(\"--verbose\", \"开启详细日志输出\")\r\n .option(\"--config <path>\", \"指定配置文件路径\");\r\n\r\n program.helpInformation = () => customHelp(program);\r\n\r\n program.hook(\"preAction\", async (thisCommand, actionCommand) => {\r\n const ctx = await loadConfigMiddleware.call(thisCommand);\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n (actionCommand as unknown as Record<string, unknown>).dskcodeCtx = ctx;\r\n });\r\n\r\n // chat — 交互式对话\r\n program\r\n .command(\"chat\")\r\n .description(\"启动交互式对话会话\")\r\n .action(async function () {\r\n if (!process.stdin.isTTY) {\r\n console.error(\"dskcode chat 需要交互式终端。如需执行一次性任务,请使用 dskcode run。\");\r\n process.exit(1);\r\n }\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n let ctx = (this as unknown as Record<string, unknown>).dskcodeCtx as DskcodeContext | undefined;\r\n\r\n // 检查 API Key,如果没有则交互式输入\r\n if (ctx && !hasApiKey(ctx.config.providers)) {\r\n const key = await promptForApiKey();\r\n if (!key) process.exit(1);\r\n\r\n // 保存到全局配置\r\n const savedPath = await saveApiKey(key);\r\n console.log(` ${chalk.green(\"✔\")} API Key 已保存到 ${chalk.dim(savedPath)}\\n`);\r\n\r\n // 重新加载配置,使新 Key 生效\r\n const result = await loadAndValidate();\r\n ctx = { ...ctx, config: result.config };\r\n }\r\n\r\n // API Key 检查之后:检测项目本地 skill 和 Claude Code skill\r\n // 如果 .dskcode/skill 下有 skill,则跳过导入 Claude Code skill\r\n await promptImportClaudeSkills(process.cwd());\r\n\r\n // 从配置创建 CostTracker\r\n const costTracker = new CostTracker({\r\n budgetLimit: ctx?.config.budgetLimit ?? 0,\r\n tokenBudgetLimit: ctx?.config.tokenBudgetLimit ?? 0,\r\n });\r\n\r\n void startChat(ctx, costTracker);\r\n });\r\n\r\n // run\r\n program\r\n .command(\"run\")\r\n .description(\"执行一次性任务\")\r\n .argument(\"[prompt...]\", \"任务描述\")\r\n .option(\"--model <name>\", \"指定使用的模型\")\r\n .action(async function (_prompt: string[]) {\r\n console.log(\"dskcode run — 待实现(第07章)\");\r\n });\r\n\r\n // setup\r\n program\r\n .command(\"setup\")\r\n .description(\"运行配置向导\")\r\n .option(\"--export\", \"以 JSON 格式导出配置\")\r\n .option(\"--test\", \"测试 API Key 连通性\")\r\n .action(async function () {\r\n console.log(\"dskcode setup — 待实现(第14章)\");\r\n });\r\n\r\n // init\r\n program\r\n .command(\"init\")\r\n .description(\"在当前项目下生成项目记忆文件(AGENTS.md)\")\r\n .action(async function () {\r\n console.log(\"dskcode init — 待实现(第11章)\");\r\n });\r\n\r\n // completion\r\n program\r\n .command(\"completion\")\r\n .description(\"输出 shell 自动补全配置说明(bash/zsh)\")\r\n .argument(\"[shell]\", \"shell 类型\", /^(bash|zsh)$/i)\r\n .action(async function (shell?: string) {\r\n if (!shell) {\r\n console.log(\"请指定 shell 类型:dskcode completion bash 或 dskcode completion zsh\");\r\n return;\r\n }\r\n if (shell === \"bash\") {\r\n console.log(`# dskcode bash 自动补全\r\n_dskcode_completion() {\r\n local cur=\\${COMP_WORDS[COMP_CWORD]}\r\n if [[ \\${COMP_CWORD} -eq 1 ]]; then\r\n COMPREPLY=( $(compgen -W \"${SUBCOMMANDS.join(\" \")}\" -- \"\\${cur}\") )\r\n return 0\r\n fi\r\n COMPREPLY=( $(compgen -W \"--verbose --config --model\" -- \"\\${cur}\") )\r\n}\r\ncomplete -F _dskcode_completion dskcode`);\r\n } else {\r\n console.log(`# dskcode zsh 自动补全\r\n_dskcode_completion() {\r\n local -a commands\r\n commands=(\r\n \"chat:启动交互式对话会话\"\r\n \"run:执行一次性任务\"\r\n \"setup:运行配置向导\"\r\n \"init:生成项目记忆文件\"\r\n \"completion:输出 shell 自动补全说明\"\r\n \"game:内置小游戏\"\r\n \"stock:查看自选股实时行情\"\r\n )\r\n _describe 'dskcode commands' commands\r\n}\r\ncompdef _dskcode_completion dskcode`);\r\n }\r\n });\r\n\r\n // stock — 股票行情\r\n program\r\n .command(\"stock\")\r\n .description(\"查看自选股实时行情\")\r\n .argument(\"[codes...]\", \"股票代码(空格分隔),如 513090 600519\")\r\n .action(async function (codes: string[]) {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const _ctx = (this as unknown as Record<string, unknown>).dskcodeCtx as DskcodeContext | undefined;\r\n\r\n // 检查用户全局配置文件是否已有自选股配置;没有则自动创建\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n const globalConfigPath = join(home, \".dskcode\", \"settings.json\");\r\n let globalConfigHasStock = false;\r\n try {\r\n const raw = await readFile(globalConfigPath, \"utf-8\");\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const parsed = JSON.parse(raw) as Record<string, unknown>;\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const stock = parsed.stock as Record<string, unknown> | undefined;\r\n globalConfigHasStock = Array.isArray(stock?.symbols) && (stock.symbols as unknown[]).length > 0;\r\n } catch {\r\n // 文件不存在 — 需要创建\r\n }\r\n\r\n if (!globalConfigHasStock) {\r\n const defaultSymbols = [\r\n { code: \"sh000001\" },\r\n { code: \"sz399300\" },\r\n { code: \"sh601899\" },\r\n ];\r\n const savedPath = await saveStockConfig(defaultSymbols);\r\n console.log(`${chalk.green(\"✔\")} 已生成自选股配置: ${chalk.dim(savedPath)}`);\r\n console.log(`${chalk.dim(\" 提示: 可编辑上述文件自定义自选股列表\")}\\n`);\r\n }\r\n\r\n // 加载配置(包含可能刚写入的自选股)\r\n const freshResult = await loadAndValidate();\r\n const codeList = codes && codes.length > 0\r\n ? codes\r\n : freshResult.config.stock?.symbols?.map((s) => s.code)\r\n ?? [\"sh000001\", \"sz399006\", \"sh601688\"];\r\n\r\n const app = renderApp(\r\n <StockList\r\n codes={codeList}\r\n onExit={() => process.exit(0)}\r\n />,\r\n );\r\n\r\n await app.waitUntilExit;\r\n });\r\n\r\n // game — 游戏模式\r\n initGames();\r\n\r\n program\r\n .command(\"game\")\r\n .description(\"启动内置小游戏\")\r\n .argument(\"[name]\", \"游戏名称,不指定则显示交互式游戏列表\")\r\n .action(async function (name?: string) {\r\n if (name) {\r\n const game = getGame(name);\r\n if (!game) {\r\n console.error(`未找到游戏 \"${name}\"。使用 dskcode game 查看可用游戏列表。`);\r\n process.exit(1);\r\n }\r\n console.log(`正在启动: ${game.name} — ${game.description}\\n`);\r\n await game.play();\r\n } else {\r\n const games = listGames();\r\n if (games.length === 0) {\r\n console.log(\"暂无可用游戏。\");\r\n return;\r\n }\r\n\r\n const selectedGame = await new Promise<Game | null>((resolve) => {\r\n const { unmount } = render(\r\n <GamePicker\r\n games={games}\r\n onSelect={(game) => {\r\n unmount();\r\n resolve(game);\r\n }}\r\n onExit={() => {\r\n unmount();\r\n resolve(null);\r\n }}\r\n />,\r\n { exitOnCtrlC: false },\r\n );\r\n });\r\n\r\n if (selectedGame) {\r\n console.log(`\\n 启动游戏: ${chalk.green(selectedGame.name)}\\n`);\r\n await selectedGame.play();\r\n }\r\n }\r\n });\r\n\r\n return program;\r\n}\r\n\r\nasync function startChat(\r\n ctx: DskcodeContext | undefined,\r\n costTracker: CostTracker,\r\n) {\r\n // 统计 skill 数量并获取详情列表,以及扫描项目文件\r\n const [globalSkillCount, localSkillCount, skills, files] = await Promise.all([\r\n countDskcodeSkills(),\r\n countProjectLocalSkills(process.cwd()),\r\n getAllSkills(process.cwd()),\r\n scanProjectFiles(process.cwd()),\r\n ]);\r\n const skillCount = globalSkillCount + localSkillCount;\r\n // 从配置中提取默认 Provider 的 apiKey 和 baseUrl\r\n const defaultProvider = ctx?.config.providers.find(\r\n (p) => p.name === (ctx?.config.defaultProvider ?? \"deepseek\"),\r\n );\r\n const model = defaultProvider?.model ?? \"deepseek-v4-flash\";\r\n const chatApp = renderApp(\r\n <ChatSession\r\n skillCount={skillCount}\r\n skills={skills}\r\n files={files}\r\n toolCount={ctx?.config.tools.length ?? 0}\r\n verbose={ctx?.verbose ?? false}\r\n apiKey={defaultProvider?.apiKey}\r\n baseUrl={defaultProvider?.baseUrl ?? \"https://api.deepseek.com\"}\r\n costTracker={costTracker}\r\n model={model}\r\n onLaunchGame={() => {\r\n chatApp.unmount();\r\n setImmediate(() => {\r\n initGames();\r\n const games = listGames();\r\n const { unmount } = render(\r\n <GamePicker\r\n games={games}\r\n onSelect={async (game: Game) => {\r\n unmount();\r\n await game.play();\r\n // 游戏结束后返回对话\r\n void startChat(ctx, costTracker);\r\n }}\r\n onBackToChat={() => {\r\n unmount();\r\n setImmediate(() => startChat(ctx, costTracker));\r\n }}\r\n />,\r\n { exitOnCtrlC: false },\r\n );\r\n });\r\n }}\r\n onLaunchStock={() => {\r\n chatApp.unmount();\r\n setImmediate(() => {\r\n // 使用配置中的自选股列表或兜底默认值\r\n const defaultStockCodes = ctx?.config.stock?.symbols?.map((s) => s.code)\r\n ?? [\"sh000001\", \"sz399006\", \"sh601688\"];\r\n const stockApp = renderApp(\r\n <StockList\r\n codes={defaultStockCodes}\r\n onBackToChat={() => {\r\n stockApp.unmount();\r\n setImmediate(() => startChat(ctx, costTracker));\r\n }}\r\n onExit={() => process.exit(0)}\r\n />,\r\n );\r\n });\r\n }}\r\n />,\r\n );\r\n}\r\n","import { existsSync, watch } from \"node:fs\";\r\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\r\nimport { join } from \"node:path\";\r\nimport type { Config, StockConfig } from \"./types.js\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 出厂默认配置\r\n// ---------------------------------------------------------------------------\r\n\r\nexport const defaultConfig: Config = {\r\n defaultProvider: \"deepseek\",\r\n maxTokens: 8192,\r\n temperature: 0.7,\r\n maxToolRounds: 20,\r\n providers: [\r\n {\r\n name: \"deepseek\",\r\n baseUrl: \"https://api.deepseek.com\",\r\n model: \"deepseek-v4-flash\",\r\n },\r\n ],\r\n tools: [\r\n { name: \"read_file\", enabled: true },\r\n { name: \"write_file\", enabled: true },\r\n { name: \"edit_file\", enabled: true },\r\n { name: \"bash\", enabled: true },\r\n { name: \"glob\", enabled: true },\r\n { name: \"grep\", enabled: true },\r\n { name: \"ls\", enabled: true },\r\n { name: \"fetch\", enabled: true },\r\n ],\r\n plugins: [],\r\n stock: {\r\n symbols: [\r\n { code: \"sh000001\" },\r\n { code: \"sz399300\" },\r\n { code: \"sh601899\" },\r\n ],\r\n },\r\n};\r\n\r\n// ---------------------------------------------------------------------------\r\n// 配置文件路径解析\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 判断一个字符串是否为合法的 URL(用于区分 local path 和 url)。\r\n * 目前暂未使用,保留以备 plugin 配置 URL 校验时使用。\r\n */\r\nfunction _isUrl(s: string): boolean {\r\n try {\r\n void new URL(s);\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * 返回候选配置文件路径列表。\r\n * 若传入了 --config 路径,则只使用该路径;\r\n * 否则依次检查用户全局目录和项目本地目录。\r\n */\r\nfunction resolveConfigFiles(configPath?: string): string[] {\r\n if (configPath) {\r\n return [configPath];\r\n }\r\n\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n return [\r\n join(home, \".dskcode\", \"settings.json\"),\r\n join(process.cwd(), \".dskcode\", \"settings.json\"),\r\n ];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 深度合并\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 将较高优先级的配置 overlay 合并到 base 之上。\r\n *\r\n * 合并规则:\r\n * - 标量字段(string / number / boolean):覆盖\r\n * - 数组字段(providers / tools / plugins):直接替换,不合并\r\n */\r\nfunction mergeConfig(base: Config, overlay: Partial<Config>): Config {\r\n const result: Config = { ...base };\r\n\r\n if (overlay.defaultProvider !== undefined) {\r\n result.defaultProvider = overlay.defaultProvider;\r\n }\r\n if (overlay.verbose !== undefined) {\r\n result.verbose = overlay.verbose;\r\n }\r\n if (overlay.maxTokens !== undefined) {\r\n result.maxTokens = overlay.maxTokens;\r\n }\r\n if (overlay.temperature !== undefined) {\r\n result.temperature = overlay.temperature;\r\n }\r\n if (overlay.maxToolRounds !== undefined) {\r\n result.maxToolRounds = overlay.maxToolRounds;\r\n }\r\n if (overlay.budgetLimit !== undefined) {\r\n result.budgetLimit = overlay.budgetLimit;\r\n }\r\n if (overlay.tokenBudgetLimit !== undefined) {\r\n result.tokenBudgetLimit = overlay.tokenBudgetLimit;\r\n }\r\n if (overlay.providers !== undefined) {\r\n result.providers = overlay.providers;\r\n }\r\n if (overlay.tools !== undefined) {\r\n result.tools = overlay.tools;\r\n }\r\n if (overlay.plugins !== undefined) {\r\n result.plugins = overlay.plugins;\r\n }\r\n if (overlay.stock !== undefined) {\r\n result.stock = overlay.stock;\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 环境变量解析\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 环境变量前缀 */\r\nconst ENV_PREFIX = \"DSKCODE_\";\r\n\r\n/** 支持的环境变量映射表 */\r\nconst ENV_MAP: Record<string, keyof Config> = {\r\n [`${ENV_PREFIX}DEFAULT_PROVIDER`]: \"defaultProvider\",\r\n [`${ENV_PREFIX}VERBOSE`]: \"verbose\",\r\n [`${ENV_PREFIX}MAX_TOKENS`]: \"maxTokens\",\r\n [`${ENV_PREFIX}TEMPERATURE`]: \"temperature\",\r\n [`${ENV_PREFIX}MAX_TOOL_ROUNDS`]: \"maxToolRounds\",\r\n [`${ENV_PREFIX}BUDGET_LIMIT`]: \"budgetLimit\",\r\n [`${ENV_PREFIX}TOKEN_BUDGET_LIMIT`]: \"tokenBudgetLimit\",\r\n};\r\n\r\n/**\r\n * 将环境变量中读取的值覆盖到配置上。\r\n * 环境变量的优先级高于 TOML 文件,但低于 CLI flag。\r\n * 返回一个新的配置对象,不修改原始配置。\r\n */\r\nfunction applyEnvVars(config: Config): Config {\r\n // 浅拷贝以避免修改原始对象\r\n const result: Config = { ...config, providers: [...config.providers] };\r\n\r\n // 1. DSKCODE_* 前缀的环境变量\r\n for (const [envKey, configKey] of Object.entries(ENV_MAP)) {\r\n const raw = process.env[envKey];\r\n if (raw === undefined) continue;\r\n\r\n switch (configKey) {\r\n case \"verbose\":\r\n case \"defaultProvider\": {\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n (result as unknown as Record<string, unknown>)[configKey] = raw;\r\n break;\r\n }\r\n case \"maxTokens\":\r\n case \"maxToolRounds\":\r\n case \"budgetLimit\":\r\n case \"tokenBudgetLimit\": {\r\n const n = Number(raw);\r\n if (Number.isFinite(n) && n >= 0) {\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n (result as unknown as Record<string, unknown>)[configKey] = n;\r\n }\r\n break;\r\n }\r\n case \"temperature\": {\r\n const n = Number(raw);\r\n if (Number.isFinite(n) && n >= 0 && n <= 2) {\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n (result as unknown as Record<string, unknown>)[configKey] = n;\r\n }\r\n break;\r\n }\r\n }\r\n }\r\n\r\n // 2. DEEPSEEK_API_KEY — 注入到名为 deepseek 的 provider\r\n const apiKey = process.env.DEEPSEEK_API_KEY;\r\n if (apiKey) {\r\n const deepseekIdx = result.providers.findIndex((p) => p.name === \"deepseek\");\r\n if (deepseekIdx !== -1) {\r\n const existing = result.providers[deepseekIdx]!;\r\n if (!existing.apiKey) {\r\n result.providers[deepseekIdx] = { ...existing, apiKey };\r\n }\r\n } else {\r\n // 如果没有 deepseek provider,自动创建一个\r\n result.providers.unshift({\r\n name: \"deepseek\",\r\n baseUrl: \"https://api.deepseek.com\",\r\n model: \"deepseek-v4-flash\",\r\n apiKey,\r\n });\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// CLI flag 覆盖\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface CliFlags {\r\n verbose?: boolean;\r\n model?: string;\r\n maxTokens?: number;\r\n temperature?: number;\r\n budgetLimit?: number;\r\n tokenBudgetLimit?: number;\r\n}\r\n\r\n/**\r\n * 将 CLI flag 中的值覆盖到配置上。\r\n * CLI flag 的优先级最高。\r\n * 返回一个新的配置对象,不修改原始配置。\r\n */\r\nexport function applyCliOverrides(config: Config, flags: CliFlags): Config {\r\n const result: Config = { ...config, providers: [...config.providers] };\r\n\r\n if (flags.verbose !== undefined) {\r\n result.verbose = flags.verbose;\r\n }\r\n if (flags.model !== undefined) {\r\n // 将 --model 的值映射为标准 model 名称\r\n // 如果用户指定了 --model,覆盖 defaultProvider 中配置的 model\r\n const providerIdx = result.providers.findIndex(\r\n (p) => p.name === result.defaultProvider,\r\n );\r\n if (providerIdx !== -1) {\r\n result.providers[providerIdx] = {\r\n ...result.providers[providerIdx]!,\r\n model: flags.model,\r\n };\r\n }\r\n }\r\n if (flags.maxTokens !== undefined && flags.maxTokens > 0) {\r\n result.maxTokens = flags.maxTokens;\r\n }\r\n if (\r\n flags.temperature !== undefined &&\r\n flags.temperature >= 0 &&\r\n flags.temperature <= 2\r\n ) {\r\n result.temperature = flags.temperature;\r\n }\r\n if (flags.budgetLimit !== undefined && flags.budgetLimit >= 0) {\r\n result.budgetLimit = flags.budgetLimit;\r\n }\r\n if (flags.tokenBudgetLimit !== undefined && flags.tokenBudgetLimit >= 0) {\r\n result.tokenBudgetLimit = flags.tokenBudgetLimit;\r\n }\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 配置校验\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface ConfigError {\r\n field: string;\r\n message: string;\r\n}\r\n\r\n/**\r\n * 校验配置的合法性,返回错误列表。\r\n * 返回空数组表示配置合法。\r\n */\r\nexport function validateConfig(config: Config): ConfigError[] {\r\n const errors: ConfigError[] = [];\r\n\r\n // 1. 至少需要一个 Provider\r\n if (!config.providers || config.providers.length === 0) {\r\n errors.push({\r\n field: \"providers\",\r\n message: \"至少需要配置一个 Provider。请通过配置文件或 DEEPSEEK_API_KEY 环境变量设置。\",\r\n });\r\n }\r\n\r\n // 2. 每个 Provider 必须有 name 和 model,且 model 必须受支持\r\n const SUPPORTED_MODELS = [\"deepseek-v4-flash\", \"deepseek-v4-pro\"];\r\n for (let i = 0; i < config.providers.length; i++) {\r\n const p = config.providers[i]!;\r\n if (!p.name) {\r\n errors.push({\r\n field: `providers[${i}].name`,\r\n message: `第 ${i + 1} 个 Provider 缺少 name 字段。`,\r\n });\r\n }\r\n if (!p.model) {\r\n errors.push({\r\n field: `providers[${i}].model`,\r\n message: `Provider \"${p.name || i}\" 缺少 model 字段。`,\r\n });\r\n } else if (!SUPPORTED_MODELS.includes(p.model)) {\r\n errors.push({\r\n field: `providers[${i}].model`,\r\n message: `Provider \"${p.name || i}\" 的 model \"${p.model}\" 不受支持。dskcode 仅支持: ${SUPPORTED_MODELS.join(\", \")}`,\r\n });\r\n }\r\n }\r\n\r\n // 3. defaultProvider 必须存在于 providers 列表中\r\n if (config.defaultProvider) {\r\n const exists = config.providers.some(\r\n (p) => p.name === config.defaultProvider,\r\n );\r\n if (!exists) {\r\n errors.push({\r\n field: \"defaultProvider\",\r\n message: `默认 Provider \"${config.defaultProvider}\" 未在 providers 中定义。`,\r\n });\r\n }\r\n }\r\n\r\n // 4. temperature 范围校验\r\n if (\r\n config.temperature !== undefined &&\r\n (config.temperature < 0 || config.temperature > 2)\r\n ) {\r\n errors.push({\r\n field: \"temperature\",\r\n message: \"temperature 必须在 0.0 ~ 2.0 之间。\",\r\n });\r\n }\r\n\r\n // 5. maxTokens 范围校验\r\n if (config.maxTokens !== undefined && config.maxTokens < 1) {\r\n errors.push({\r\n field: \"maxTokens\",\r\n message: \"maxTokens 必须大于等于 1。\",\r\n });\r\n }\r\n\r\n // 6. maxToolRounds 范围校验\r\n if (config.maxToolRounds !== undefined && config.maxToolRounds < 1) {\r\n errors.push({\r\n field: \"maxToolRounds\",\r\n message: \"maxToolRounds 必须大于等于 1。\",\r\n });\r\n }\r\n\r\n // 7. budgetLimit 范围校验\r\n if (config.budgetLimit !== undefined && config.budgetLimit < 0) {\r\n errors.push({\r\n field: \"budgetLimit\",\r\n message: \"budgetLimit 必须大于等于 0(0 表示不限制)。\",\r\n });\r\n }\r\n\r\n // 8. tokenBudgetLimit 范围校验\r\n if (config.tokenBudgetLimit !== undefined && config.tokenBudgetLimit < 0) {\r\n errors.push({\r\n field: \"tokenBudgetLimit\",\r\n message: \"tokenBudgetLimit 必须大于等于 0(0 表示不限制)。\",\r\n });\r\n }\r\n\r\n return errors;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 核心加载流程\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 从多级配置源加载并合并配置。\r\n *\r\n * 解析顺序(后加载的优先级更高):\r\n * 1. 内置默认值 —— defaultConfig\r\n * 2. 用户全局 —— ~/.dskcode/settings.json\r\n * 3. 项目本地 —— .dskcode/settings.json(或通过 --config 指定的路径)\r\n * 4. 环境变量 —— DEEPSEEK_API_KEY、DSKCODE_* 等\r\n * 5. CLI flag —— 由调用方通过 applyCliOverrides() 单独注入\r\n */\r\nexport async function loadConfig(configPath?: string): Promise<Config> {\r\n const filePaths = resolveConfigFiles(configPath);\r\n\r\n let config: Config = structuredClone(defaultConfig);\r\n\r\n // 1-3. 依次加载 JSON 配置文件\r\n for (const filePath of filePaths) {\r\n try {\r\n const raw = await readFile(filePath, \"utf-8\");\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n const parsed = JSON.parse(raw) as Partial<Config>;\r\n config = mergeConfig(config, parsed);\r\n } catch {\r\n // 文件不存在或权限不足 — 静默跳过\r\n }\r\n }\r\n\r\n // 4. 环境变量覆盖\r\n config = applyEnvVars(config);\r\n\r\n return config;\r\n}\r\n\r\n/**\r\n * 加载配置并同时执行校验。\r\n * 校验错误不会 throw,而是通过返回值中的 errors 字段返回,\r\n * 由调用方决定如何处理(例如在 middleware 中输出警告)。\r\n */\r\nexport async function loadAndValidate(\r\n configPath?: string,\r\n): Promise<{ config: Config; errors: ConfigError[] }> {\r\n const config = await loadConfig(configPath);\r\n const errors = validateConfig(config);\r\n return { config, errors };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 配置热加载(Watch 模式)\r\n// ---------------------------------------------------------------------------\r\n\r\nexport type ConfigChangeCallback = (config: Config) => void;\r\n\r\n/**\r\n * 监听配置文件变更,在文件被修改时重新加载配置并调用回调。\r\n *\r\n * @param callback 配置变更后的回调函数\r\n * @param configPath 可选,指定配置文件路径(对应 --config flag)\r\n * @returns 一个 unwatch 函数,调用后可停止监听\r\n */\r\nexport function watchConfig(\r\n callback: ConfigChangeCallback,\r\n configPath?: string,\r\n): () => void {\r\n const filePaths = resolveConfigFiles(configPath).filter((fp) => existsSync(fp));\r\n\r\n // 如果一个文件都不存在,则监听项目本地的 .dskcode/settings.json(即使还没创建)\r\n if (filePaths.length === 0) {\r\n filePaths.push(join(process.cwd(), \".dskcode\", \"settings.json\"));\r\n }\r\n\r\n const watchers: ReturnType<typeof watch>[] = [];\r\n let debounceTimer: ReturnType<typeof setTimeout>;\r\n\r\n for (const filePath of filePaths) {\r\n try {\r\n const watcher = watch(filePath, (eventType) => {\r\n if (eventType !== \"change\") return;\r\n\r\n // 防抖:多次连续变更只触发一次\r\n if (debounceTimer) clearTimeout(debounceTimer);\r\n debounceTimer = setTimeout(() => {\r\n void (async () => {\r\n try {\r\n const config = await loadConfig(configPath);\r\n callback(config);\r\n } catch {\r\n // 重载失败时不回调,等待下一次变更\r\n }\r\n })();\r\n }, 300);\r\n });\r\n\r\n watchers.push(watcher);\r\n } catch {\r\n // 无法监听的文件(例如还不存在)— 跳过\r\n }\r\n }\r\n\r\n return () => {\r\n if (debounceTimer) clearTimeout(debounceTimer);\r\n for (const w of watchers) {\r\n w.close();\r\n }\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// API Key 持久化\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 将 API Key 保存到用户全局配置 ~/.dskcode/settings.json。\r\n * 如果文件已存在,合并写入;不存在则新建。\r\n * 返回保存的文件路径。\r\n */\r\nexport async function saveApiKey(apiKey: string): Promise<string> {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n const configDir = join(home, \".dskcode\");\r\n const configFile = join(configDir, \"settings.json\");\r\n\r\n // 确保目录存在\r\n await mkdir(configDir, { recursive: true });\r\n\r\n // 读取现有配置,或从默认配置开始\r\n let configData: Record<string, unknown>;\r\n try {\r\n const raw = await readFile(configFile, \"utf-8\");\r\n configData = JSON.parse(raw);\r\n } catch {\r\n // 文件不存在,用内置默认值填充(tools、plugins 等都会写入)\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n configData = structuredClone(defaultConfig) as unknown as Record<string, unknown>;\r\n }\r\n\r\n // 更新或创建 deepseek provider\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n const providers = (configData.providers as Record<string, unknown>[]) ?? [];\r\n const existing = providers.find((p) => p.name === \"deepseek\");\r\n\r\n if (existing) {\r\n existing.apiKey = apiKey;\r\n } else {\r\n providers.push({\r\n name: \"deepseek\",\r\n apiKey,\r\n baseUrl: \"https://api.deepseek.com\",\r\n model: \"deepseek-v4-flash\",\r\n });\r\n }\r\n\r\n configData.providers = providers;\r\n\r\n // 写回文件\r\n await writeFile(configFile, JSON.stringify(configData, null, 2), \"utf-8\");\r\n\r\n return configFile;\r\n}\r\n\r\n/**\r\n * 将模型偏好保存到用户全局配置 ~/.dskcode/settings.json。\r\n * 只更新 defaultProvider 的 model 字段,不覆盖其他配置。\r\n * 返回保存的文件路径。\r\n */\r\nexport async function saveModelConfig(model: string): Promise<string> {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n const configDir = join(home, \".dskcode\");\r\n const configFile = join(configDir, \"settings.json\");\r\n\r\n // 确保目录存在\r\n await mkdir(configDir, { recursive: true });\r\n\r\n // 读取现有配置\r\n let configData: Record<string, unknown>;\r\n try {\r\n const raw = await readFile(configFile, \"utf-8\");\r\n configData = JSON.parse(raw);\r\n } catch {\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n configData = structuredClone(defaultConfig) as unknown as Record<string, unknown>;\r\n }\r\n\r\n // 更新 defaultProvider 的 model\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n const providers = (configData.providers as Record<string, unknown>[]) ?? [];\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n const defaultProviderName = (configData.defaultProvider as string) ?? \"deepseek\";\r\n const existing = providers.find((p) => p.name === defaultProviderName);\r\n\r\n if (existing) {\r\n existing.model = model;\r\n } else {\r\n // 没有 default provider,创建一个\r\n providers.push({\r\n name: defaultProviderName,\r\n baseUrl: \"https://api.deepseek.com\",\r\n model,\r\n });\r\n }\r\n\r\n configData.providers = providers;\r\n\r\n // 写回文件\r\n await writeFile(configFile, JSON.stringify(configData, null, 2), \"utf-8\");\r\n\r\n return configFile;\r\n}\r\n\r\n/**\r\n * 将自选股配置保存到用户全局配置 ~/.dskcode/settings.json。\r\n * 如果文件已存在,合并写入;不存在则新建。\r\n * 返回保存的文件路径。\r\n */\r\nexport async function saveStockConfig(symbols: StockConfig[\"symbols\"]): Promise<string> {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n const configDir = join(home, \".dskcode\");\r\n const configFile = join(configDir, \"settings.json\");\r\n\r\n // 确保目录存在\r\n await mkdir(configDir, { recursive: true });\r\n\r\n // 读取现有配置,或从默认配置开始\r\n let configData: Record<string, unknown>;\r\n try {\r\n const raw = await readFile(configFile, \"utf-8\");\r\n configData = JSON.parse(raw);\r\n } catch {\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n configData = structuredClone(defaultConfig) as unknown as Record<string, unknown>;\r\n }\r\n\r\n // 写入自选股配置\r\n configData.stock = { symbols };\r\n\r\n // 写回文件\r\n await writeFile(configFile, JSON.stringify(configData, null, 2), \"utf-8\");\r\n\r\n return configFile;\r\n}\r\n","import type { Command } from \"commander\";\r\nimport {\r\n loadAndValidate,\r\n applyCliOverrides,\r\n defaultConfig,\r\n} from \"../config/index.js\";\r\nimport type { Config } from \"../config/index.js\";\r\n\r\n/**\r\n * dskcode 运行时上下文。\r\n * 通过 commander 的 preAction hook 注入到每个命令中。\r\n */\r\nexport interface DskcodeContext {\r\n config: Config;\r\n verbose: boolean;\r\n}\r\n\r\n/**\r\n * 在 preAction hook 中加载配置并构造上下文。\r\n *\r\n * 完整的配置解析流水线:\r\n * 1. 内置默认值 —— defaultConfig\r\n * 2. 用户全局 —— ~/.dskcode/settings.json\r\n * 3. 项目本地 —— .dskcode/settings.json(或 --config 指定的路径)\r\n * 4. 环境变量 —— DEEPSEEK_API_KEY / DSKCODE_*\r\n * 5. CLI flag —— --verbose / --model 等\r\n */\r\nexport async function loadConfigMiddleware(\r\n this: Command,\r\n): Promise<DskcodeContext> {\r\n // eslint-disable-next-line oxc/no-this-in-exported-function, @typescript-eslint/no-unsafe-type-assertion\r\n const opts = this.optsWithGlobals() as {\r\n verbose?: boolean;\r\n config?: string;\r\n model?: string;\r\n };\r\n const verbose = opts.verbose ?? false;\r\n\r\n // 1-4. 加载 TOML 文件 + 环境变量\r\n let config: Config;\r\n const errors: string[] = [];\r\n try {\r\n const result = await loadAndValidate(opts.config);\r\n config = result.config;\r\n if (result.errors.length > 0) {\r\n for (const e of result.errors) {\r\n errors.push(e.message);\r\n }\r\n }\r\n } catch {\r\n config = structuredClone(defaultConfig);\r\n }\r\n\r\n // 5. CLI flag 覆盖(优先级最高)\r\n config = applyCliOverrides(config, {\r\n verbose,\r\n model: opts.model,\r\n });\r\n\r\n // 校验错误输出(不阻断执行)\r\n if (errors.length > 0 && verbose) {\r\n for (const msg of errors) {\r\n console.error(` ⚠ ${msg}`);\r\n }\r\n }\r\n\r\n return { config, verbose };\r\n}\r\n","import type { Command } from \"commander\";\r\nimport chalk from \"chalk\";\r\n\r\nexport function customHelp(program: Command): string {\r\n const lines: string[] = [];\r\n\r\n lines.push(\"\");\r\n lines.push(chalk.bold(\"用法:\"));\r\n lines.push(` ${chalk.cyan(\"dskcode\")} ${chalk.dim(\"[global-options]\")} ${chalk.green(\"<command>\")} ${chalk.dim(\"[options]\")}`);\r\n lines.push(\"\");\r\n\r\n const globalOpts = program.options.filter(\r\n (o) => o.long !== \"--help\" && o.long !== \"--version\" && o.long !== \"--config\",\r\n );\r\n if (globalOpts.length > 0) {\r\n lines.push(chalk.bold(\"全局选项:\"));\r\n for (const opt of globalOpts) {\r\n const flags = [opt.short, opt.long].filter(Boolean).join(\", \");\r\n lines.push(` ${chalk.cyan(flags.padEnd(24))} ${opt.description ?? \"\"}`);\r\n }\r\n lines.push(\"\");\r\n }\r\n\r\n lines.push(chalk.bold(\"内置选项:\"));\r\n for (const flag of [\"-h, --help\", \"-V, --version\"]) {\r\n const opt = program.options.find(\r\n (o) => o.long === (flag.includes(\"help\") ? \"--help\" : \"--version\"),\r\n );\r\n if (opt) {\r\n lines.push(` ${chalk.cyan(flag.padEnd(24))} ${opt.description ?? \"\"}`);\r\n }\r\n }\r\n lines.push(\"\");\r\n\r\n const cmds = program.commands.filter((c) => !c.name().startsWith(\"help\"));\r\n if (cmds.length > 0) {\r\n lines.push(chalk.bold(\"命令:\"));\r\n for (const cmd of cmds) {\r\n lines.push(` ${chalk.green(cmd.name().padEnd(24))} ${cmd.description()}`);\r\n }\r\n lines.push(\"\");\r\n }\r\n\r\n lines.push(chalk.bold(\"示例:\"));\r\n lines.push(` ${chalk.dim(\"# 启动交互式对话\")}`);\r\n lines.push(\" dskcode chat\");\r\n lines.push(` ${chalk.dim(\"# 让 AI 执行一个任务\")}`);\r\n lines.push(\" dskcode run 修改所有 TODO 注释\");\r\n lines.push(` ${chalk.dim(\"# 运行配置向导\")}`);\r\n lines.push(\" dskcode setup\");\r\n lines.push(` ${chalk.dim(\"# 生成 shell 自动补全\")}`);\r\n lines.push(\" dskcode completion\");\r\n lines.push(` ${chalk.dim(\"# 查看自选股行情\")}`);\r\n lines.push(\" dskcode stock\");\r\n lines.push(` ${chalk.dim(\"# 查看指定股票行情\")}`);\r\n lines.push(\" dskcode stock sh513090 sz000001\");\r\n lines.push(\"\");\r\n\r\n return lines.join(\"\\n\");\r\n}\r\n","import { createInterface } from \"node:readline\";\r\nimport chalk from \"chalk\";\r\n\r\n/**\r\n * 检测是否有可用的 API Key。\r\n * 遍历所有 provider 检查是否配置了 apiKey,同时检查 DEEPSEEK_API_KEY 环境变量。\r\n */\r\nexport function hasApiKey(providers: { apiKey?: string }[]): boolean {\r\n if (providers.some((p) => p.apiKey)) return true;\r\n if (process.env.DEEPSEEK_API_KEY) return true;\r\n return false;\r\n}\r\n\r\n/**\r\n * API Key 最小长度校验值。\r\n * DeepSeek 的 Key 通常以 sk- 开头,长度远超 10 位。\r\n */\r\nconst MIN_API_KEY_LENGTH = 10;\r\n\r\n/**\r\n * 交互式提示用户输入 DeepSeek API Key。\r\n * 使用 Node readline 的 password 模式(输入不可见)。\r\n * 返回用户输入的 Key,如果用户取消则返回 null。\r\n */\r\nexport async function promptForApiKey(): Promise<string | null> {\r\n console.log(chalk.yellow(\"\\n ⚠ 未检测到 API Key 配置\"));\r\n console.log(chalk.dim(\" 你可以通过以下任一方式配置:\"));\r\n console.log(chalk.dim(\" · 环境变量: export DEEPSEEK_API_KEY=sk-xxx\"));\r\n console.log(chalk.dim(\" · 配置文件: ~/.dskcode/settings.json\"));\r\n console.log(chalk.dim(\" · 下面直接输入,自动保存到全局配置\\n\"));\r\n\r\n const rl = createInterface({\r\n input: process.stdin,\r\n output: process.stdout,\r\n });\r\n\r\n return new Promise<string | null>((resolve) => {\r\n let resolved = false;\r\n\r\n const cleanup = () => {\r\n if (resolved) return;\r\n resolved = true;\r\n process.stdin.removeListener(\"keypress\", onKeypress);\r\n rl.close();\r\n };\r\n\r\n const onKeypress = (_: unknown, key: { ctrl?: boolean; name?: string }) => {\r\n if (key.ctrl && key.name === \"c\") {\r\n cleanup();\r\n resolve(null);\r\n }\r\n };\r\n\r\n process.stdin.on(\"keypress\", onKeypress);\r\n\r\n rl.question(\r\n ` ${chalk.cyan(\"🔑\")} ${chalk.bold(\"请输入你的 DeepSeek API Key:\")} `,\r\n (answer) => {\r\n cleanup();\r\n const trimmed = answer.trim();\r\n if (!trimmed) {\r\n console.log(chalk.red(\" ✖ API Key 不能为空\"));\r\n resolve(null);\r\n return;\r\n }\r\n if (trimmed.length < MIN_API_KEY_LENGTH) {\r\n console.log(chalk.red(\" ✖ API Key 格式不正确,长度至少 10 位\"));\r\n resolve(null);\r\n return;\r\n }\r\n resolve(trimmed);\r\n },\r\n );\r\n });\r\n}\r\n","import { createInterface } from \"node:readline\";\r\nimport { existsSync, statSync, lstatSync } from \"node:fs\";\r\nimport { mkdir, readdir, cp, access, realpath, readFile } from \"node:fs/promises\";\r\nimport { join } from \"node:path\";\r\nimport chalk from \"chalk\";\r\n\r\n/**\r\n * Claude Code 全局 skills 目录路径(~/.claude/skills)。\r\n * 跨平台兼容:优先用 HOME,Windows 下回退到 USERPROFILE。\r\n */\r\nfunction getClaudeSkillsDir(): string {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n return join(home, \".claude\", \"skills\");\r\n}\r\n\r\n/**\r\n * dskcode 全局 skills 目录路径(~/.dskcode/skills)。\r\n */\r\nfunction getDskcodeSkillsDir(): string {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n return join(home, \".dskcode\", \"skills\");\r\n}\r\n\r\n/**\r\n * 判断某个目录是否为一个有效的 skill(包含 SKILL.md)。\r\n */\r\nasync function isSkillDir(dir: string): Promise<boolean> {\r\n try {\r\n await access(join(dir, \"SKILL.md\"));\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * 列出 Claude Code skills 目录下所有有效的 skill 名称(子目录包含 SKILL.md)。\r\n * 找不到目录返回空数组。\r\n */\r\nexport async function listClaudeSkills(): Promise<string[]> {\r\n const claudeDir = getClaudeSkillsDir();\r\n if (!existsSync(claudeDir)) return [];\r\n\r\n let entries: string[];\r\n try {\r\n entries = await readdir(claudeDir);\r\n } catch {\r\n return [];\r\n }\r\n\r\n const skills: string[] = [];\r\n for (const name of entries) {\r\n const full = join(claudeDir, name);\r\n const stat = statSync(full);\r\n if (stat.isDirectory() && (await isSkillDir(full))) {\r\n skills.push(name);\r\n }\r\n }\r\n return skills;\r\n}\r\n\r\n/**\r\n * 是否存在可导入的 Claude skills。\r\n */\r\nexport async function hasClaudeSkills(): Promise<boolean> {\r\n const skills = await listClaudeSkills();\r\n return skills.length > 0;\r\n}\r\n\r\n/**\r\n * 交互式询问用户是否确认。默认接受回车视为\"否\"。\r\n * 返回 true 表示用户确认。\r\n */\r\nfunction askConfirm(question: string): Promise<boolean> {\r\n const rl = createInterface({\r\n input: process.stdin,\r\n output: process.stdout,\r\n });\r\n\r\n return new Promise<boolean>((resolve) => {\r\n let resolved = false;\r\n\r\n const cleanup = () => {\r\n if (resolved) return;\r\n resolved = true;\r\n process.stdin.removeListener(\"keypress\", onKeypress);\r\n rl.close();\r\n };\r\n\r\n const onKeypress = (_: unknown, key: { ctrl?: boolean; name?: string }) => {\r\n if (key.ctrl && key.name === \"c\") {\r\n cleanup();\r\n resolve(false);\r\n }\r\n };\r\n\r\n process.stdin.on(\"keypress\", onKeypress);\r\n\r\n rl.question(question, (answer) => {\r\n cleanup();\r\n const trimmed = answer.trim().toLowerCase();\r\n // 默认回车 = 否;明确输入 y / yes 才算确认\r\n resolve(trimmed === \"y\" || trimmed === \"yes\");\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * 将 Claude skills 目录复制到 ~/.dskcode/skills。\r\n * 已存在的同名 skill 跳过,避免覆盖用户自定义修改。\r\n * 返回 { imported, skipped } 两个数组。\r\n */\r\nexport async function importClaudeSkills(\r\n skillNames: string[],\r\n): Promise<{ imported: string[]; skipped: string[] }> {\r\n const claudeDir = getClaudeSkillsDir();\r\n const dskcodeDir = getDskcodeSkillsDir();\r\n\r\n await mkdir(dskcodeDir, { recursive: true });\r\n\r\n const imported: string[] = [];\r\n const skipped: string[] = [];\r\n\r\n for (const name of skillNames) {\r\n const src = join(claudeDir, name);\r\n const dest = join(dskcodeDir, name);\r\n\r\n if (existsSync(dest)) {\r\n skipped.push(name);\r\n continue;\r\n }\r\n\r\n // 递归复制整个 skill 目录,保留原始结构(SKILL.md、reference 等)。\r\n // 两个解引用手段叠加:\r\n // 1) realpath() 解决 source 顶层是 symlink 的情况(cp 的 dereference 只处理源目录内部 symlink)\r\n // 2) cp 的 dereference: true 解决源目录内部遇到的 symlink\r\n // 这样可以避开 Windows 上非管理员无法创建 symlink 的 EPERM 错误\r\n // (Claude Code 的 skill 目录往往是 symlink 指向 ~/.agents/skills)。\r\n const realSrc = lstatSync(src).isSymbolicLink() ? await realpath(src) : src;\r\n await cp(realSrc, dest, { recursive: true, dereference: true });\r\n imported.push(name);\r\n }\r\n\r\n return { imported, skipped };\r\n}\r\n\r\n/**\r\n * 统计全局 ~/.dskcode/skills 下的有效 skill 数量。\r\n */\r\nexport async function countDskcodeSkills(): Promise<number> {\r\n const dskcodeDir = getDskcodeSkillsDir();\r\n if (!existsSync(dskcodeDir)) return 0;\r\n\r\n let entries: string[];\r\n try {\r\n entries = await readdir(dskcodeDir);\r\n } catch {\r\n return 0;\r\n }\r\n\r\n let count = 0;\r\n for (const name of entries) {\r\n const full = join(dskcodeDir, name);\r\n if (statSync(full).isDirectory() && (await isSkillDir(full))) {\r\n count++;\r\n }\r\n }\r\n return count;\r\n}\r\n\r\n/**\r\n * 项目本地 skill 目录路径({cwd}/.dskcode/skill)。\r\n */\r\nexport function getProjectSkillDir(cwd: string): string {\r\n return join(cwd, \".dskcode\", \"skill\");\r\n}\r\n\r\n/**\r\n * 检测项目本地 .dskcode/skill 目录下是否存在有效 skill。\r\n */\r\nexport async function hasProjectLocalSkills(cwd: string): Promise<boolean> {\r\n const count = await countProjectLocalSkills(cwd);\r\n return count > 0;\r\n}\r\n\r\n/**\r\n * 统计项目本地 .dskcode/skill 下的有效 skill 数量。\r\n */\r\nexport async function countProjectLocalSkills(cwd: string): Promise<number> {\r\n const skillDir = getProjectSkillDir(cwd);\r\n if (!existsSync(skillDir)) return 0;\r\n\r\n let entries: string[];\r\n try {\r\n entries = await readdir(skillDir);\r\n } catch {\r\n return 0;\r\n }\r\n\r\n let count = 0;\r\n for (const name of entries) {\r\n const full = join(skillDir, name);\r\n const stat = statSync(full);\r\n if (stat.isDirectory() && (await isSkillDir(full))) {\r\n count++;\r\n }\r\n }\r\n return count;\r\n}\r\n\r\n/**\r\n * 检测全局 ~/.dskcode/skills 目录下是否存在有效 skill。\r\n */\r\nexport async function hasDskcodeSkills(): Promise<boolean> {\r\n const dskcodeDir = getDskcodeSkillsDir();\r\n if (!existsSync(dskcodeDir)) return false;\r\n\r\n let entries: string[];\r\n try {\r\n entries = await readdir(dskcodeDir);\r\n } catch {\r\n return false;\r\n }\r\n\r\n for (const name of entries) {\r\n const full = join(dskcodeDir, name);\r\n const stat = statSync(full);\r\n if (stat.isDirectory() && (await isSkillDir(full))) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n}\r\n\r\n/**\r\n * 主流程:检测已有 skill 并决定是否提示导入 Claude Code skill。\r\n * 通常在 chat 命令的 API Key 检查之后调用。\r\n *\r\n * 规则(按优先级):\r\n * 1. 如果项目本地 .dskcode/skill 下有 skill,跳过导入\r\n * 2. 如果全局 ~/.dskcode/skills 下有 skill(已导入过),跳过导入\r\n * 3. 否则检测 Claude Code 并询问是否导入\r\n */\r\nexport async function promptImportClaudeSkills(cwd?: string): Promise<void> {\r\n // 检查项目本地 .dskcode/skill\r\n if (cwd) {\r\n const hasLocal = await hasProjectLocalSkills(cwd);\r\n if (hasLocal) {\r\n console.log(chalk.dim(\" 检测到项目本地 .dskcode/skill,跳过导入 Claude Code skill\\n\"));\r\n return;\r\n }\r\n }\r\n\r\n // 检查全局 ~/.dskcode/skills(已经导入过的无需再提示)\r\n const hasGlobal = await hasDskcodeSkills();\r\n if (hasGlobal) {\r\n return;\r\n }\r\n\r\n const skills = await listClaudeSkills();\r\n if (skills.length === 0) return;\r\n\r\n console.log(chalk.cyan(`\\n ✦ 检测到你在 Claude Code 中安装了 ${String(skills.length)} 个 skill:`));\r\n // 每行最多展示 5 个,避免列表过长\r\n const preview = skills.slice(0, 5).map((s) => chalk.dim(` · ${s}`)).join(\"\\n\");\r\n console.log(preview);\r\n if (skills.length > 5) {\r\n console.log(chalk.dim(` · ...等 ${String(skills.length)} 个`));\r\n }\r\n console.log(chalk.dim(` 源目录: ${getClaudeSkillsDir()}`));\r\n console.log(chalk.dim(` 目标目录: ${getDskcodeSkillsDir()}\\n`));\r\n\r\n const confirmed = await askConfirm(\r\n ` ${chalk.cyan(\"📦\")} 是否将这些 skill 导入到 dskcode?${chalk.dim(\"[y/N] \")} `,\r\n );\r\n\r\n if (!confirmed) {\r\n console.log(chalk.dim(\" 已跳过 skill 导入\\n\"));\r\n return;\r\n }\r\n\r\n try {\r\n const { imported, skipped } = await importClaudeSkills(skills);\r\n if (imported.length > 0) {\r\n console.log(\r\n chalk.green(` ✔ 已导入 ${String(imported.length)} 个 skill 到 ~/.dskcode/skills`),\r\n );\r\n }\r\n if (skipped.length > 0) {\r\n console.log(\r\n chalk.yellow(` ⚠ ${String(skipped.length)} 个 skill 已存在,已跳过:${skipped.join(\", \")}`),\r\n );\r\n }\r\n console.log(\"\");\r\n } catch (err) {\r\n console.log(\r\n chalk.red(` ✖ 导入 skill 失败: ${err instanceof Error ? err.message : String(err)}`),\r\n );\r\n console.log(chalk.dim(\" 你可以稍后手动复制 ~/.claude/skills 到 ~/.dskcode/skills\\n\"));\r\n }\r\n}\r\n\r\n/** 单个 skill 的展示信息 */\r\nexport interface SkillInfo {\r\n name: string;\r\n description: string;\r\n}\r\n\r\n/**\r\n * 列出全局 ~/.dskcode/skills 下所有有效的 skill 名称。\r\n */\r\nexport async function listDskcodeSkills(): Promise<string[]> {\r\n const dskcodeDir = getDskcodeSkillsDir();\r\n if (!existsSync(dskcodeDir)) return [];\r\n\r\n let entries: string[];\r\n try {\r\n entries = await readdir(dskcodeDir);\r\n } catch {\r\n return [];\r\n }\r\n\r\n const skills: string[] = [];\r\n for (const name of entries) {\r\n const full = join(dskcodeDir, name);\r\n if (statSync(full).isDirectory() && (await isSkillDir(full))) {\r\n skills.push(name);\r\n }\r\n }\r\n return skills;\r\n}\r\n\r\n/**\r\n * 列出项目本地 .dskcode/skill 下所有有效的 skill 名称。\r\n */\r\nexport async function listProjectSkills(cwd: string): Promise<string[]> {\r\n const skillDir = getProjectSkillDir(cwd);\r\n if (!existsSync(skillDir)) return [];\r\n\r\n let entries: string[];\r\n try {\r\n entries = await readdir(skillDir);\r\n } catch {\r\n return [];\r\n }\r\n\r\n const skills: string[] = [];\r\n for (const name of entries) {\r\n const full = join(skillDir, name);\r\n if (statSync(full).isDirectory() && (await isSkillDir(full))) {\r\n skills.push(name);\r\n }\r\n }\r\n return skills;\r\n}\r\n\r\n/**\r\n * 解析 SKILL.md 的 YAML front matter,获取 skill 名称和描述。\r\n * 解析失败时返回 null。\r\n */\r\nasync function readSkillInfo(skillDir: string): Promise<SkillInfo | null> {\r\n try {\r\n const content = await readFile(join(skillDir, \"SKILL.md\"), \"utf-8\");\r\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---/);\r\n if (!match) return null;\r\n\r\n const frontMatter = match[1]!;\r\n const nameMatch = frontMatter.match(/^name:\\s*(.+)$/m);\r\n const descMatch = frontMatter.match(/^description:\\s*(.+)$/m);\r\n\r\n return {\r\n name: nameMatch?.[1]?.trim() ?? \"\",\r\n description: descMatch?.[1]?.trim() ?? \"\",\r\n };\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * 获取所有可用 skill 的详细信息(全局 + 项目本地)。\r\n * 返回 SkillInfo 列表,解析失败时仍使用目录名作为 name。\r\n */\r\nexport async function getAllSkills(cwd: string): Promise<SkillInfo[]> {\r\n const [globalNames, localNames] = await Promise.all([\r\n listDskcodeSkills(),\r\n listProjectSkills(cwd),\r\n ]);\r\n\r\n // 合并去重,项目本地优先\r\n const seen = new Set<string>();\r\n const allNames: string[] = [];\r\n for (const name of [...localNames, ...globalNames]) {\r\n if (!seen.has(name)) {\r\n seen.add(name);\r\n allNames.push(name);\r\n }\r\n }\r\n\r\n const dskcodeDir = getDskcodeSkillsDir();\r\n const projectDir = getProjectSkillDir(cwd);\r\n\r\n const results: SkillInfo[] = [];\r\n for (const name of allNames) {\r\n // 优先读项目本地的 SKILL.md,再读全局\r\n const localPath = join(projectDir, name);\r\n let info: SkillInfo | null = null;\r\n if (existsSync(localPath)) {\r\n info = await readSkillInfo(localPath);\r\n }\r\n if (!info) {\r\n const globalPath = join(dskcodeDir, name);\r\n if (existsSync(globalPath)) {\r\n info = await readSkillInfo(globalPath);\r\n }\r\n }\r\n results.push(info ?? { name, description: \"\" });\r\n }\r\n\r\n return results;\r\n}\r\n\r\n// 暴露路径获取函数,便于测试\r\nexport { getClaudeSkillsDir, getDskcodeSkillsDir };","import { render } from \"ink\";\r\nimport type { ReactNode } from \"react\";\r\n\r\nexport interface RenderScopeHandle {\r\n waitUntilExit: Promise<unknown>;\r\n unmount: () => void;\r\n clear: () => void;\r\n}\r\n\r\nexport function renderApp(node: ReactNode): RenderScopeHandle {\r\n // exitOnCtrlC: false — 让组件自行处理 Ctrl+C(双击退出)\r\n const { waitUntilExit, clear, unmount } = render(node, { exitOnCtrlC: false });\r\n return { waitUntilExit: waitUntilExit(), clear, unmount };\r\n}\r\n\r\nexport async function unmountApp(handle: RenderScopeHandle): Promise<void> {\r\n handle.unmount();\r\n await new Promise((resolve) => setTimeout(resolve, 50));\r\n}\r\n","import { Text } from \"ink\";\r\nimport InkSpinner from \"ink-spinner\";\r\n\r\ninterface SpinnerProps {\r\n type?: \"dots\" | \"line\" | \"bouncingBar\" | \"aesthetic\";\r\n label?: string;\r\n}\r\n\r\nexport function Spinner({ type = \"dots\", label }: SpinnerProps) {\r\n return (\r\n <Text>\r\n <Text color=\"cyan\">\r\n <InkSpinner type={type} />\r\n </Text>\r\n {label ? <Text> {label}</Text> : null}\r\n </Text>\r\n );\r\n}\r\n","import { Box, Text } from \"ink\";\r\n\r\ntype MessageType = \"info\" | \"success\" | \"warning\" | \"error\";\r\n\r\ninterface StatusMessageProps {\r\n type?: MessageType;\r\n label: string;\r\n detail?: string;\r\n}\r\n\r\nconst STYLES: Record<MessageType, { color: string; icon: string }> = {\r\n info: { color: \"cyan\", icon: \"ℹ\" },\r\n success: { color: \"green\", icon: \"✔\" },\r\n warning: { color: \"yellow\", icon: \"⚠\" },\r\n error: { color: \"red\", icon: \"✖\" },\r\n};\r\n\r\nexport function StatusMessage({ type = \"info\", label, detail }: StatusMessageProps) {\r\n const { color, icon } = STYLES[type];\r\n return (\r\n <Box>\r\n <Text color={color}>\r\n {icon} {label}\r\n </Text>\r\n {detail ? <Text dimColor>: {detail}</Text> : null}\r\n </Box>\r\n );\r\n}\r\n","import { Box, Text } from \"ink\";\r\nimport { useEffect, useState } from \"react\";\r\n\r\n/** 赛博朋克调色板 — 多处组件复用 */\r\nexport const CYBER_PALETTE = [\"#00ffff\", \"#ff00ff\", \"#00ff41\", \"#ff1493\", \"#8b00ff\"] as const;\r\n\r\n/** ASCII LOGO 行 — 多处组件复用 */\r\nexport const LOGO_LINES = [\r\n \" ██████╗ ███████╗██╗ ██╗\",\r\n \" ██╔══██╗██╔════╝██║ ██╔╝\",\r\n \" ██║ ██║███████╗█████╔╝ \",\r\n \" ██║ ██║╚════██║██╔═██╗ \",\r\n \" ██████╔╝███████║██║ ██╗\",\r\n \" ╚═════╝ ╚══════╝╚═╝ ╚═╝\",\r\n] as const;\r\n\r\nexport function DskcodeSplash() {\r\n const [offset, setOffset] = useState(0);\r\n\r\n useEffect(() => {\r\n const timer = setInterval(() => {\r\n setOffset((prev) => (prev + 1) % CYBER_PALETTE.length);\r\n }, 500);\r\n return () => clearInterval(timer);\r\n }, []);\r\n\r\n return (\r\n <Box flexDirection=\"column\" paddingLeft={1}>\r\n {LOGO_LINES.map((line, i) => {\r\n const colorIndex = (i + offset) % CYBER_PALETTE.length;\r\n return (\r\n <Box key={i}>\r\n <Text bold color={CYBER_PALETTE[colorIndex]}>\r\n {line}\r\n </Text>\r\n </Box>\r\n );\r\n })}\r\n </Box>\r\n );\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 交互式聊天会话组件 — 接入 Agent 主循环,实现流式对话\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text, useInput, Static } from \"ink\";\r\nimport TextInput from \"ink-text-input\";\r\nimport { useEffect, useState, useCallback, useMemo, useRef } from \"react\";\r\nimport { useDoubleCtrlC } from \"./useDoubleCtrlC.js\";\r\nimport { CYBER_PALETTE, LOGO_LINES } from \"./DskcodeSplash.js\";\r\nimport InkSpinner from \"ink-spinner\";\r\nimport { AssistantMessage } from \"./AssistantMessage.js\";\r\nimport { DiffPreview } from \"./DiffPreview.js\";\r\nimport { SkillSelector } from \"./SkillSelector.js\";\r\nimport { FileSelector } from \"./FileSelector.js\";\r\nimport type { SkillInfo } from \"../cli/skill-import.js\";\r\nimport { CostTracker } from \"../provider/cost-tracker.js\";\r\nimport type { ModelId, ProviderToolCall, UsageInfo } from \"../provider/index.js\";\r\nimport type { FileDiff } from \"../tool/types.js\";\r\nimport { TodoListPanel } from \"./TodoListPanel.js\";\r\nimport type { TodoItem } from \"../harness/todo-list.js\";\r\nimport { createProvider } from \"../provider/index.js\";\r\nimport {\r\n Session,\r\n type MessageCheckpointInfo,\r\n type RewindResult,\r\n} from \"../agent/index.js\";\r\nimport type { SessionMode } from \"../agent/types.js\";\r\nimport { builtinTools } from \"../tool/index.js\";\r\nimport { ToolRegistry } from \"../tool/registry.js\";\r\nimport {\r\n IDLE_GRADIENT_STOPS,\r\n STREAMING_GRADIENT_STOPS,\r\n CMD_TIP_GRADIENT_STOPS,\r\n GRADIENT_ANIMATION,\r\n getGradientColors,\r\n} from \"../utils/gradient.js\";\r\nimport { SUPPORTED_MODELS, calculateCost } from \"../provider/models.js\";\r\nimport { saveModelConfig } from \"../config/loader.js\";\r\n\r\n/** 流式阶段配置:图标、标签、颜色 */\r\nconst PHASE_CONFIG = {\r\n thinking: { icon: \"🧠\", label: \"深度思考中\", color: \"#ff9800\" },\r\n generating: { icon: \"✨\", label: \"生成中\", color: \"#00ff41\" },\r\n calling_tools: { icon: \"🛠\", label: \"调用工具\", color: \"#f59e0b\" },\r\n executing_tools: { icon: \"⚡\", label: \"执行工具\", color: \"#00ffff\" },\r\n} as const;\r\n\r\n/** 判断工具名是否属于会修改文件系统的类(用于决定 rewind 提示是否出现) */\r\nexport function isFileMutatingTool(name: string): boolean {\r\n return (\r\n name === \"edit_file\" ||\r\n name === \"write_file\" ||\r\n name === \"multi_edit\" ||\r\n name === \"delete_range\"\r\n );\r\n}\r\n\r\n/** 命令处理结果的类型,支持文本响应和动作跳转 */\r\nexport type CommandAction =\r\n | { kind: \"text\"; content: string }\r\n | { kind: \"exit\" }\r\n | { kind: \"clear\" }\r\n | { kind: \"navigate\"; target: \"game\" | \"stock\" };\r\n\r\nexport interface ChatCommand {\r\n desc: string;\r\n handler: () => CommandAction;\r\n}\r\n\r\n/** 命令注册表,支持动态注册新命令 */\r\nconst commandRegistry = new Map<string, ChatCommand>();\r\n\r\n/** 注册一个命令 */\r\nexport function registerCommand(name: string, cmd: ChatCommand): void {\r\n commandRegistry.set(name, cmd);\r\n}\r\n\r\n/** 获取所有已注册命令(用于 /help 生成帮助文本) */\r\nfunction getRegisteredCommands(): Map<string, ChatCommand> {\r\n return commandRegistry;\r\n}\r\n\r\n// 注册内置命令\r\nregisterCommand(\"/exit\", { desc: \"退出对话\", handler: () => ({ kind: \"exit\" }) });\r\nregisterCommand(\"/quit\", { desc: \"退出对话\", handler: () => ({ kind: \"exit\" }) });\r\nregisterCommand(\"/help\", {\r\n desc: \"显示帮助信息\",\r\n handler: () => {\r\n const commands = getRegisteredCommands();\r\n const lines = [\"可用命令:\"];\r\n for (const [name, cmd] of commands) {\r\n lines.push(` ${name.padEnd(16)}${cmd.desc}`);\r\n }\r\n return { kind: \"text\", content: lines.join(\"\\n\") };\r\n },\r\n});\r\nregisterCommand(\"/clear\", { desc: \"清空对话历史\", handler: () => ({ kind: \"clear\" }) });\r\nregisterCommand(\"/version\", {\r\n desc: \"显示版本信息\",\r\n handler: () => ({ kind: \"text\", content: \"dskcode v0.1.10\" }),\r\n});\r\nregisterCommand(\"/model\", {\r\n desc: \"切换模型\",\r\n handler: () => ({ kind: \"text\", content: \"请直接输入 /model 进入选择界面\" }),\r\n});\r\nregisterCommand(\"/thinking\", {\r\n desc: \"切换深度思考模式\",\r\n handler: () => ({ kind: \"text\", content: \"请直接输入 /thinking 切换\" }),\r\n});\r\nregisterCommand(\"/effort\", {\r\n desc: \"切换推理等级 High/Max\",\r\n handler: () => ({ kind: \"text\", content: \"请直接输入 /effort 切换\" }),\r\n});\r\nregisterCommand(\"/game\", {\r\n desc: \"启动游戏\",\r\n handler: () => ({ kind: \"navigate\", target: \"game\" }),\r\n});\r\nregisterCommand(\"/stock\", {\r\n desc: \"查看股票行情\",\r\n handler: () => ({ kind: \"navigate\", target: \"stock\" }),\r\n});\r\nregisterCommand(\"/plan\", {\r\n desc: \"切换为计划模式(Shift+Tab)\",\r\n handler: () => ({ kind: \"text\", content: \"输入 /plan 或按 Shift+Tab 切换为计划模式\" }),\r\n});\r\nregisterCommand(\"/code\", {\r\n desc: \"切换回代码模式(Shift+Tab)\",\r\n handler: () => ({ kind: \"text\", content: \"输入 /code 或按 Shift+Tab 切换回代码模式\" }),\r\n});\r\nregisterCommand(\"/rewind\", {\r\n desc: \"回退到历史检查点(1 = 最新)\",\r\n handler: () => ({\r\n kind: \"text\",\r\n content:\r\n \"请直接输入 /rewind 查看可回退的检查点列表,或 /rewind <序号> 直接回退(1 = 最新,2 = 上一次,依此类推)\",\r\n }),\r\n});\r\n\r\n/** 流式输出时,输入框随机展示的占位符列表 */\r\nconst STREAMING_PLACEHOLDERS = [\r\n \"让子弹飞一会儿...\",\r\n \"马上就好...\",\r\n \"正在憋大招...\",\r\n \"稍等一下下~\",\r\n \"码字中...\",\r\n \"脑子转得飞快...\",\r\n];\r\n\r\n/** 空闲时输入框随机展示的占位符列表 */\r\nconst IDLE_PLACEHOLDERS = [\r\n \"想干啥?直接说~\",\r\n \"来吧,吩咐点啥\",\r\n \"随时待命...\",\r\n \"戳这里开聊...\",\r\n \"等你开口...\",\r\n \"尽管使唤~\",\r\n];\r\n\r\nfunction pickRandom<T>(arr: readonly T[]): T {\r\n return arr[Math.floor(Math.random() * arr.length)]!;\r\n}\r\n\r\n/** 单条已完成的助手消息 */\r\ninterface CompletedAssistant {\r\n content: string;\r\n /**\r\n * 思考链段列表(thinking 模式的 CoT),可选。\r\n * 模型一轮内可能经历多轮“思考→调工具”,每一段都保留在这里。\r\n */\r\n reasoning?: string[];\r\n toolCalls?: ProviderToolCall[];\r\n usage?: UsageInfo;\r\n elapsed?: number;\r\n cost?: number;\r\n model?: string;\r\n}\r\n\r\n/** 单条显示消息 */\r\ninterface DisplayMessage {\r\n role: \"user\" | \"assistant\" | \"tool\";\r\n content: string;\r\n /** 已完成的助手消息详情(仅在 role=assistant 时) */\r\n assistantDetail?: CompletedAssistant;\r\n /** 文件变更 diff(仅在 role=tool 且有 diff 时) */\r\n diff?: FileDiff;\r\n}\r\n\r\ninterface ChatSessionProps {\r\n skillCount: number;\r\n /** 可用 skill 详情列表(用于 / 输入时展示) */\r\n skills?: SkillInfo[];\r\n /** 项目源码文件路径列表(用于 @ 输入时展示) */\r\n files?: string[];\r\n toolCount: number;\r\n verbose: boolean;\r\n apiKey?: string;\r\n baseUrl?: string;\r\n costTracker?: CostTracker;\r\n model?: string;\r\n onLaunchGame?: () => void;\r\n onLaunchStock?: () => void;\r\n}\r\n\r\nexport function ChatSession({\r\n skillCount,\r\n skills = [],\r\n files = [],\r\n toolCount,\r\n verbose,\r\n apiKey,\r\n baseUrl,\r\n costTracker: externalCostTracker,\r\n model = \"deepseek-v4-flash\",\r\n onLaunchGame,\r\n onLaunchStock,\r\n}: ChatSessionProps) {\r\n const termWidth =\r\n typeof process.stdout.columns === \"number\" ? process.stdout.columns : 80;\r\n const dividerWidth = Math.max(termWidth - 2, 1);\r\n\r\n const [offset, setOffset] = useState(0);\r\n const [displayMessages, setDisplayMessages] = useState<DisplayMessage[]>([]);\r\n const [staticKey, setStaticKey] = useState(0);\r\n const [input, setInput] = useState(\"\");\r\n const [balance, setBalance] = useState<number | null>(null);\r\n const [balanceLoading, setBalanceLoading] = useState(false);\r\n const [todayCost, setTodayCost] = useState<number | null>(null);\r\n\r\n // 流式状态\r\n const [isStreaming, setIsStreaming] = useState(false);\r\n const [streamingPhase, setStreamingPhase] = useState<\r\n \"thinking\" | \"generating\" | \"calling_tools\" | \"executing_tools\" | null\r\n >(null);\r\n const [streamingPlaceholder, setStreamingPlaceholder] = useState(\"\");\r\n const [idlePlaceholder, setIdlePlaceholder] = useState(() =>\r\n pickRandom(IDLE_PLACEHOLDERS),\r\n );\r\n const [gradientColors, setGradientColors] = useState<string[]>([]);\r\n const gradientPhaseRef = useRef(0);\r\n const [streamingGradientColors, setStreamingGradientColors] = useState<string[]>([]);\r\n const streamingPhaseRef = useRef(0);\r\n const [currentContent, setCurrentContent] = useState(\"\");\r\n /**\r\n * 本轮已累积的思考链段列表。一轮内可能经历多轮“思考→调工具→思考”,\r\n * 每一段都 push 到这里,避免被后一段覆盖。\r\n */\r\n const [currentReasoning, setCurrentReasoning] = useState<string[]>([]);\r\n const [currentToolCalls, setCurrentToolCalls] = useState<ProviderToolCall[]>([]);\r\n const [_currentUsage, setCurrentUsage] = useState<UsageInfo | undefined>(undefined);\r\n const [_currentElapsed, setCurrentElapsed] = useState<number | undefined>(undefined);\r\n const [_currentCost, setCurrentCost] = useState<number | undefined>(undefined);\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const [activeModel, setActiveModel] = useState<ModelId>(model as unknown as ModelId);\r\n const [_streamingModel, setStreamingModel] = useState<string | undefined>(undefined);\r\n const [streamError, setStreamError] = useState<string | undefined>(undefined);\r\n // todo 任务进度面板 state — todo_* 工具结果更新此状态,<TodoListPanel> 独立渲染\r\n const [todoSnapshot, setTodoSnapshot] = useState<ReadonlyArray<TodoItem>>([]);\r\n\r\n // 会话模式(code / plan)\r\n const [sessionMode, setSessionMode] = useState<SessionMode>(\"code\");\r\n\r\n // 高级设置\r\n const [thinkingEnabled, setThinkingEnabled] = useState(true);\r\n const [thinkingEffort, setThinkingEffort] = useState<\"high\" | \"max\">(\"high\");\r\n const [responseFormat] = useState<\"text\" | \"json_object\">(\"text\");\r\n const [toolChoice, setToolChoice] = useState<\"auto\" | \"required\" | \"none\" | undefined>(\r\n undefined,\r\n );\r\n\r\n // 本次会话累计费用(从已完成的助手消息中汇总)\r\n const sessionCost = useMemo(() => {\r\n return displayMessages.reduce((sum, msg) => {\r\n if (msg.assistantDetail?.cost) {\r\n return sum + msg.assistantDetail.cost;\r\n }\r\n return sum;\r\n }, 0);\r\n }, [displayMessages]);\r\n\r\n const hasConversationStarted = displayMessages.length > 0;\r\n\r\n // 命令提示轮播索引\r\n const cmdTips = Array.from(getRegisteredCommands())\r\n .filter(([name]) => name !== \"/exit\" && name !== \"/quit\")\r\n .map(([name, cmd]) => ({ name, desc: cmd.desc }));\r\n const [cmdTipIndex, setCmdTipIndex] = useState(0);\r\n const [cmdTipGradientColors, setCmdTipGradientColors] = useState<string[]>([]);\r\n const cmdTipPhaseRef = useRef(0);\r\n\r\n // skill 选择索引(用于上下键导航)\r\n const [skillSelectIndex, setSkillSelectIndex] = useState(0);\r\n // 文件选择索引\r\n const [fileSelectIndex, setFileSelectIndex] = useState(0);\r\n // 输入框 key(补全时递增,强制 TextInput 重挂载以重置光标到末尾)\r\n const [inputKey, setInputKey] = useState(0);\r\n\r\n // /rewind 回退选择模式\r\n const [rewindSelecting, setRewindSelecting] = useState(false);\r\n const [rewindSelectIndex, setRewindSelectIndex] = useState(0);\r\n const [rewindList, setRewindList] = useState<MessageCheckpointInfo[]>([]);\r\n const [rewinding, setRewinding] = useState(false);\r\n\r\n // /rewind 提示:某轮对话修改了文件后,在原状态 loading 处显示「/rewind 1 可撤回本次修改」。\r\n // 三阶段:idle(不显示)→ pending(流式刚结束,等 2s)→ visible(展示提示)\r\n // 提示一旦出现会一直保留到下次对话开始,不会被用户输入打断。\r\n const [rewindHintPhase, setRewindHintPhase] = useState<\"idle\" | \"pending\" | \"visible\">(\r\n \"idle\",\r\n );\r\n // 本轮是否触发了文件修改类工具调用(edit_file/write_file/multi_edit/delete_range)\r\n const currentRoundModifiedRef = useRef(false);\r\n // 提示计时器句柄\r\n const rewindHintTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\r\n\r\n // 模型选择模式\r\n const [selectingModel, setSelectingModel] = useState(false);\r\n const [modelSelectIndex, setModelSelectIndex] = useState(0);\r\n const modelOptions: ModelId[] = [\"deepseek-v4-flash\", \"deepseek-v4-pro\"];\r\n\r\n // Session 引用(保持跨渲染稳定)\r\n const sessionRef = useRef<Session | null>(null);\r\n const abortRef = useRef<AbortController>(null);\r\n\r\n // 用 ref 跟踪流式内容的最新值,以便 finally 块能获取非闭包过期的值\r\n const currentContentRef = useRef(\"\");\r\n const currentReasoningRef = useRef<string[]>([]);\r\n const currentToolCallsRef = useRef<ProviderToolCall[]>([]);\r\n const currentUsageRef = useRef<UsageInfo | undefined>(undefined);\r\n const currentElapsedRef = useRef<number | undefined>(undefined);\r\n const currentCostRef = useRef<number | undefined>(undefined);\r\n const currentModelRef = useRef<string | undefined>(undefined);\r\n const streamErrorRef = useRef<string | undefined>(undefined);\r\n\r\n // 输入变更时仅重置选择索引;rewind 提示由下一轮对话开始时清掉\r\n useEffect(() => {\r\n setSkillSelectIndex(0);\r\n setFileSelectIndex(0);\r\n }, [input]);\r\n\r\n // 组件卸载时清掉 rewind 提示计时器,避免内存泄露\r\n useEffect(() => {\r\n return () => {\r\n if (rewindHintTimerRef.current) {\r\n clearTimeout(rewindHintTimerRef.current);\r\n rewindHintTimerRef.current = null;\r\n }\r\n };\r\n }, []);\r\n\r\n // 获取当前输入匹配的 skill 列表\r\n const getFilteredSkills = useCallback(\r\n (value: string) => {\r\n const match = value.match(/(?:^|\\s)\\/([^/]*)$/);\r\n if (!match) return [];\r\n const q = match[1]!.toLowerCase().trim();\r\n if (!q) {\r\n if (value.startsWith(\"/\")) return skills.slice(0, 3);\r\n return [];\r\n }\r\n const matched = skills.filter((s) => s.name.toLowerCase().includes(q)).slice(0, 3);\r\n // 精确匹配时表示已补全完成,不显示列表\r\n if (matched.some((s) => s.name.toLowerCase() === q)) return [];\r\n return matched;\r\n },\r\n [skills],\r\n );\r\n\r\n // 获取当前输入匹配的文件列表\r\n const getFilteredFiles = useCallback(\r\n (value: string) => {\r\n const match = value.match(/(?:^|\\s)@([^@]*)$/);\r\n if (!match) return [];\r\n const q = match[1]!.toLowerCase().trim();\r\n // @ 在开头且无后续内容时,展示前 5 个文件作为提示\r\n if (!q) {\r\n if (value.startsWith(\"@\")) return files.slice(0, 5);\r\n return [];\r\n }\r\n const matched = files.filter((f) => f.toLowerCase().includes(q)).slice(0, 5);\r\n // 精确匹配时表示已补全完成,不显示列表\r\n if (matched.some((f) => f.toLowerCase() === q)) return [];\r\n return matched;\r\n },\r\n [files],\r\n );\r\n\r\n // 根据 session.messages 重建 displayMessages,绕开下标对齐问题\r\n function rebuildDisplayFromSession(sess: Session): void {\r\n const next: DisplayMessage[] = [];\r\n for (const m of sess.messages) {\r\n if (m.role === \"user\") {\r\n next.push({ role: \"user\", content: m.content });\r\n } else if (m.role === \"assistant\") {\r\n next.push({\r\n role: \"assistant\",\r\n content: m.content,\r\n assistantDetail: {\r\n content: m.content,\r\n toolCalls: m.toolCalls,\r\n },\r\n });\r\n } else if (m.role === \"tool\") {\r\n next.push({ role: \"tool\", content: m.content });\r\n }\r\n // system 消息不展示\r\n }\r\n setDisplayMessages(next);\r\n setStaticKey((prev) => prev + 1);\r\n }\r\n\r\n const doRewind = useCallback(\r\n async (target: MessageCheckpointInfo, displayNumber: number) => {\r\n const session = sessionRef.current;\r\n if (!session) return;\r\n setRewinding(true);\r\n setIsStreaming(true);\r\n setStreamingPhase(\"thinking\");\r\n setStreamingPlaceholder(\"⏪ 正在回退到检查点…\");\r\n setInput(\"\");\r\n try {\r\n const r: RewindResult = await session.rewind(target.index);\r\n if (r.ok) {\r\n rebuildDisplayFromSession(session);\r\n const tail = r.fileRestored\r\n ? \",工作区文件已恢复\"\r\n : \"(仅对话回退,未恢复文件)\";\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n {\r\n role: \"assistant\",\r\n content: `⏪ 已回退到检查点 #${displayNumber}${tail}。`,\r\n },\r\n ]);\r\n } else {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"assistant\", content: `⚠ 回退失败:${r.error}` },\r\n ]);\r\n }\r\n } catch (err: unknown) {\r\n const msg = err instanceof Error ? err.message : String(err);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"assistant\", content: `⚠ 回退异常:${msg}` },\r\n ]);\r\n } finally {\r\n setRewinding(false);\r\n setIsStreaming(false);\r\n setStreamingPhase(null);\r\n setStreamingPlaceholder(\"\");\r\n }\r\n },\r\n [],\r\n );\r\n\r\n const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(() => {\r\n // 双击 Ctrl+C 退出进程 — 先刷新日志再退出\r\n const s = sessionRef.current;\r\n if (s) {\r\n void s.flushLog().finally(() => process.exit(0));\r\n } else {\r\n process.exit(0);\r\n }\r\n });\r\n\r\n // 捕获 Ctrl+C 和渐变占位符状态下的字符输入\r\n useInput(\r\n useCallback(\r\n (_input, key) => {\r\n // /rewind 选择模式\r\n if (rewindSelecting) {\r\n if (key.upArrow) {\r\n setRewindSelectIndex(\r\n (prev) => (prev - 1 + rewindList.length) % rewindList.length,\r\n );\r\n } else if (key.downArrow) {\r\n setRewindSelectIndex((prev) => (prev + 1) % rewindList.length);\r\n } else if (key.return) {\r\n const target = rewindList[rewindSelectIndex];\r\n setRewindSelecting(false);\r\n if (target) {\r\n void doRewind(target, rewindSelectIndex + 1);\r\n }\r\n } else if (key.escape) {\r\n setRewindSelecting(false);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"assistant\", content: \"已取消回退。\" },\r\n ]);\r\n }\r\n return;\r\n }\r\n\r\n // 模型选择模式\r\n if (selectingModel) {\r\n if (key.upArrow) {\r\n setModelSelectIndex(\r\n (prev) => (prev - 1 + modelOptions.length) % modelOptions.length,\r\n );\r\n } else if (key.downArrow) {\r\n setModelSelectIndex((prev) => (prev + 1) % modelOptions.length);\r\n } else if (key.return) {\r\n const selected = modelOptions[modelSelectIndex]!;\r\n if (selected === activeModel) {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n {\r\n role: \"assistant\",\r\n content: `已经在使用 ${SUPPORTED_MODELS[selected].displayName}`,\r\n },\r\n ]);\r\n } else {\r\n // 切换模型:持久化到配置 + 重置 Session\r\n setActiveModel(selected);\r\n sessionRef.current?.reset();\r\n saveModelConfig(selected).catch(() => {\r\n // 持久化失败静默处理,不影响当前会话\r\n });\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n {\r\n role: \"assistant\",\r\n content: `模型已切换为 ${SUPPORTED_MODELS[selected].displayName}(${selected})`,\r\n },\r\n ]);\r\n }\r\n setSelectingModel(false);\r\n } else if (key.escape) {\r\n setSelectingModel(false);\r\n }\r\n return;\r\n }\r\n\r\n // 文件 @ 选择模式 / skill 选择模式\r\n // 优先 @(文件匹配),没有文件匹配再走 skill 匹配\r\n const fileList = getFilteredFiles(input);\r\n const skillList = fileList.length === 0 ? getFilteredSkills(input) : [];\r\n\r\n if (fileList.length > 0) {\r\n if (key.upArrow) {\r\n setFileSelectIndex((prev) => (prev - 1 + fileList.length) % fileList.length);\r\n return;\r\n }\r\n if (key.downArrow) {\r\n setFileSelectIndex((prev) => (prev + 1) % fileList.length);\r\n return;\r\n }\r\n // Tab 补全选中的文件\r\n if (key.tab) {\r\n const selected = fileList[fileSelectIndex];\r\n if (selected) {\r\n const atIdx = input.lastIndexOf(\"@\");\r\n if (atIdx >= 0) {\r\n setInput(input.slice(0, atIdx) + \"@\" + selected + \" \");\r\n setInputKey((k) => k + 1);\r\n }\r\n }\r\n return;\r\n }\r\n }\r\n\r\n if (skillList.length > 0) {\r\n if (key.upArrow) {\r\n setSkillSelectIndex(\r\n (prev) => (prev - 1 + skillList.length) % skillList.length,\r\n );\r\n return;\r\n }\r\n if (key.downArrow) {\r\n setSkillSelectIndex((prev) => (prev + 1) % skillList.length);\r\n return;\r\n }\r\n // Tab 补全选中的 skill(补全后光标自动移至末尾)\r\n if (key.tab) {\r\n const selected = skillList[skillSelectIndex];\r\n if (selected) {\r\n // 只替换最后一个 / 之后的部分,保留之前输入的内容\r\n // 用 lastIndexOf 定位 / 的精确位置,确保前置空格不被吃掉\r\n const slashIdx = input.lastIndexOf(\"/\");\r\n if (slashIdx >= 0) {\r\n // 补全后加空格分隔,方便继续输入下一个 skill 或问题\r\n setInput(input.slice(0, slashIdx) + \"/\" + selected.name + \" \");\r\n setInputKey((k) => k + 1);\r\n }\r\n }\r\n return;\r\n }\r\n }\r\n\r\n // 模型选择 / 设置选择模式下的通用键盘处理\r\n if (selectingModel) {\r\n if (key.upArrow || key.downArrow) {\r\n // 已在模型选择模式下处理上下键\r\n }\r\n return;\r\n }\r\n\r\n if (key.ctrl && _input === \"c\") {\r\n if (isStreaming) {\r\n abortRef.current?.abort();\r\n } else {\r\n handleCtrlC();\r\n }\r\n return;\r\n }\r\n\r\n // Shift+Tab 切换计划模式 / 代码模式\r\n if (key.shift && key.tab) {\r\n if (!isStreaming) {\r\n const newMode: SessionMode = sessionMode === \"plan\" ? \"code\" : \"plan\";\r\n setSessionMode(newMode);\r\n sessionRef.current?.setMode(newMode);\r\n setToolChoice(undefined);\r\n sessionRef.current?.reset();\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n {\r\n role: \"assistant\",\r\n content:\r\n newMode === \"plan\"\r\n ? \"📋 已切换为 **计划模式**(Shift+Tab)\\n\\n在此模式下,我只能读取和分析代码,不会执行任何修改。\"\r\n : \"🛠 已切换为 **代码模式**(Shift+Tab)\\n\\n现在可以正常读取和修改代码了。\",\r\n },\r\n ]);\r\n }\r\n return;\r\n }\r\n\r\n // 渐变占位符显示时(TextInput 未渲染),将按键字符加入 input 触发切换\r\n if (!input && !isStreaming && _input) {\r\n setInput(_input);\r\n }\r\n },\r\n [\r\n selectingModel,\r\n modelSelectIndex,\r\n modelOptions,\r\n activeModel,\r\n isStreaming,\r\n handleCtrlC,\r\n input,\r\n skills,\r\n skillSelectIndex,\r\n fileSelectIndex,\r\n getFilteredSkills,\r\n getFilteredFiles,\r\n sessionMode,\r\n setSessionMode,\r\n rewindSelecting,\r\n rewindSelectIndex,\r\n rewindList,\r\n doRewind,\r\n ],\r\n ),\r\n );\r\n\r\n // 命令提示轮播 — 每 2 秒切换下一条\r\n useEffect(() => {\r\n if (cmdTips.length <= 1) return;\r\n const timer = setInterval(() => {\r\n setCmdTipIndex((prev) => (prev + 1) % cmdTips.length);\r\n }, 2000);\r\n return () => clearInterval(timer);\r\n }, [cmdTips.length]);\r\n\r\n // 命令提示条渐变动画(反向流动:1 - phase)\r\n useEffect(() => {\r\n const tip = cmdTips[cmdTipIndex % cmdTips.length];\r\n if (!tip) {\r\n setCmdTipGradientColors([]);\r\n return;\r\n }\r\n const text = `${tip.name} ${tip.desc}`;\r\n cmdTipPhaseRef.current = 0;\r\n setCmdTipGradientColors(getGradientColors(text, 1, CMD_TIP_GRADIENT_STOPS));\r\n\r\n const interval = setInterval(() => {\r\n cmdTipPhaseRef.current =\r\n (cmdTipPhaseRef.current + GRADIENT_ANIMATION.cmdTipPhaseStep) % 1;\r\n // 反向 phase 使色彩从左到右流动\r\n setCmdTipGradientColors(\r\n getGradientColors(text, 1 - cmdTipPhaseRef.current, CMD_TIP_GRADIENT_STOPS),\r\n );\r\n }, GRADIENT_ANIMATION.cmdTipInterval);\r\n\r\n return () => clearInterval(interval);\r\n }, [cmdTipIndex, cmdTips.length]);\r\n\r\n // Logo 色彩动画\r\n useEffect(() => {\r\n const timer = setInterval(() => {\r\n setOffset((prev) => (prev + 1) % CYBER_PALETTE.length);\r\n }, 500);\r\n return () => clearInterval(timer);\r\n }, []);\r\n\r\n // 初始化 Session(当模型切换时重建)\r\n useEffect(() => {\r\n if (!apiKey || !baseUrl) return;\r\n\r\n const provider = createProvider({\r\n name: \"deepseek\",\r\n apiKey,\r\n baseUrl,\r\n model: activeModel,\r\n });\r\n\r\n const tracker = externalCostTracker ?? new CostTracker();\r\n // 注册所有内置工具\r\n const toolRegistry = new ToolRegistry();\r\n toolRegistry.registerAll(builtinTools);\r\n const session = new Session(provider, toolRegistry, tracker, {\r\n cwd: process.cwd(),\r\n });\r\n sessionRef.current = session;\r\n\r\n return () => {\r\n sessionRef.current = null;\r\n };\r\n }, [apiKey, baseUrl, activeModel, externalCostTracker]);\r\n\r\n // 查询余额\r\n useEffect(() => {\r\n if (!apiKey || !baseUrl) return;\r\n let cancelled = false;\r\n setBalanceLoading(true);\r\n import(\"../provider/deepseek.js\")\r\n .then(({ DeepSeekProvider }) => {\r\n const provider = new DeepSeekProvider({\r\n apiKey,\r\n baseUrl,\r\n model: \"deepseek-v4-flash\",\r\n });\r\n return provider.getBalance();\r\n })\r\n .then((result) => {\r\n if (cancelled) return;\r\n const cny = result.balances.find((b) => b.currency === \"CNY\");\r\n if (cny) {\r\n setBalance(cny.totalBalance);\r\n }\r\n })\r\n .catch(() => {\r\n // 查询失败静默处理,不影响主流程\r\n })\r\n .finally(() => {\r\n if (!cancelled) setBalanceLoading(false);\r\n });\r\n return () => {\r\n cancelled = true;\r\n };\r\n }, [apiKey, baseUrl]);\r\n\r\n // 加载今日消耗历史数据,并定时刷新\r\n useEffect(() => {\r\n if (!externalCostTracker) return;\r\n let cancelled = false;\r\n let timer: ReturnType<typeof setInterval>;\r\n\r\n const refresh = () => {\r\n setTodayCost(externalCostTracker.todayTotalCost);\r\n };\r\n\r\n externalCostTracker\r\n .load()\r\n .then(() => {\r\n if (cancelled) return;\r\n refresh();\r\n timer = setInterval(refresh, 5000);\r\n })\r\n .catch(() => {\r\n // 加载失败静默处理\r\n });\r\n\r\n return () => {\r\n cancelled = true;\r\n if (timer) clearInterval(timer);\r\n };\r\n }, [externalCostTracker]);\r\n\r\n // 空闲占位符渐变动画\r\n useEffect(() => {\r\n if (isStreaming || !idlePlaceholder) {\r\n setGradientColors([]);\r\n return;\r\n }\r\n\r\n gradientPhaseRef.current = 0;\r\n setGradientColors(getGradientColors(idlePlaceholder, 1, IDLE_GRADIENT_STOPS));\r\n\r\n const interval = setInterval(() => {\r\n gradientPhaseRef.current =\r\n (gradientPhaseRef.current + GRADIENT_ANIMATION.idlePhaseStep) % 1;\r\n setGradientColors(\r\n getGradientColors(\r\n idlePlaceholder,\r\n 1 - gradientPhaseRef.current,\r\n IDLE_GRADIENT_STOPS,\r\n ),\r\n );\r\n }, GRADIENT_ANIMATION.idleInterval);\r\n\r\n return () => clearInterval(interval);\r\n }, [isStreaming, idlePlaceholder]);\r\n\r\n // 流式占位符渐变动画\r\n useEffect(() => {\r\n if (!isStreaming || !streamingPlaceholder) {\r\n setStreamingGradientColors([]);\r\n return;\r\n }\r\n\r\n streamingPhaseRef.current = 0;\r\n setStreamingGradientColors(\r\n getGradientColors(streamingPlaceholder, 1, STREAMING_GRADIENT_STOPS),\r\n );\r\n\r\n const interval = setInterval(() => {\r\n streamingPhaseRef.current =\r\n (streamingPhaseRef.current + GRADIENT_ANIMATION.streamingPhaseStep) % 1;\r\n setStreamingGradientColors(\r\n getGradientColors(\r\n streamingPlaceholder,\r\n 1 - streamingPhaseRef.current,\r\n STREAMING_GRADIENT_STOPS,\r\n ),\r\n );\r\n }, GRADIENT_ANIMATION.streamingInterval);\r\n\r\n return () => clearInterval(interval);\r\n }, [isStreaming, streamingPlaceholder]);\r\n\r\n /** 处理用户输入 */\r\n const handleSubmit = useCallback(\r\n async (value: string) => {\r\n const trimmed = value.trim();\r\n if (!trimmed) return;\r\n\r\n // 处理斜杠命令\r\n if (trimmed.startsWith(\"/\") && trimmed.length > 1) {\r\n const cmdLower = trimmed.toLowerCase();\r\n\r\n // /rewind 命令:回退到历史检查点\r\n if (cmdLower === \"/rewind\" || cmdLower.startsWith(\"/rewind \")) {\r\n if (isStreaming || rewinding) {\r\n const reason = rewinding ? \"回退中\" : \"生成中\";\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: `⚠ 正在${reason},请稍后再试 /rewind。` },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n if (!sessionRef.current) {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: \"⚠ Session 未就绪,无法回退。\" },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n const cps = sessionRef.current.listCheckpoints();\r\n if (cps.length === 0) {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n {\r\n role: \"assistant\",\r\n content:\r\n \"⚠ 没有可回退的检查点。\\n只有在 git 仓库内且发生过对话后才会生成检查点。\",\r\n },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n // /rewind <N>:1 表示最新(与 git reset HEAD~ 习惯一致),2 表示上一个,依次类推\r\n const parts = trimmed.split(/\\s+/);\r\n if (parts.length >= 2) {\r\n const n = Number(parts[1]);\r\n if (!Number.isInteger(n) || n < 1 || n > cps.length) {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n {\r\n role: \"assistant\",\r\n content: `⚠ 无效的序号「${parts[1]}」。可用范围 1~${cps.length}(1 表示最新)。`,\r\n },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n // 将用户输入的 1-based \"最新优先\"序号转换为 cps 列表里的索引\r\n const target = cps[cps.length - n]!;\r\n setInput(\"\");\r\n await doRewind(target, n);\r\n return;\r\n }\r\n // 无参数:进入选择模式。逆序展示(最新在第 1 位),默认选中最新\r\n setRewindList([...cps].reverse());\r\n setRewindSelectIndex(0);\r\n setRewindSelecting(true);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n {\r\n role: \"assistant\",\r\n content: `⏷↑↓ 选择检查点,Enter 确认,Esc 取消(共 ${cps.length} 个可回退位置)`,\r\n },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n // /model 命令:进入模型选择模式\r\n if (cmdLower === \"/model\") {\r\n const curIdx = modelOptions.indexOf(activeModel);\r\n setModelSelectIndex(curIdx >= 0 ? curIdx : 0);\r\n setSelectingModel(true);\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n // /thinking 命令:切换深度思考\r\n if (cmdLower === \"/thinking\") {\r\n setThinkingEnabled((prev) => !prev);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n {\r\n role: \"assistant\",\r\n content: `深度思考已${thinkingEnabled ? \"关闭\" : \"开启\"}`,\r\n },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n // /effort 命令:切换推理等级\r\n if (cmdLower === \"/effort\") {\r\n const next = thinkingEffort === \"high\" ? \"max\" : \"high\";\r\n setThinkingEffort(next);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n {\r\n role: \"assistant\",\r\n content: `推理等级已切换为 ${next === \"high\" ? \"High\" : \"Max\"}`,\r\n },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n // /plan 命令:切换为计划模式(只读分析,不能修改代码)\r\n if (cmdLower === \"/plan\") {\r\n if (sessionMode === \"plan\") {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n {\r\n role: \"assistant\",\r\n content: \"已经在计划模式中。输入 /code 切回代码模式。\",\r\n },\r\n ]);\r\n } else {\r\n setSessionMode(\"plan\");\r\n sessionRef.current?.setMode(\"plan\");\r\n // 计划模式下自动启用工具调用,确保模型可以使用读工具\r\n setToolChoice(undefined);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n {\r\n role: \"assistant\",\r\n content:\r\n \"📋 已切换为 **计划模式**\\n\\n在此模式下,我只能读取和分析代码,不会执行任何修改。\\n输入 /code 切回代码模式。\",\r\n },\r\n ]);\r\n // 清空会话历史,防止旧模式下产生的工具调用消息干扰新模式\r\n sessionRef.current?.reset();\r\n }\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n // /code 命令:切换回代码模式\r\n if (cmdLower === \"/code\") {\r\n if (sessionMode === \"code\") {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n {\r\n role: \"assistant\",\r\n content: \"已经在代码模式中。输入 /plan 切回计划模式。\",\r\n },\r\n ]);\r\n } else {\r\n setSessionMode(\"code\");\r\n sessionRef.current?.setMode(\"code\");\r\n // 回到代码模式时重置工具调用策略,确保可以正常使用所有工具\r\n setToolChoice(undefined);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n {\r\n role: \"assistant\",\r\n content:\r\n \"🛠 已切换为 **代码模式**\\n\\n现在可以正常读取和修改代码了。\\n输入 /plan 切换为计划模式(只读分析)。\",\r\n },\r\n ]);\r\n // 清空会话历史,防止计划模式下产生的消息干扰代码模式\r\n sessionRef.current?.reset();\r\n }\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n // /tools 命令:切换工具调用策略\r\n if (cmdLower === \"/tools\") {\r\n const next =\r\n toolChoice === undefined\r\n ? \"none\"\r\n : toolChoice === \"none\"\r\n ? \"required\"\r\n : toolChoice === \"required\"\r\n ? \"auto\"\r\n : undefined;\r\n setToolChoice(next);\r\n const label =\r\n next === undefined\r\n ? \"自动(默认)\"\r\n : next === \"none\"\r\n ? \"禁止调用\"\r\n : next === \"required\"\r\n ? \"强制调用\"\r\n : \"自动(默认)\";\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: `工具调用策略已切换为 ${label}` },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n const cmd = commandRegistry.get(cmdLower);\r\n if (cmd) {\r\n const result = cmd.handler();\r\n\r\n switch (result.kind) {\r\n case \"exit\":\r\n await sessionRef.current?.flushLog();\r\n process.exit(0);\r\n return;\r\n case \"clear\":\r\n // 清空所有会话状态,回到初始界面\r\n setDisplayMessages([]);\r\n setStaticKey((prev) => prev + 1);\r\n setInput(\"\");\r\n setStreamError(undefined);\r\n // 先清屏再清滚动区,确保终端完全重置\r\n process.stdout.write(\"\\x1b[2J\\x1b[H\\x1b[3J\");\r\n // 重置 Session 历史\r\n sessionRef.current?.reset();\r\n return;\r\n case \"navigate\":\r\n setInput(\"\");\r\n if (result.target === \"game\") {\r\n onLaunchGame?.();\r\n } else if (result.target === \"stock\") {\r\n onLaunchStock?.();\r\n }\r\n return;\r\n case \"text\":\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: result.content },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n }\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: `未知命令:${trimmed}。输入 /help 查看。` },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n // 检查 Session 是否就绪\r\n if (!sessionRef.current) {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n {\r\n role: \"assistant\",\r\n content: \"⚠ 无法连接到 Provider。请检查 API Key 和网络配置。\",\r\n },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n // ---- 正常对话:接入 Agent 流式主循环 ----\r\n\r\n // 追加用户消息到显示列表\r\n setDisplayMessages((prev) => [...prev, { role: \"user\", content: trimmed }]);\r\n\r\n // 进入流式状态\r\n setInput(\"\");\r\n // 重置流式状态\r\n setIsStreaming(true);\r\n setStreamingPhase(\"thinking\");\r\n setStreamingPlaceholder(pickRandom(STREAMING_PLACEHOLDERS));\r\n setCurrentContent(\"\");\r\n setCurrentReasoning([]);\r\n setCurrentToolCalls([]);\r\n setCurrentUsage(undefined);\r\n setCurrentElapsed(undefined);\r\n setCurrentCost(undefined);\r\n setStreamingModel(undefined);\r\n setStreamError(undefined);\r\n // 同步重置 ref\r\n currentContentRef.current = \"\";\r\n currentReasoningRef.current = [];\r\n currentToolCallsRef.current = [];\r\n currentUsageRef.current = undefined;\r\n currentElapsedRef.current = undefined;\r\n currentCostRef.current = undefined;\r\n currentModelRef.current = undefined;\r\n streamErrorRef.current = undefined;\r\n // 新一轮对话开始:清除上轮可能残留的 rewind 提示\r\n currentRoundModifiedRef.current = false;\r\n setRewindHintPhase(\"idle\");\r\n if (rewindHintTimerRef.current) {\r\n clearTimeout(rewindHintTimerRef.current);\r\n rewindHintTimerRef.current = null;\r\n }\r\n\r\n const session = sessionRef.current;\r\n const abortController = new AbortController();\r\n abortRef.current = abortController;\r\n\r\n try {\r\n for await (const event of session.chat(trimmed, {\r\n thinkingAllowed: thinkingEnabled || undefined,\r\n thinkingEffort: thinkingEnabled ? thinkingEffort : undefined,\r\n responseFormat: responseFormat !== \"text\" ? responseFormat : undefined,\r\n toolChoice,\r\n })) {\r\n // 如果请求被取消,跳过后续事件\r\n if (abortController.signal.aborted) break;\r\n\r\n switch (event.type) {\r\n case \"text_delta\":\r\n setStreamingPhase(\"generating\");\r\n setCurrentContent((prev) => {\r\n const next = prev + event.content;\r\n currentContentRef.current = next;\r\n return next;\r\n });\r\n break;\r\n\r\n case \"reasoning_delta\":\r\n // 思考链仍处于 thinking 阶段,UI 上以暗色块单独展示\r\n // 连续多段之间如果间隔了 text/tool 会被切为多个独立段,避免合并\r\n setStreamingPhase(\"thinking\");\r\n setCurrentReasoning((prev) => {\r\n const last = prev[prev.length - 1];\r\n const next =\r\n last !== undefined\r\n ? [...prev.slice(0, -1), last + event.content]\r\n : [...prev, event.content];\r\n currentReasoningRef.current = next;\r\n return next;\r\n });\r\n break;\r\n\r\n case \"tool_calls\":\r\n setStreamingPhase(\"calling_tools\");\r\n setCurrentToolCalls((prev) => {\r\n const next = [...prev, ...event.calls];\r\n currentToolCallsRef.current = next;\r\n return next;\r\n });\r\n // 标记本轮是否触发了文件修改类工具调用\r\n for (const call of event.calls) {\r\n if (isFileMutatingTool(call.name)) {\r\n currentRoundModifiedRef.current = true;\r\n break;\r\n }\r\n }\r\n break;\r\n\r\n case \"tool_result\":\r\n // 工具执行完成 — 重置流式状态,准备接收模型的新一轮回复\r\n // 这是因为 Agent 循环会在工具执行后再次调用模型\r\n setStreamingPhase(\"executing_tools\");\r\n // 延迟重置为思考中,表示模型正在处理工具结果进行下一轮推理\r\n setTimeout(() => setStreamingPhase(\"thinking\"), 300);\r\n setCurrentContent(\"\");\r\n currentContentRef.current = \"\";\r\n // 思考链:工具结果代表一个 sub-turn 结束,把“正在累积的那一段”封口(推 \"\" 占位),\r\n // 下一段思考会 push 到数组末尾,原有前几段保留可见。\r\n setCurrentReasoning((prev) => {\r\n const next = [...prev, \"\"];\r\n currentReasoningRef.current = next;\r\n return next;\r\n });\r\n setCurrentToolCalls([]);\r\n currentToolCallsRef.current = [];\r\n // 将工具结果追加为一条用户可见的消息\r\n const r = event.result;\r\n if (event.name.startsWith(\"todo_\") && event.todoSnapshot) {\r\n // todo_* 工具:更新独立的任务进度 state(覆盖而非追加),不产生消息条目\r\n setTodoSnapshot(event.todoSnapshot);\r\n } else {\r\n // 非 todo 工具:优先使用 summary(一行简短摘要),避免在 UI 中撑出大段文件内容\r\n const line = r.success\r\n ? (r.summary ??\r\n `✅ ${event.name}: ${r.data.slice(0, 500)}${r.data.length > 500 ? \"...\" : \"\"}`)\r\n : `❌ ${event.name}: ${r.error ?? \"执行失败\"}`;\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n {\r\n role: \"tool\" as const,\r\n content: line,\r\n diff: r.diff,\r\n },\r\n ]);\r\n }\r\n break;\r\n\r\n case \"usage\":\r\n setCurrentUsage(event.usage);\r\n setStreamingModel(event.model);\r\n currentUsageRef.current = event.usage;\r\n currentModelRef.current = event.model;\r\n // 同步计算费用\r\n {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const cost = calculateCost(\r\n event.usage,\r\n event.model as unknown as ModelId,\r\n );\r\n setCurrentCost(cost.totalCost);\r\n currentCostRef.current = cost.totalCost;\r\n }\r\n break;\r\n\r\n case \"done\":\r\n setCurrentElapsed(event.elapsed);\r\n currentElapsedRef.current = event.elapsed;\r\n break;\r\n\r\n case \"error\":\r\n setStreamError(event.error.message);\r\n streamErrorRef.current = event.error.message;\r\n break;\r\n }\r\n }\r\n } catch (err: unknown) {\r\n const msg = err instanceof Error ? err.message : String(err);\r\n setStreamError(msg);\r\n streamErrorRef.current = msg;\r\n } finally {\r\n setIsStreaming(false);\r\n setStreamingPhase(null);\r\n setIdlePlaceholder(pickRandom(IDLE_PLACEHOLDERS));\r\n abortRef.current = null;\r\n\r\n // 流式结束后,用 ref 拿到最新值,直接追加完成的助手消息\r\n const finContent = currentContentRef.current;\r\n // 过滤掉占位的空串和首尾空白,避免在 UI 上生成空白思考块\r\n const finReasoning = currentReasoningRef.current\r\n .map((s) => s.trim())\r\n .filter((s) => s.length > 0);\r\n const finToolCalls =\r\n currentToolCallsRef.current.length > 0\r\n ? currentToolCallsRef.current\r\n : undefined;\r\n const finStreamError = streamErrorRef.current;\r\n\r\n if (finContent || finToolCalls || finStreamError) {\r\n const completed: CompletedAssistant = {\r\n content: finStreamError ? `⚠ 请求出错:${finStreamError}` : finContent || \"\",\r\n ...(finReasoning.length > 0 ? { reasoning: finReasoning } : {}),\r\n toolCalls: finToolCalls,\r\n usage: currentUsageRef.current,\r\n elapsed: currentElapsedRef.current,\r\n cost: currentCostRef.current,\r\n model: currentModelRef.current,\r\n };\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n {\r\n role: \"assistant\" as const,\r\n content: completed.content,\r\n assistantDetail: completed,\r\n },\r\n ]);\r\n }\r\n\r\n // 如果本轮触发了文件修改且无流错误 -> 2s 后展示 rewind 提示\r\n // 提示会一直保留到下次对话开始(流式再次启动)为止\r\n if (currentRoundModifiedRef.current && !finStreamError) {\r\n setRewindHintPhase(\"pending\");\r\n if (rewindHintTimerRef.current) clearTimeout(rewindHintTimerRef.current);\r\n rewindHintTimerRef.current = setTimeout(() => {\r\n setRewindHintPhase((prev) => (prev === \"pending\" ? \"visible\" : prev));\r\n rewindHintTimerRef.current = null;\r\n }, 2000);\r\n }\r\n }\r\n },\r\n [\r\n onLaunchGame,\r\n onLaunchStock,\r\n currentContent,\r\n currentReasoning,\r\n currentToolCalls,\r\n skills,\r\n skillSelectIndex,\r\n getFilteredSkills,\r\n thinkingEnabled,\r\n thinkingEffort,\r\n responseFormat,\r\n toolChoice,\r\n activeModel,\r\n sessionMode,\r\n isStreaming,\r\n rewinding,\r\n ],\r\n );\r\n\r\n // 从 costTracker 更新今日消耗(每次流式结束后刷新)\r\n useEffect(() => {\r\n if (!isStreaming && externalCostTracker) {\r\n setTodayCost(externalCostTracker.todayTotalCost);\r\n }\r\n }, [isStreaming, externalCostTracker]);\r\n\r\n return (\r\n <Box flexDirection=\"column\" paddingLeft={1} paddingRight={1}>\r\n {/* Logo + 状态栏 + 余额 — 三栏布局(仅对话未开始时显示) */}\r\n {!hasConversationStarted && (\r\n <Box flexDirection=\"row\" marginBottom={1}>\r\n {/* Logo */}\r\n <Box flexDirection=\"column\" marginRight={4}>\r\n {LOGO_LINES.map((line, i) => {\r\n const colorIndex = (i + offset) % CYBER_PALETTE.length;\r\n return (\r\n <Box key={i}>\r\n <Text bold color={CYBER_PALETTE[colorIndex]}>\r\n {line}\r\n </Text>\r\n </Box>\r\n );\r\n })}\r\n </Box>\r\n\r\n {/* 状态信息 */}\r\n <Box flexDirection=\"column\" justifyContent=\"center\">\r\n <Text color=\"#00ff41\">\r\n {\" ✔ \"}已就绪 {skillCount} 个 Skill\r\n </Text>\r\n <Text color=\"#00ffff\">\r\n {\" ℹ \"}已就绪 {toolCount} 个工具\r\n </Text>\r\n <Text color=\"#00ffff\">\r\n {\" 🔧 模型 \"}\r\n {SUPPORTED_MODELS[activeModel]?.displayName ?? activeModel}\r\n </Text>\r\n {thinkingEnabled && (\r\n <Text color=\"#ff9800\">\r\n {\" 🧠 深度思考 \"}\r\n {thinkingEffort === \"max\" ? \"Max\" : \"High\"}\r\n </Text>\r\n )}\r\n {/* 模式指示器 */}\r\n {sessionMode === \"plan\" && (\r\n <Text color=\"#ff69b4\" bold>\r\n {\" 📋 计划模式\"}\r\n </Text>\r\n )}\r\n {responseFormat === \"json_object\" && (\r\n <Text color=\"#4caf50\">{\" 📄 JSON\"}</Text>\r\n )}\r\n {toolChoice !== undefined && (\r\n <Text color=\"#e91e63\">\r\n {\" 🛠 \"}\r\n {toolChoice === \"none\"\r\n ? \"禁止工具\"\r\n : toolChoice === \"required\"\r\n ? \"强制工具\"\r\n : \"\"}\r\n </Text>\r\n )}\r\n {/* 命令提示轮播 */}\r\n {cmdTips.length > 0 &&\r\n (() => {\r\n const tip = cmdTips[cmdTipIndex % cmdTips.length];\r\n if (!tip) return null;\r\n const text = `${tip.name} ${tip.desc}`;\r\n return (\r\n <Text>\r\n <Text color=\"#808080\">{\" 💡 \"}</Text>\r\n {cmdTipGradientColors.length > 0 ? (\r\n text.split(\"\").map((ch, i) => (\r\n <Text key={i} color={cmdTipGradientColors[i] || undefined}>\r\n {ch}\r\n </Text>\r\n ))\r\n ) : (\r\n <Text color=\"#808080\">{text}</Text>\r\n )}\r\n </Text>\r\n );\r\n })()}\r\n {verbose ? <Text color=\"#ff1493\">{\" ⚡ Verbose\"}</Text> : null}\r\n </Box>\r\n\r\n {/* 右侧余额 + 今日消耗 */}\r\n <Box\r\n flexGrow={1}\r\n flexDirection=\"column\"\r\n justifyContent=\"center\"\r\n alignItems=\"flex-end\"\r\n >\r\n {balanceLoading && balance === null ? (\r\n <Text color=\"yellow\">{\" ⏳ 查询余额...\"}</Text>\r\n ) : balance !== null ? (\r\n <Box flexDirection=\"row\">\r\n <Text color=\"yellow\">{\"💰 \"}</Text>\r\n <Text color=\"yellow\">\r\n {\"余额 ¥\"}\r\n {balance.toFixed(2)}\r\n </Text>\r\n </Box>\r\n ) : null}\r\n {todayCost !== null ? (\r\n <Box flexDirection=\"row\">\r\n <Text color=\"cyan\">{\"📊 \"}</Text>\r\n <Text color=\"cyan\">\r\n {\"今日 ¥\"}\r\n {todayCost.toFixed(2)}\r\n </Text>\r\n </Box>\r\n ) : null}\r\n </Box>\r\n </Box>\r\n )}\r\n\r\n {/* 消息列表 - 已完成的消息用 Static 固定,避免重绘时丢失滚动位置 */}\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n <Static key={staticKey} items={displayMessages}>\r\n {(msg, i) => {\r\n if (msg.role === \"user\") {\r\n return (\r\n <Box key={i} marginTop={1}>\r\n <Box width={4} flexShrink={0}>\r\n <Text bold color=\"#00ff41\">\r\n {\"👤\"}\r\n </Text>\r\n </Box>\r\n <Box flexGrow={1}>\r\n <Text wrap=\"wrap\">{msg.content}</Text>\r\n </Box>\r\n </Box>\r\n );\r\n }\r\n\r\n // 工具消息 — 显示文本内容 + Diff 预览\r\n // 工具调用属于“过程信息”,使用 dimColor 让主内容(助手回复)的视觉权重更高\r\n if (msg.role === \"tool\") {\r\n return (\r\n <Box key={i} marginTop={1} flexDirection=\"column\">\r\n <Box flexDirection=\"row\">\r\n <Box width={4} flexShrink={0}>\r\n <Text dimColor>{\"🔧\"}</Text>\r\n </Box>\r\n <Box flexGrow={1}>\r\n <Text dimColor wrap=\"wrap\">\r\n {msg.content}\r\n </Text>\r\n </Box>\r\n </Box>\r\n {msg.diff && <DiffPreview diff={msg.diff} />}\r\n </Box>\r\n );\r\n }\r\n\r\n // 已完成的助手消息\r\n const detail = msg.assistantDetail;\r\n return (\r\n <AssistantMessage\r\n key={i}\r\n content={msg.content}\r\n reasoning={detail?.reasoning}\r\n toolCalls={detail?.toolCalls}\r\n isStreaming={false}\r\n usage={detail?.usage}\r\n elapsed={detail?.elapsed}\r\n cost={detail?.cost}\r\n model={detail?.model}\r\n />\r\n );\r\n }}\r\n </Static>\r\n\r\n {/* 正在流式输出的助手消息 */}\r\n {isStreaming && (\r\n <AssistantMessage\r\n content={currentContent}\r\n reasoning={currentReasoning}\r\n toolCalls={currentToolCalls.length > 0 ? currentToolCalls : undefined}\r\n isStreaming={true}\r\n usage={_currentUsage}\r\n cost={_currentCost}\r\n model={_streamingModel}\r\n />\r\n )}\r\n\r\n {/* 错误信息(流式结束后显示) */}\r\n {!isStreaming && streamError && (\r\n <Box marginTop={1} marginLeft={3}>\r\n <Text color=\"red\">⚠ {streamError}</Text>\r\n </Box>\r\n )}\r\n </Box>\r\n\r\n {/* 独立的任务进度面板 — 不混入消息列表,每次 todo_* 刷新原地更新 */}\r\n {todoSnapshot.length > 0 && <TodoListPanel items={todoSnapshot} />}\r\n\r\n {/* 输入区 */}\r\n {selectingModel ? (\r\n <Box marginTop={1} flexDirection=\"column\">\r\n <Text color=\"#00ffff\" dimColor>\r\n {\"─\".repeat(dividerWidth)}\r\n </Text>\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n <Text bold color=\"#00ffff\">\r\n 选择模型:\r\n </Text>\r\n {modelOptions.map((id, i) => {\r\n const meta = SUPPORTED_MODELS[id];\r\n const isCurrent = id === activeModel;\r\n const isSelected = i === modelSelectIndex;\r\n const marker = isSelected ? \" > \" : \" \";\r\n const suffix = isCurrent ? \" (当前)\" : \"\";\r\n return (\r\n <Box key={id}>\r\n <Text color={isSelected ? \"#00ff41\" : undefined} bold={isSelected}>\r\n {marker}\r\n {meta.displayName}\r\n {suffix}\r\n </Text>\r\n {isSelected && <Text color=\"#808080\"> — {id}</Text>}\r\n </Box>\r\n );\r\n })}\r\n <Box marginTop={1}>\r\n <Text color=\"#808080\" dimColor>\r\n ↑↓ 选择 · Enter 确认 · Esc 取消\r\n </Text>\r\n </Box>\r\n </Box>\r\n <Text color=\"#00ffff\" dimColor>\r\n {\"─\".repeat(dividerWidth)}\r\n </Text>\r\n </Box>\r\n ) : rewindSelecting ? (\r\n <Box marginTop={1} flexDirection=\"column\">\r\n <Text color=\"#00ffff\" dimColor>\r\n {\"─\".repeat(dividerWidth)}\r\n </Text>\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n <Text bold color=\"#ff9800\">\r\n 选择要回退的检查点:\r\n </Text>\r\n {rewindList.map((cp, i) => {\r\n const isSelected = i === rewindSelectIndex;\r\n const marker = isSelected ? \" > \" : \" \";\r\n const time = new Date(cp.timestamp).toLocaleTimeString();\r\n const preview = cp.preview || \"(空)\";\r\n const tag = cp.isGitRepo ? \"\" : \" [非 git,仅对话]\";\r\n return (\r\n <Box key={cp.index}>\r\n <Text color={isSelected ? \"#ff9800\" : undefined} bold={isSelected}>\r\n {marker}#{i + 1} {time} `{preview}\r\n {tag}`\r\n </Text>\r\n </Box>\r\n );\r\n })}\r\n <Box marginTop={1}>\r\n <Text color=\"#808080\" dimColor>\r\n ↑↓ 选择 · Enter 确认 · Esc 取消\r\n </Text>\r\n </Box>\r\n </Box>\r\n <Text color=\"#00ffff\" dimColor>\r\n {\"─\".repeat(dividerWidth)}\r\n </Text>\r\n </Box>\r\n ) : (\r\n <>\r\n {/* 流式状态指示器 — 上框上方居中显示 */}\r\n {(hasConversationStarted || sessionMode === \"plan\") &&\r\n isStreaming &&\r\n streamingPhase ? (\r\n <Box marginTop={1} justifyContent=\"center\">\r\n <Text bold color={PHASE_CONFIG[streamingPhase].color}>\r\n {PHASE_CONFIG[streamingPhase].icon} {PHASE_CONFIG[streamingPhase].label}{\" \"}\r\n <InkSpinner type=\"dots\" />\r\n </Text>\r\n </Box>\r\n ) : rewindHintPhase === \"visible\" ? (\r\n // 本轮修改了文件时,流式结束后 2s 在原位置展示 /rewind 提示\r\n // 一直保留到下次对话开始,与 /rewind 1(最新检查点)配套\r\n <Box marginTop={1} justifyContent=\"center\">\r\n <Text color=\"#808080\">{\"↩ /rewind 1 可撤回本次修改\"}</Text>\r\n </Box>\r\n ) : null}\r\n <Box marginTop={1}>\r\n {hasConversationStarted || sessionMode === \"plan\" ? (\r\n <Text\r\n color={sessionMode === \"plan\" ? \"#ff69b4\" : \"#00ffff\"}\r\n dimColor={sessionMode !== \"plan\"}\r\n >\r\n <Text>\r\n {sessionMode === \"plan\"\r\n ? \"─\".repeat(Math.max(dividerWidth - 48, 1))\r\n : \"─\".repeat(Math.max(dividerWidth - 35, 10))}\r\n </Text>\r\n {balance !== null && (\r\n <Text color=\"yellow\">\r\n {\" 💰 余额 ¥\"}\r\n {balance.toFixed(2)}\r\n </Text>\r\n )}\r\n {isStreaming ? (\r\n <Text color=\"cyan\">\r\n {\" 📊 本次 ¥\"}\r\n {sessionCost > 0 ? sessionCost.toFixed(4) + \" \" : \"\"}\r\n <InkSpinner type=\"dots\" />\r\n </Text>\r\n ) : sessionCost > 0 ? (\r\n <Text color=\"cyan\">\r\n {\" 📊 本次 ¥\"}\r\n {sessionCost.toFixed(4)}\r\n </Text>\r\n ) : null}\r\n {sessionMode === \"plan\" && (\r\n <Text color=\"#ff69b4\" bold>\r\n {\" 📋 计划模式\"}\r\n </Text>\r\n )}\r\n </Text>\r\n ) : (\r\n <Text color=\"#00ffff\" dimColor>\r\n {\"─\".repeat(dividerWidth)}\r\n </Text>\r\n )}\r\n </Box>\r\n <Box>\r\n <Box width={4} flexShrink={0}>\r\n <Text bold color=\"#00ff41\">\r\n {\"⚡\"}\r\n </Text>\r\n </Box>\r\n <Box flexGrow={1}>\r\n {!input && !isStreaming && idlePlaceholder && gradientColors.length > 0 ? (\r\n <Text>\r\n {idlePlaceholder.split(\"\").map((ch, i) => (\r\n <Text key={i} color={gradientColors[i] ?? undefined}>\r\n {ch}\r\n </Text>\r\n ))}\r\n </Text>\r\n ) : !input &&\r\n isStreaming &&\r\n streamingPlaceholder &&\r\n streamingGradientColors.length > 0 ? (\r\n <Text>\r\n {streamingPlaceholder.split(\"\").map((ch, i) => (\r\n <Text key={i} color={streamingGradientColors[i] ?? undefined}>\r\n {ch}\r\n </Text>\r\n ))}\r\n </Text>\r\n ) : (\r\n <TextInput\r\n key={inputKey}\r\n value={input}\r\n onChange={setInput}\r\n onSubmit={handleSubmit}\r\n placeholder=\"\"\r\n />\r\n )}\r\n </Box>\r\n </Box>\r\n <Box>\r\n <Text color=\"#00ffff\" dimColor>\r\n {\"─\".repeat(dividerWidth)}\r\n </Text>\r\n </Box>\r\n\r\n {/* 用户输入 / 时显示 skill 列表 */}\r\n <SkillSelector skills={skills} input={input} selectedIndex={skillSelectIndex} />\r\n {/* 用户输入 @ 时显示文件列表 */}\r\n <FileSelector files={files} input={input} selectedIndex={fileSelectIndex} />\r\n </>\r\n )}\r\n\r\n {/* 双击 Ctrl+C 退出提示 */}\r\n {doubleCtrlC && !isStreaming && (\r\n <Box marginTop={1}>\r\n <Text color=\"#ff1493\" bold>\r\n {\" ⚠ 再按一次 Ctrl+C 退出 dskcode\"}\r\n </Text>\r\n </Box>\r\n )}\r\n\r\n {/* 流式中 Ctrl+C 取消提示 */}\r\n {isStreaming && (\r\n <Box marginTop={1}>\r\n <Text color=\"yellow\" dimColor>\r\n {\" 提示:按 Ctrl+C 取消当前请求\"}\r\n </Text>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}\r\n","import { useState, useCallback, useEffect, useRef } from \"react\";\r\n\r\nconst CTRL_C_TIMEOUT_MS = 1500;\r\n\r\n/**\r\n * 提供\"双击 Ctrl+C 才退出\"的交互逻辑。\r\n *\r\n * - 第一次 Ctrl+C:设置提示状态,1.5 秒后自动重置\r\n * - 第二次 Ctrl+C(1.5 秒内):执行 onExit 回调\r\n *\r\n * 返回 { doubleCtrlC, handleCtrlC },组件通过 useInput 捕获\r\n * Ctrl+C 后调用 handleCtrlC,并根据 doubleCtrlC 显示提示。\r\n */\r\nexport function useDoubleCtrlC(onExit: () => void) {\r\n const [doubleCtrlC, setDoubleCtrlC] = useState(false);\r\n const timerRef = useRef<ReturnType<typeof setTimeout>>(null);\r\n const onExitRef = useRef(onExit);\r\n onExitRef.current = onExit;\r\n\r\n // 组件卸载时清理定时器\r\n useEffect(() => {\r\n return () => {\r\n if (timerRef.current) clearTimeout(timerRef.current);\r\n };\r\n }, []);\r\n\r\n const handleCtrlC = useCallback(() => {\r\n if (doubleCtrlC) {\r\n // 第二次 Ctrl+C → 退出\r\n onExitRef.current();\r\n return;\r\n }\r\n\r\n // 第一次 Ctrl+C → 显示提示,启动倒计时重置\r\n setDoubleCtrlC(true);\r\n if (timerRef.current) clearTimeout(timerRef.current);\r\n timerRef.current = setTimeout(() => {\r\n setDoubleCtrlC(false);\r\n timerRef.current = null;\r\n }, CTRL_C_TIMEOUT_MS);\r\n }, [doubleCtrlC]);\r\n\r\n return { doubleCtrlC, handleCtrlC };\r\n}","// ---------------------------------------------------------------------------\r\n// 助手消息组件 — 渲染单条 assistant 回复(流式文本 + 工具调用 + 成本行)\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\nimport { useEffect, useRef, useState } from \"react\";\r\nimport type { ProviderToolCall, UsageInfo } from \"../provider/index.js\";\r\nimport { formatMoney } from \"../provider/cost-tracker.js\";\r\nimport { ToolCallBlock } from \"./ToolCallBlock.js\";\r\nimport { formatUsageSummary } from \"../agent/message-builder.js\";\r\nimport { HighlightedText } from \"./HighlightedText.js\";\r\nimport {\r\n DEFAULT_REASONING_MAX_LINES,\r\n joinReasoningSegments,\r\n truncateReasoningLines,\r\n} from \"./reasoning-utils.js\";\r\n\r\ninterface AssistantMessageProps {\r\n /** 助手回复的文本内容 */\r\n content: string;\r\n /**\r\n * 思考链段列表(thinking 模式的 CoT),可选。\r\n * 一个回合可能包含多段 CoT(多轮“思考→调工具”),每段独立成块展示。\r\n */\r\n reasoning?: string[];\r\n /** 工具调用列表 */\r\n toolCalls?: ProviderToolCall[];\r\n /** 是否正在流式输出中 */\r\n isStreaming?: boolean;\r\n /** Token 使用统计 */\r\n usage?: UsageInfo;\r\n /** 本次调用耗时(毫秒) */\r\n elapsed?: number;\r\n /** 本次调用费用 */\r\n cost?: number;\r\n /** 使用的模型标识 */\r\n model?: string;\r\n}\r\n\r\n/** 格式化毫秒为人类可读的时间 */\r\nfunction formatElapsed(ms: number): string {\r\n if (ms < 1000) return `${ms}ms`;\r\n const seconds = (ms / 1000).toFixed(1);\r\n return `${seconds}s`;\r\n}\r\n\r\n/**\r\n * 平滑动画的实时消耗行。\r\n *\r\n * props 中的 token/cost 是“目标值”,组件内部以 33ms (~30fps) 步进\r\n * 用 easeOutCubic 缓动插值,避免上游 300ms 节流推送造成的跳变感。\r\n * 当 target ≤ displayed 时直接跳到目标(支持估算/真实值互相覆盖)。\r\n */\r\nfunction AnimatedUsage({ usage, cost }: { usage?: UsageInfo; cost?: number }) {\r\n const targetTokens = usage ? usage.promptTokens + usage.completionTokens : 0;\r\n const targetCost = cost ?? 0;\r\n\r\n // 初次进入时直接定位到首帧,避免从 0 长动画\r\n const [displayedTokens, setDisplayedTokens] = useState(targetTokens);\r\n const [displayedCost, setDisplayedCost] = useState(targetCost);\r\n\r\n // 记录“上次起点”,动画以该起点为 0% 位置,趋近 targetTokens / targetCost\r\n const startRef = useRef<{\r\n fromTokens: number;\r\n fromCost: number;\r\n toTokens: number;\r\n toCost: number;\r\n startMs: number;\r\n durationMs: number;\r\n } | null>(null);\r\n const rafRef = useRef<ReturnType<typeof setInterval> | null>(null);\r\n\r\n useEffect(() => {\r\n // 目标值不变时跳过\r\n if (\r\n startRef.current &&\r\n startRef.current.toTokens === targetTokens &&\r\n startRef.current.toCost === targetCost\r\n ) {\r\n return;\r\n }\r\n\r\n // 目标值 ≤ 当前显示值(例:真实 usage 覆盖估算值,且可能偏低)\r\n // 或首次进入 → 直接跳到目标\r\n if (targetTokens <= displayedTokens && targetCost <= displayedCost) {\r\n setDisplayedTokens(targetTokens);\r\n setDisplayedCost(targetCost);\r\n startRef.current = {\r\n fromTokens: targetTokens,\r\n fromCost: targetCost,\r\n toTokens: targetTokens,\r\n toCost: targetCost,\r\n startMs: Date.now(),\r\n durationMs: 0,\r\n };\r\n return;\r\n }\r\n\r\n // 按变化量粗略估算动画时长:300ms 节流周期 + 少许渐变\r\n const tokensDelta = targetTokens - displayedTokens;\r\n const costDelta = targetCost - displayedCost;\r\n const durationMs = Math.min(600, Math.max(220, Math.max(tokensDelta, 0) * 1.2 + 220));\r\n startRef.current = {\r\n fromTokens: displayedTokens,\r\n fromCost: displayedCost,\r\n toTokens: targetTokens,\r\n toCost: targetCost,\r\n startMs: Date.now(),\r\n durationMs,\r\n };\r\n\r\n if (rafRef.current) clearInterval(rafRef.current);\r\n rafRef.current = setInterval(() => {\r\n const s = startRef.current;\r\n if (!s) return;\r\n const elapsed = Date.now() - s.startMs;\r\n if (elapsed >= s.durationMs) {\r\n setDisplayedTokens(s.toTokens);\r\n setDisplayedCost(s.toCost);\r\n if (rafRef.current) {\r\n clearInterval(rafRef.current);\r\n rafRef.current = null;\r\n }\r\n return;\r\n }\r\n // easeOutCubic:1 - (1 - t)^3\r\n const t = elapsed / s.durationMs;\r\n const eased = 1 - Math.pow(1 - t, 3);\r\n const tokens = s.fromTokens + (s.toTokens - s.fromTokens) * eased;\r\n const cost = s.fromCost + (s.toCost - s.fromCost) * eased;\r\n setDisplayedTokens(tokens);\r\n setDisplayedCost(cost);\r\n }, 33);\r\n\r\n return () => {\r\n if (rafRef.current) {\r\n clearInterval(rafRef.current);\r\n rafRef.current = null;\r\n }\r\n };\r\n // 只在目标值变化时重启动画,不依赖 displayedX\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [targetTokens, targetCost]);\r\n\r\n return (\r\n <>\r\n {usage && (\r\n <Text color=\"#888888\">{Math.round(displayedTokens).toLocaleString()} tokens</Text>\r\n )}\r\n {cost !== undefined && cost > 0 && (\r\n <Text color=\"#888888\">\r\n {\" · \"}¥{displayedCost.toFixed(4)}\r\n </Text>\r\n )}\r\n </>\r\n );\r\n}\r\n\r\n/**\r\n * 渲染单条助手回复。\r\n *\r\n * 显示结构:\r\n * 🤖 流式文本内容...\r\n * 📦 tool_name ────\r\n * 参数摘要\r\n * ⏳ 等待执行\r\n * ────────────────────────────\r\n * 💰 本次 ¥0.0012 │ 🕐 3.2s │ 📦 1.2k tokens\r\n */\r\nexport function AssistantMessage({\r\n content,\r\n reasoning,\r\n toolCalls,\r\n isStreaming = false,\r\n usage,\r\n elapsed,\r\n cost,\r\n model: _model,\r\n}: AssistantMessageProps) {\r\n // 内容为空且无工具调用且无思考链时不渲染\r\n if (\r\n !content &&\r\n (!toolCalls || toolCalls.length === 0) &&\r\n (!reasoning || reasoning.length === 0) &&\r\n !isStreaming\r\n ) {\r\n return null;\r\n }\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n {/* 思考链:所有段拼接为单文本(段间空行分隔),统一截断到 8 行。\r\n 多 sub-turn 的短段不再各自画框,避免空块堆点屏幕。 */}\r\n {reasoning &&\r\n reasoning.length > 0 &&\r\n (() => {\r\n const merged = joinReasoningSegments(reasoning);\r\n if (!merged) return null;\r\n // 滚动窗口:保留最后 N 行(最新思考),丢弃开头的旧行,与终端自动滚屏一致\r\n const { visible } = truncateReasoningLines(\r\n merged,\r\n DEFAULT_REASONING_MAX_LINES,\r\n );\r\n return (\r\n <Box flexDirection=\"row\" marginBottom={1}>\r\n <Box width={4} flexShrink={0}>\r\n <Text dimColor>{\"🧠\"}</Text>\r\n </Box>\r\n <Box\r\n flexGrow={1}\r\n flexDirection=\"column\"\r\n borderStyle=\"single\"\r\n borderColor=\"#444444\"\r\n paddingLeft={1}\r\n paddingRight={1}\r\n >\r\n <Text dimColor wrap=\"wrap\">\r\n {visible}\r\n </Text>\r\n </Box>\r\n </Box>\r\n );\r\n })()}\r\n\r\n {/* 助手标识 + 内容 */}\r\n <Box flexDirection=\"row\">\r\n <Box width={4} flexShrink={0}>\r\n <Text bold color=\"#ff00ff\">\r\n {\"🤖\"}\r\n </Text>\r\n </Box>\r\n <Box flexGrow={1} flexDirection=\"column\">\r\n {/* 文本内容(带语法高亮) */}\r\n {content && <HighlightedText>{content}</HighlightedText>}\r\n {/* 流式输出时的光标 */}\r\n {isStreaming && !content && (!reasoning || reasoning.length === 0) && (\r\n <Text color=\"#888888\">...</Text>\r\n )}\r\n </Box>\r\n </Box>\r\n\r\n {/* 工具调用 */}\r\n {toolCalls && toolCalls.length > 0 && (\r\n <Box flexDirection=\"column\">\r\n {toolCalls.map((tc, i) => (\r\n <ToolCallBlock key={tc.id ?? i} call={tc} />\r\n ))}\r\n </Box>\r\n )}\r\n\r\n {/* 流式输出中的实时消耗(带平滑动画) */}\r\n {isStreaming && (usage || cost !== undefined) && (\r\n <Box flexDirection=\"row\" marginTop={1} marginLeft={3}>\r\n <Text color=\"#666666\" dimColor>\r\n ⏳ 已消耗 <AnimatedUsage usage={usage} cost={cost} />\r\n </Text>\r\n </Box>\r\n )}\r\n\r\n {/* 成本/耗时摘要行(仅在流式结束后显示) */}\r\n {!isStreaming && (usage || elapsed !== undefined) && (\r\n <Box flexDirection=\"column\" marginTop={1} marginLeft={3}>\r\n <Text color=\"#555555\">{\"─\".repeat(36)}</Text>\r\n <Box flexDirection=\"row\" gap={2}>\r\n {cost !== undefined && cost > 0 && (\r\n <Text color=\"yellow\">💰 本次 {formatMoney(cost)}</Text>\r\n )}\r\n {elapsed !== undefined && (\r\n <Text color=\"cyan\">🕐 {formatElapsed(elapsed)}</Text>\r\n )}\r\n {usage && <Text color=\"#888888\">{formatUsageSummary(usage)}</Text>}\r\n </Box>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 成本追踪器 — 会话级 / 日级 / 历史级三层成本统计与持久化\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\r\nimport { join } from \"node:path\";\r\nimport type { UsageInfo, CostInfo, ModelId } from \"./types.js\";\r\nimport { calculateCost } from \"./models.js\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 类型定义\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 单次 API 调用的成本记录 */\r\nexport interface CostRecord {\r\n /** 记录时间戳(ISO 8601) */\r\n timestamp: string;\r\n /** 使用的模型 */\r\n model: ModelId;\r\n /** Token 使用量 */\r\n usage: UsageInfo;\r\n /** 费用明细(单位:元) */\r\n cost: CostInfo;\r\n}\r\n\r\n/** 单个会话的成本汇总 */\r\nexport interface SessionCostSummary {\r\n /** 会话 ID */\r\n sessionId: string;\r\n /** 会话开始时间(ISO 8601) */\r\n startedAt: string;\r\n /** 总输入 token 数 */\r\n totalPromptTokens: number;\r\n /** 总输出 token 数 */\r\n totalCompletionTokens: number;\r\n /** 总缓存命中 token 数 */\r\n totalCachedTokens: number;\r\n /** 总费用(元) */\r\n totalCost: number;\r\n /** 每次调用的详细记录 */\r\n records: CostRecord[];\r\n}\r\n\r\n/** 一天的成本汇总 */\r\nexport interface DailyCostSummary {\r\n /** 日期(YYYY-MM-DD 格式) */\r\n date: string;\r\n /** 当日总输入 token 数 */\r\n totalPromptTokens: number;\r\n /** 当日总输出 token 数 */\r\n totalCompletionTokens: number;\r\n /** 当日总缓存命中 token 数 */\r\n totalCachedTokens: number;\r\n /** 当日总费用(元) */\r\n totalCost: number;\r\n /** 当日 API 调用次数 */\r\n totalCalls: number;\r\n /** 按模型分类的明细 */\r\n byModel: Record<string, ModelCostSummary>;\r\n}\r\n\r\n/** 按模型分类的成本汇总 */\r\nexport interface ModelCostSummary {\r\n /** 模型标识 */\r\n model: ModelId;\r\n /** 该模型的总输入 token 数 */\r\n totalPromptTokens: number;\r\n /** 该模型的总输出 token 数 */\r\n totalCompletionTokens: number;\r\n /** 该模型的总缓存命中 token 数 */\r\n totalCachedTokens: number;\r\n /** 该模型的总费用(元) */\r\n totalCost: number;\r\n /** 该模型的 API 调用次数 */\r\n totalCalls: number;\r\n}\r\n\r\n/** 持久化文件格式 */\r\nexport interface CostStore {\r\n /** 版本号,便于未来格式迁移 */\r\n version: 1;\r\n /** 按日期索引的汇总数据 */\r\n daily: Record<string, DailyCostSummary>;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// CostTracker 配置\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface CostTrackerOptions {\r\n /** 成本持久化目录,默认 ~/.dskcode/costs */\r\n costDir?: string;\r\n /** 预算上限(元),超过后触发回调,0 表示不限制 */\r\n budgetLimit?: number;\r\n /** Token 预算上限,超过后触发回调,0 表示不限制 */\r\n tokenBudgetLimit?: number;\r\n /** 预算超限回调 */\r\n onBudgetExceeded?: (tracker: CostTracker) => void;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// CostTracker 类\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 成本追踪器 — 管理三层成本统计:\r\n *\r\n * 1. **会话级**(内存):当前会话内的累计 token 与费用\r\n * 2. **日级**(内存 + 持久化):按日聚合,支持\"今日消耗\"查询\r\n * 3. **历史级**(持久化):跨日的历史汇总查询\r\n *\r\n * 使用方式:\r\n * ```ts\r\n * const tracker = new CostTracker({ budgetLimit: 10 });\r\n * tracker.record(usage, 'deepseek-v4-flash');\r\n * console.log(tracker.todaySummary());\r\n * ```\r\n */\r\nexport class CostTracker {\r\n /** 全局活跃实例表,用于进程退出时统一 flush。 */\r\n static readonly #instances = new Set<CostTracker>();\r\n\r\n /** 进程退出兜底:统一 flush 所有活跃 CostTracker。 */\r\n static async flushAll(): Promise<void> {\r\n await Promise.allSettled(\r\n [...CostTracker.#instances].map((t) => t.flush()),\r\n );\r\n }\r\n\r\n readonly #costDir: string;\r\n readonly #budgetLimit: number;\r\n readonly #tokenBudgetLimit: number;\r\n readonly #onBudgetExceeded?: (tracker: CostTracker) => void;\r\n\r\n // 会话级统计(内存)\r\n #sessionId: string;\r\n #sessionStartedAt: string;\r\n readonly #sessionRecords: CostRecord[] = [];\r\n\r\n // 日级统计(内存缓存,启动时从磁盘恢复)\r\n #todayDate: string;\r\n #todaySummary: DailyCostSummary;\r\n\r\n // 持久化标记\r\n #dirty = false;\r\n #flushInProgress = false;\r\n\r\n constructor(options: CostTrackerOptions = {}) {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n this.#costDir = options.costDir ?? join(home, \".dskcode\", \"costs\");\r\n this.#budgetLimit = options.budgetLimit ?? 0;\r\n this.#tokenBudgetLimit = options.tokenBudgetLimit ?? 0;\r\n this.#onBudgetExceeded = options.onBudgetExceeded;\r\n\r\n // 初始化会话\r\n this.#sessionId = generateSessionId();\r\n this.#sessionStartedAt = new Date().toISOString();\r\n\r\n // 初始化日级统计\r\n const today = getTodayStr();\r\n this.#todayDate = today;\r\n this.#todaySummary = createEmptyDailySummary(today);\r\n\r\n // 注册到全局实例表(用于进程退出时统一 flush)\r\n CostTracker.#instances.add(this);\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 核心方法\r\n // -----------------------------------------------------------------------\r\n\r\n /**\r\n * 记录一次 API 调用的 Token 使用量和成本。\r\n * 自动计算费用,累加到会话级和日级统计。\r\n * 标记脏数据(dirty),由上层在合适的时机调用 flush() 持久化到磁盘。\r\n */\r\n record(usage: UsageInfo, model: ModelId): CostInfo {\r\n const cost = calculateCost(usage, model);\r\n const record: CostRecord = {\r\n timestamp: new Date().toISOString(),\r\n model,\r\n usage: { ...usage },\r\n cost: { ...cost },\r\n };\r\n\r\n // 1. 会话级累加\r\n this.#sessionRecords.push(record);\r\n\r\n // 2. 日级累加(自动处理日期变更)\r\n this.#ensureTodayBucket();\r\n this.#addToDaily(record);\r\n\r\n // 3. 标记需要持久化(上层在对话周期结束时调用 flush() 落盘)\r\n this.#dirty = true;\r\n\r\n // 4. 预算检查\r\n this.#checkBudget();\r\n\r\n return cost;\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 会话级查询\r\n // -----------------------------------------------------------------------\r\n\r\n /** 当前会话 ID */\r\n get sessionId(): string {\r\n return this.#sessionId;\r\n }\r\n\r\n /** 当前会话的所有成本记录 */\r\n get records(): readonly CostRecord[] {\r\n return this.#sessionRecords;\r\n }\r\n\r\n /** 当前会话累计成本汇总 */\r\n get sessionSummary(): SessionCostSummary {\r\n let totalPromptTokens = 0;\r\n let totalCompletionTokens = 0;\r\n let totalCachedTokens = 0;\r\n let totalCost = 0;\r\n\r\n for (const r of this.#sessionRecords) {\r\n totalPromptTokens += r.usage.promptTokens;\r\n totalCompletionTokens += r.usage.completionTokens;\r\n totalCachedTokens += r.usage.cachedPromptTokens ?? 0;\r\n totalCost += r.cost.totalCost;\r\n }\r\n\r\n return {\r\n sessionId: this.#sessionId,\r\n startedAt: this.#sessionStartedAt,\r\n totalPromptTokens,\r\n totalCompletionTokens,\r\n totalCachedTokens,\r\n totalCost,\r\n records: [...this.#sessionRecords],\r\n };\r\n }\r\n\r\n /** 当前会话总费用(元) */\r\n get sessionTotalCost(): number {\r\n return this.#sessionRecords.reduce((sum, r) => sum + r.cost.totalCost, 0);\r\n }\r\n\r\n /** 当前会话总 prompt token 数 */\r\n get sessionPromptTokens(): number {\r\n return this.#sessionRecords.reduce((sum, r) => sum + r.usage.promptTokens, 0);\r\n }\r\n\r\n /** 当前会话总 completion token 数 */\r\n get sessionCompletionTokens(): number {\r\n return this.#sessionRecords.reduce((sum, r) => sum + r.usage.completionTokens, 0);\r\n }\r\n\r\n /** 当前会话 API 调用次数 */\r\n get sessionCallCount(): number {\r\n return this.#sessionRecords.length;\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 日级查询\r\n // -----------------------------------------------------------------------\r\n\r\n /** 今日成本汇总 */\r\n get todaySummary(): DailyCostSummary {\r\n this.#ensureTodayBucket();\r\n return { ...this.#todaySummary };\r\n }\r\n\r\n /** 今日总费用(元) */\r\n get todayTotalCost(): number {\r\n this.#ensureTodayBucket();\r\n return this.#todaySummary.totalCost;\r\n }\r\n\r\n /** 今日 API 调用次数 */\r\n get todayCallCount(): number {\r\n this.#ensureTodayBucket();\r\n return this.#todaySummary.totalCalls;\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 预算检查\r\n // -----------------------------------------------------------------------\r\n\r\n /** 是否已超出预算 */\r\n get isBudgetExceeded(): boolean {\r\n if (this.#budgetLimit > 0 && this.todayTotalCost >= this.#budgetLimit) {\r\n return true;\r\n }\r\n if (this.#tokenBudgetLimit > 0) {\r\n const totalTokens =\r\n this.#todaySummary.totalPromptTokens +\r\n this.#todaySummary.totalCompletionTokens;\r\n if (totalTokens >= this.#tokenBudgetLimit) return true;\r\n }\r\n return false;\r\n }\r\n\r\n /** 剩余预算(元),无限制时返回 Infinity */\r\n get remainingBudget(): number {\r\n if (this.#budgetLimit <= 0) return Infinity;\r\n return Math.max(0, this.#budgetLimit - this.todayTotalCost);\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 持久化\r\n // -----------------------------------------------------------------------\r\n\r\n /**\r\n * 从磁盘加载历史成本数据。\r\n * 启动时调用,用于恢复今日和历史数据。\r\n * 副作用:确保成本目录存在(首次运行时创建 ~/.dskcode/costs)。\r\n */\r\n async load(): Promise<void> {\r\n await this.#ensureCostDir();\r\n const store = await this.#loadStore();\r\n const today = getTodayStr();\r\n\r\n if (store.daily[today]) {\r\n this.#todaySummary = store.daily[today];\r\n this.#todayDate = today;\r\n }\r\n }\r\n\r\n /**\r\n * 将脏数据持久化到磁盘。\r\n * 会话结束/进程退出时主动调用一次即可。需要每日数据精确时也可在每次\r\n * record() 后调用。并发安全:靠 #flushInProgress 互斥。\r\n */\r\n async flush(): Promise<void> {\r\n if (!this.#dirty || this.#flushInProgress) return;\r\n this.#flushInProgress = true;\r\n this.#dirty = false;\r\n\r\n try {\r\n await this.#ensureCostDir();\r\n await this.#saveStore();\r\n } catch (err) {\r\n // 落盘失败:把 dirty 标记还回去,等下次重试\r\n this.#dirty = true;\r\n console.error(\"[CostTracker] flush 失败:\", err);\r\n } finally {\r\n this.#flushInProgress = false;\r\n }\r\n }\r\n\r\n /**\r\n * 查询指定日期范围的成本汇总。\r\n * @param startDate 起始日期(YYYY-MM-DD),包含\r\n * @param endDate 截止日期(YYYY-MM-DD),包含,默认同 startDate\r\n */\r\n async queryRange(\r\n startDate: string,\r\n endDate?: string,\r\n ): Promise<DailyCostSummary[]> {\r\n const store = await this.#loadStore();\r\n const end = endDate ?? startDate;\r\n const result: DailyCostSummary[] = [];\r\n\r\n for (const [date, summary] of Object.entries(store.daily)) {\r\n if (date >= startDate && date <= end) {\r\n result.push(summary);\r\n }\r\n }\r\n\r\n result.sort((a, b) => a.date.localeCompare(b.date));\r\n return result;\r\n }\r\n\r\n /** 重置会话(开始新会话,不影响日级累计) */\r\n resetSession(): void {\r\n this.#sessionId = generateSessionId();\r\n this.#sessionStartedAt = new Date().toISOString();\r\n this.#sessionRecords.length = 0;\r\n }\r\n\r\n /**\r\n * 销毁实例:从全局实例表移除并 flush。\r\n * 一般用不到,仅在测试或显式生命周期管理时使用。\r\n */\r\n async dispose(): Promise<void> {\r\n CostTracker.#instances.delete(this);\r\n await this.flush();\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 内部方法\r\n // -----------------------------------------------------------------------\r\n\r\n /** 确保今日的统计桶存在(处理跨日情况) */\r\n #ensureTodayBucket(): void {\r\n const today = getTodayStr();\r\n if (today !== this.#todayDate) {\r\n // 日期变更:将旧日数据持久化后切换到新日\r\n this.#todayDate = today;\r\n this.#todaySummary = createEmptyDailySummary(today);\r\n this.#dirty = true;\r\n }\r\n }\r\n\r\n /** 将一条记录累加到日级汇总 */\r\n #addToDaily(record: CostRecord): void {\r\n this.#todaySummary.totalPromptTokens += record.usage.promptTokens;\r\n this.#todaySummary.totalCompletionTokens += record.usage.completionTokens;\r\n this.#todaySummary.totalCachedTokens += record.usage.cachedPromptTokens ?? 0;\r\n this.#todaySummary.totalCost += record.cost.totalCost;\r\n this.#todaySummary.totalCalls += 1;\r\n\r\n // 按模型分类累加\r\n const modelKey = record.model;\r\n if (!this.#todaySummary.byModel[modelKey]) {\r\n this.#todaySummary.byModel[modelKey] = {\r\n model: record.model,\r\n totalPromptTokens: 0,\r\n totalCompletionTokens: 0,\r\n totalCachedTokens: 0,\r\n totalCost: 0,\r\n totalCalls: 0,\r\n };\r\n }\r\n\r\n const modelSummary = this.#todaySummary.byModel[modelKey];\r\n modelSummary.totalPromptTokens += record.usage.promptTokens;\r\n modelSummary.totalCompletionTokens += record.usage.completionTokens;\r\n modelSummary.totalCachedTokens += record.usage.cachedPromptTokens ?? 0;\r\n modelSummary.totalCost += record.cost.totalCost;\r\n modelSummary.totalCalls += 1;\r\n }\r\n\r\n /** 预算超限检查 */\r\n #checkBudget(): void {\r\n if (!this.isBudgetExceeded) return;\r\n this.#onBudgetExceeded?.(this);\r\n }\r\n\r\n /** 从磁盘加载成本存储 */\r\n async #loadStore(): Promise<CostStore> {\r\n const filePath = join(this.#costDir, \"history.json\");\r\n try {\r\n const raw = await readFile(filePath, \"utf-8\");\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n return JSON.parse(raw) as unknown as CostStore;\r\n } catch {\r\n return { version: 1, daily: {} };\r\n }\r\n }\r\n\r\n /** 确保成本目录存在(首次运行时创建 ~/.dskcode/costs) */\r\n async #ensureCostDir(): Promise<void> {\r\n await mkdir(this.#costDir, { recursive: true });\r\n }\r\n\r\n /** 保存成本存储到磁盘 */\r\n async #saveStore(): Promise<void> {\r\n const store = await this.#loadStore();\r\n\r\n // 合并今日数据到存储中\r\n store.daily[this.#todayDate] = this.#todaySummary;\r\n\r\n // 保留最近 90 天的数据(避免文件过大)\r\n const cutoffDate = getDateNDaysAgo(90);\r\n const datesToRemove = Object.keys(store.daily).filter(\r\n (date) => date < cutoffDate,\r\n );\r\n for (const date of datesToRemove) {\r\n // eslint-disable-next-line @typescript-eslint/no-dynamic-delete\r\n delete store.daily[date];\r\n }\r\n\r\n const filePath = join(this.#costDir, \"history.json\");\r\n await writeFile(filePath, JSON.stringify(store, null, 2), \"utf-8\");\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 工具函数\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 生成会话 ID:时间戳 + 随机后缀 */\r\nfunction generateSessionId(): string {\r\n const ts = Date.now().toString(36);\r\n const rand = Math.random().toString(36).slice(2, 8);\r\n return `${ts}-${rand}`;\r\n}\r\n\r\n/** 获取今日日期字符串(YYYY-MM-DD) */\r\nfunction getTodayStr(): string {\r\n return new Date().toISOString().slice(0, 10);\r\n}\r\n\r\n/** 获取 N 天前的日期字符串 */\r\nfunction getDateNDaysAgo(n: number): string {\r\n const d = new Date();\r\n d.setDate(d.getDate() - n);\r\n return d.toISOString().slice(0, 10);\r\n}\r\n\r\n/** 创建空的日级成本汇总 */\r\nfunction createEmptyDailySummary(date: string): DailyCostSummary {\r\n return {\r\n date,\r\n totalPromptTokens: 0,\r\n totalCompletionTokens: 0,\r\n totalCachedTokens: 0,\r\n totalCost: 0,\r\n totalCalls: 0,\r\n byModel: {},\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 格式化工具\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 格式化费用金额(保留4位小数) */\r\nexport function formatMoney(yuan: number): string {\r\n return `¥${yuan.toFixed(4)}`;\r\n}\r\n\r\n/** 格式化 token 数量(带千分位) */\r\nexport function formatTokens(tokens: number): string {\r\n return tokens.toLocaleString(\"en-US\");\r\n}\r\n\r\n/** 格式化缓存命中率百分比 */\r\nexport function formatCacheHitRate(cached: number, total: number): string {\r\n if (total === 0) return \"0.0%\";\r\n return `${((cached / total) * 100).toFixed(1)}%`;\r\n}\r\n\r\n/** 生成今日消耗报告的终端友好文本 */\r\nexport function formatTodayReport(summary: DailyCostSummary): string {\r\n const lines: string[] = [];\r\n\r\n lines.push(`📊 今日消耗报告 (${summary.date})`);\r\n lines.push(\"─\".repeat(40));\r\n lines.push(` 💰 总费用: ${formatMoney(summary.totalCost)}`);\r\n lines.push(` 📞 调用次数: ${String(summary.totalCalls)} 次`);\r\n lines.push(` 📥 输入 Token: ${formatTokens(summary.totalPromptTokens)}`);\r\n lines.push(` 📤 输出 Token: ${formatTokens(summary.totalCompletionTokens)}`);\r\n lines.push(\r\n ` 🗄️ 缓存命中: ${formatTokens(summary.totalCachedTokens)} (${formatCacheHitRate(summary.totalCachedTokens, summary.totalPromptTokens)})`,\r\n );\r\n lines.push(\"\");\r\n\r\n // 按模型分类明细\r\n const models = Object.values(summary.byModel);\r\n if (models.length > 0) {\r\n lines.push(\"📈 按模型分类:\");\r\n for (const m of models) {\r\n lines.push(` ─ ${m.model} ─`);\r\n lines.push(` 费用: ${formatMoney(m.totalCost)} | 调用: ${String(m.totalCalls)} 次`);\r\n lines.push(\r\n ` 输入: ${formatTokens(m.totalPromptTokens)} | 输出: ${formatTokens(m.totalCompletionTokens)}`,\r\n );\r\n lines.push(\r\n ` 缓存命中: ${formatTokens(m.totalCachedTokens)} (${formatCacheHitRate(m.totalCachedTokens, m.totalPromptTokens)})`,\r\n );\r\n }\r\n }\r\n\r\n return lines.join(\"\\n\");\r\n}\r\n\r\n/** 生成会话成本摘要的单行文本 */\r\nexport function formatSessionCostLine(summary: SessionCostSummary): string {\r\n const cacheRate = formatCacheHitRate(\r\n summary.totalCachedTokens,\r\n summary.totalPromptTokens,\r\n );\r\n return (\r\n `💰 ¥${summary.totalCost.toFixed(4)} | ` +\r\n `📥${formatTokens(summary.totalPromptTokens)} ` +\r\n `📤${formatTokens(summary.totalCompletionTokens)} ` +\r\n `🗄️${cacheRate} | ` +\r\n `📞${String(summary.records.length)}次`\r\n );\r\n}\r\n\r\n/** 生成单次调用的成本单行文本 */\r\nexport function formatCallCostLine(record: CostRecord): string {\r\n const cacheRate = formatCacheHitRate(\r\n record.usage.cachedPromptTokens ?? 0,\r\n record.usage.promptTokens,\r\n );\r\n return (\r\n `≈¥${record.cost.totalCost.toFixed(4)} ` +\r\n `(📥${String(record.usage.promptTokens)} 📤${String(record.usage.completionTokens)} 🗄️${cacheRate})`\r\n );\r\n}","// ---------------------------------------------------------------------------\r\n// 工具调用块组件 — 在终端中渲染单个工具调用\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\nimport type { ProviderToolCall } from \"../provider/index.js\";\r\n\r\ninterface ToolCallBlockProps {\r\n call: ProviderToolCall;\r\n /** 是否显示\"等待执行\"提示(默认 true) */\r\n showPendingHint?: boolean;\r\n}\r\n\r\n/**\r\n * 格式化工具调用参数的摘要。\r\n * 短参数直接展示,长参数截断。\r\n */\r\nfunction formatArgsSummary(args: string): string {\r\n try {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const parsed = JSON.parse(args) as unknown as Record<string, unknown>;\r\n const lines = Object.entries(parsed).map(([key, value]) => {\r\n const val = String(value);\r\n const truncated = val.length > 80 ? val.slice(0, 77) + \"...\" : val;\r\n return ` ${key}: ${truncated}`;\r\n });\r\n return lines.join(\"\\n\");\r\n } catch {\r\n // 无法解析的 JSON,直接截断展示\r\n const truncated = args.length > 120 ? args.slice(0, 117) + \"...\" : args;\r\n return ` ${truncated}`;\r\n }\r\n}\r\n\r\n/**\r\n * 渲染单个工具调用信息块。\r\n *\r\n * 显示效果:\r\n * 📦 read_file ─────────────────\r\n * 📂 path: src/provider/types.ts\r\n * ⏳ 等待执行\r\n */\r\nexport function ToolCallBlock({ call, showPendingHint = true }: ToolCallBlockProps) {\r\n const argsDisplay = formatArgsSummary(call.arguments);\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginLeft={3} marginTop={1}>\r\n <Box>\r\n <Text dimColor>📦 {call.name}</Text>\r\n <Text dimColor> {\"─\".repeat(Math.max(1, 30 - call.name.length))}</Text>\r\n </Box>\r\n <Box flexDirection=\"column\">\r\n <Text dimColor>{argsDisplay}</Text>\r\n </Box>\r\n {showPendingHint && (\r\n <Box>\r\n <Text dimColor>⏳ 等待执行</Text>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 消息组装 — 将历史消息与系统提示词合并,支持上下文裁剪\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type { ChatMessage, UsageInfo } from \"../provider/index.js\";\r\nimport type { ModelId } from \"../provider/types.js\";\r\nimport { getModelMeta, estimateTokens } from \"../provider/models.js\";\r\n\r\n/** 上下文裁剪策略 */\r\nexport interface TrimOptions {\r\n /** 模型标识,用于获取上下文窗口大小 */\r\n model: ModelId;\r\n /** 为模型输出预留的 token 数 */\r\n reservedForOutput: number;\r\n /** 系统提示词(始终保留) */\r\n systemPrompt: string;\r\n /** 最近 N 轮对话强制保留(不参与裁剪) */\r\n preserveRecentRounds: number;\r\n}\r\n\r\n/**\r\n * 估算消息的 token 数。\r\n * 简单估算:content 长度 × 0.3 + 角色开销 ~10 tokens\r\n */\r\nfunction estimateMessageTokens(msg: ChatMessage): number {\r\n let text = msg.content;\r\n // 工具调用的参数也算\r\n if (msg.toolCalls) {\r\n for (const tc of msg.toolCalls) {\r\n text += tc.name + tc.arguments;\r\n }\r\n }\r\n return estimateTokens(text) + 10;\r\n}\r\n\r\n/**\r\n * 将消息历史划分为不可分割的「回合」单元。\r\n *\r\n * 一个回合的定义:\r\n * - 以一条 user 消息开头\r\n * - 后续连续的 assistant / tool 附属属于该回合,直到下一条 user 出现\r\n * - 特别地,assistant 携带 tool_calls 时,其后紧跟的若干 tool 消息必须与该 assistant 绑定\r\n * (DeepSeek/OpenAI 兼容 API 要求 tool_calls 与对应的 tool 结果消息一一配对,\r\n * 否则返回 400 错误)。因此裁剪时不能把这对拆开。\r\n *\r\n * @returns 回合数组;每个回合是一个非空消息数组,原顺序保留\r\n */\r\nfunction groupIntoTurns(messages: ChatMessage[]): ChatMessage[][] {\r\n const turns: ChatMessage[][] = [];\r\n let current: ChatMessage[] | null = null;\r\n\r\n for (const msg of messages) {\r\n if (msg.role === \"user\") {\r\n // 新回合开始\r\n if (current) turns.push(current);\r\n current = [msg];\r\n } else {\r\n // assistant / tool:归属当前回合;若无当前回合(历史以非 user 开头)\r\n // 则单独为保证不丢失而新建一个虚拟回合\r\n if (!current) current = [];\r\n current.push(msg);\r\n }\r\n }\r\n if (current && current.length > 0) turns.push(current);\r\n return turns;\r\n}\r\n\r\n/**\r\n * 估算一个回合内所有消息的总 token 数。\r\n */\r\nfunction estimateTurnTokens(turn: ChatMessage[]): number {\r\n let sum = 0;\r\n for (const msg of turn) sum += estimateMessageTokens(msg);\r\n return sum;\r\n}\r\n\r\n/**\r\n * 裁剪消息历史以适应上下文窗口,保证工具调用回合不被拆散。\r\n *\r\n * 策略:\r\n * 1. 系统提示词始终保留,不计入裁剪范围\r\n * 2. 将历史按「回合」分组(user 起头 → 直到下一个 user 之前的所有 assistant/tool)\r\n * 3. 最近的 `preserveRecentRounds` 个回合整体强制保留\r\n * 4. 若保留区超限,从最前的回合整组丢弃,直到不超限\r\n * 5. 否则,从最近向 earliest 后填更早的整回合,装不下就停\r\n *\r\n * 关键不变量:任何含 toolCalls 的 assistant 与其后续的 tool 结果消息\r\n * 始终在同一回合里,裁剪不会拆开它们。\r\n *\r\n * @param messages 原始消息历史(不含 system prompt)\r\n * @param opts 裁剪选项\r\n * @returns [裁剪后的消息数组, 是否发生了裁剪]\r\n */\r\nexport function trimMessages(\r\n messages: ChatMessage[],\r\n opts: TrimOptions,\r\n): [ChatMessage[], boolean] {\r\n const meta = getModelMeta(opts.model);\r\n const maxInputTokens = meta.contextWindow - opts.reservedForOutput;\r\n\r\n // 系统提示词 token 估算\r\n const systemTokens = estimateTokens(opts.systemPrompt);\r\n let remaining = maxInputTokens - systemTokens;\r\n\r\n const turns = groupIntoTurns(messages);\r\n\r\n // 保留最近 N 个回合\r\n const preservedTurns: ChatMessage[][] = [];\r\n let roundsPreserved = 0;\r\n for (let i = turns.length - 1; i >= 0 && roundsPreserved < opts.preserveRecentRounds; i--) {\r\n preservedTurns.unshift(turns[i]!);\r\n roundsPreserved++;\r\n }\r\n\r\n const preserved: ChatMessage[] = preservedTurns.flat();\r\n for (const msg of preserved) {\r\n remaining -= estimateMessageTokens(msg);\r\n }\r\n\r\n // 保留区本身超限:从最前的回合整组丢\r\n if (remaining < 0) {\r\n while (preservedTurns.length > 1 && remaining < 0) {\r\n const dropped = preservedTurns.shift()!;\r\n remaining += estimateTurnTokens(dropped);\r\n }\r\n return [preservedTurns.flat(), true];\r\n }\r\n\r\n // 向 earliest 填更早的回合\r\n const olderTurns = turns.slice(0, turns.length - preservedTurns.length);\r\n const keptTurns: ChatMessage[][] = [];\r\n\r\n for (let i = olderTurns.length - 1; i >= 0; i--) {\r\n const cost = estimateTurnTokens(olderTurns[i]!);\r\n if (remaining - cost < 0) break;\r\n remaining -= cost;\r\n keptTurns.unshift(olderTurns[i]!);\r\n }\r\n\r\n const result = [...keptTurns, ...preservedTurns].flat();\r\n const trimmed = result.length < messages.length;\r\n return [result, trimmed];\r\n}\r\n\r\n/**\r\n * 构建完整的 API 请求消息数组。\r\n *\r\n * 在消息历史前面插入 system prompt,返回可直接传给 provider.chat() 的消息数组。\r\n */\r\nexport function buildApiMessages(\r\n systemPrompt: string,\r\n history: ChatMessage[],\r\n): ChatMessage[] {\r\n return [\r\n { role: \"system\", content: systemPrompt },\r\n ...history,\r\n ];\r\n}\r\n\r\n/**\r\n * 将 Token 使用量格式化为人类可读的摘要字符串。\r\n */\r\nexport function formatUsageSummary(usage: UsageInfo): string {\r\n const prompt = usage.promptTokens.toLocaleString();\r\n const completion = usage.completionTokens.toLocaleString();\r\n const total = (usage.promptTokens + usage.completionTokens).toLocaleString();\r\n\r\n let summary = `📥 ${prompt} + 📤 ${completion} = 📦 ${total} tokens`;\r\n\r\n if (usage.cachedPromptTokens && usage.cachedPromptTokens > 0) {\r\n const cacheRate = ((usage.cachedPromptTokens / usage.promptTokens) * 100).toFixed(1);\r\n summary += ` │ 🗄️ 缓存命中 ${cacheRate}%`;\r\n }\r\n\r\n return summary;\r\n}","// ---------------------------------------------------------------------------\r\n// HighlightedText — AI 回复文本高亮渲染\r\n//\r\n// 支持四种语法:\r\n// `` `code` `` → 天蓝色(代码标识符、文件路径)\r\n// `**bold**` → 紫色(强调内容,如\"第 12 行\")\r\n// ```...``` 代码块 → 独立块渲染,diff 内容自动绿/红着色\r\n// | col | col | 表格 → 框线表格,自动对齐\r\n// 流式传输中未闭合的标记自动降级为纯文本,不会闪烁。\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\nimport type { ReactNode } from \"react\";\r\nimport { detectTermWidth, layoutTable } from \"./table-layout.js\";\r\n\r\n/** 解析后的文本段类型 */\r\ntype SegmentType = \"plain\" | \"code\" | \"bold\" | \"code_block\" | \"table\";\r\n\r\ninterface Segment {\r\n text: string;\r\n type: SegmentType;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 第 0 遍:提取三反引号代码块\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 在全文中查找 ```...``` 代码块,将其标记为 \"code_block\" 类型。\r\n * 代码块之外的内容保持 \"plain\" 类型,后续交给 bold/code 解析。\r\n *\r\n * 未闭合的三反引号降级为纯文本(流式传输安全)。\r\n */\r\nfunction parseCodeBlocks(text: string): Segment[] {\r\n const segments: Segment[] = [];\r\n let current = 0;\r\n\r\n while (current < text.length) {\r\n const openIdx = text.indexOf(\"```\", current);\r\n\r\n if (openIdx === -1) {\r\n segments.push({ text: text.slice(current), type: \"plain\" });\r\n break;\r\n }\r\n\r\n if (openIdx > current) {\r\n segments.push({ text: text.slice(current, openIdx), type: \"plain\" });\r\n }\r\n\r\n const closeIdx = text.indexOf(\"```\", openIdx + 3);\r\n\r\n if (closeIdx === -1) {\r\n // 未闭合 → 降级为纯文本\r\n segments.push({ text: text.slice(openIdx), type: \"plain\" });\r\n break;\r\n }\r\n\r\n // 提取代码块内容(含开头的语言标识行)\r\n const codeContent = text.slice(openIdx + 3, closeIdx);\r\n segments.push({ text: codeContent, type: \"code_block\" });\r\n current = closeIdx + 3;\r\n }\r\n\r\n return segments;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 第 0.5 遍:Markdown 表格检测(在 plain 段内)\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 判断一行是不是表格行(以 | 开头并以 | 结尾) */\r\nfunction isTableRow(line: string): boolean {\r\n const t = line.trim();\r\n return /^\\|.+\\|$/.test(t);\r\n}\r\n\r\n/** 判断一行是不是表格分隔行(|:---:|:---:| 等) */\r\nfunction isTableSepRow(line: string): boolean {\r\n const t = line.trim();\r\n // 字符类需包含 | 以匹配多列分隔行,如 |---|---|\r\n return /^\\|[-: |]+\\|$/.test(t) && t.includes(\"-\");\r\n}\r\n\r\n/**\r\n * 在 plain 文本中检测 Markdown 表格块(连续的 `|...|` 行),标记为 \"table\" 类型。\r\n *\r\n * 表格判定条件:\r\n * 1. 开头行是表格行\r\n * 2. 第二行是分隔行(含 `-`)\r\n * 3. 第三行也是表格行\r\n */\r\nfunction parseTables(text: string): Segment[] {\r\n const lines = text.split(\"\\n\");\r\n const result: Segment[] = [];\r\n let plainBuf = \"\";\r\n\r\n function flushPlain() {\r\n if (plainBuf) {\r\n result.push({ text: plainBuf, type: \"plain\" });\r\n plainBuf = \"\";\r\n }\r\n }\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n const line = lines[i] ?? \"\";\r\n\r\n if (\r\n isTableRow(line) &&\r\n i + 1 < lines.length &&\r\n isTableSepRow(lines[i + 1] ?? \"\") &&\r\n i + 2 < lines.length &&\r\n isTableRow(lines[i + 2] ?? \"\")\r\n ) {\r\n flushPlain();\r\n\r\n // 收集所有连续的表格行(含分隔行)\r\n const tableLineList: string[] = [];\r\n let j = i;\r\n while (j < lines.length && isTableRow(lines[j] ?? \"\")) {\r\n tableLineList.push(lines[j]!);\r\n j++;\r\n }\r\n\r\n result.push({ text: tableLineList.join(\"\\n\"), type: \"table\" });\r\n i = j - 1;\r\n } else {\r\n if (plainBuf) plainBuf += \"\\n\" + line;\r\n else plainBuf = line;\r\n }\r\n }\r\n\r\n flushPlain();\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 第 1 遍:**bold** 解析(与之前相同)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 按 `**...**` 对拆分为段。\r\n * 未闭合的 `**`(流式传输中)降级为纯文本。\r\n */\r\nfunction parseBoldPairs(text: string): Segment[] {\r\n const segments: Segment[] = [];\r\n let current = 0;\r\n\r\n while (current < text.length) {\r\n const openIdx = text.indexOf(\"**\", current);\r\n\r\n if (openIdx === -1) {\r\n segments.push({ text: text.slice(current), type: \"plain\" });\r\n break;\r\n }\r\n\r\n // ** 前的纯文本\r\n if (openIdx > current) {\r\n segments.push({ text: text.slice(current, openIdx), type: \"plain\" });\r\n }\r\n\r\n const closeIdx = text.indexOf(\"**\", openIdx + 2);\r\n\r\n // 未闭合 → 降级为纯文本\r\n if (closeIdx === -1) {\r\n segments.push({ text: text.slice(openIdx), type: \"plain\" });\r\n break;\r\n }\r\n\r\n // 成对 **...** 内的内容 → bold\r\n segments.push({ text: text.slice(openIdx + 2, closeIdx), type: \"bold\" });\r\n current = closeIdx + 2;\r\n }\r\n\r\n return segments;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 第 2 遍:`code` 解析(处理 plain 和 bold 段内的单反引号对)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 在纯文本中查找成对的单反引号,返回 [plain, code, plain, ...] 段序列。\r\n *\r\n * - 只处理 `...` 单反引号对;多反引号序列降级为纯文本。\r\n * - 未闭合的反引号降级为纯文本。\r\n */\r\nfunction parseInlineCode(text: string): Segment[] {\r\n const result: Segment[] = [];\r\n let current = 0;\r\n\r\n while (current < text.length) {\r\n const openIdx = text.indexOf(\"`\", current);\r\n\r\n if (openIdx === -1) {\r\n result.push({ text: text.slice(current), type: \"plain\" });\r\n break;\r\n }\r\n\r\n if (openIdx > current) {\r\n result.push({ text: text.slice(current, openIdx), type: \"plain\" });\r\n }\r\n\r\n // 计算连续反引号的数量\r\n let runLength = 1;\r\n while (\r\n openIdx + runLength < text.length &&\r\n text[openIdx + runLength] === \"`\"\r\n ) {\r\n runLength++;\r\n }\r\n\r\n if (runLength === 1) {\r\n // 单反引号对 → code 段\r\n const closeIdx = text.indexOf(\"`\", openIdx + 1);\r\n\r\n if (closeIdx === -1) {\r\n result.push({ text: text.slice(openIdx), type: \"plain\" });\r\n break;\r\n }\r\n\r\n result.push({ text: text.slice(openIdx + 1, closeIdx), type: \"code\" });\r\n current = closeIdx + 1;\r\n } else {\r\n // 多反引号序列(```、`` 等)保持原样\r\n result.push({\r\n text: text.slice(openIdx, openIdx + runLength),\r\n type: \"plain\",\r\n });\r\n current = openIdx + runLength;\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * 在 plain 和 bold 段内按 `` `...` `` 对拆分为 code 段。\r\n *\r\n * - **plain** 段内的 code → 标蓝,剩余部分保持默认色\r\n * - **bold** 段内的 code → 标蓝,剩余部分保持紫色\r\n * - 多反引号序列(```` ```code``` ````、`` ``code`` `` 等)统一降级为纯文本\r\n * - code_block / table 段直接透传\r\n */\r\nfunction parseCodePairs(segments: Segment[]): Segment[] {\r\n const result: Segment[] = [];\r\n\r\n for (const seg of segments) {\r\n if (seg.type === \"code_block\" || seg.type === \"table\") {\r\n result.push(seg);\r\n continue;\r\n }\r\n\r\n // 对 plain 和 bold 的内容都做反引号解析\r\n const parts = parseInlineCode(seg.text);\r\n\r\n for (const part of parts) {\r\n if (part.type === \"code\") {\r\n // 反引号内容始终标蓝\r\n result.push(part);\r\n } else if (seg.type === \"bold\") {\r\n // bold 段内非反引号部分 → 保持紫色\r\n result.push({ text: part.text, type: \"bold\" });\r\n } else {\r\n // plain 段内非反引号部分 → 保持默认色\r\n result.push(part);\r\n }\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 代码块渲染\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 添加行绿色 */\r\nconst DIFF_ADD_COLOR = \"#22c55e\";\r\n/** 删除行红色 */\r\nconst DIFF_DEL_COLOR = \"#ef4444\";\r\n/** Hunk 头青色 */\r\nconst DIFF_HUNK_COLOR = \"#00cccc\";\r\n\r\n/**\r\n * 渲染三反引号代码块的内容,对 diff 风格的内容行着色。\r\n *\r\n * 代码块内的第一行若为纯文本(非 `+`/`-`/`@@`/空格开头),\r\n * 视为语言标识(如 \"diff\"、\"python\"),用灰色标出。\r\n */\r\nfunction CodeBlockRenderer({ code }: { code: string }): ReactNode {\r\n const lines = code.split(\"\\n\");\r\n\r\n // 判断第一行是否为语言标识行\r\n const firstLine = lines[0] ?? \"\";\r\n const hasLangLine =\r\n firstLine.trim().length > 0 &&\r\n !firstLine.startsWith(\"+\") &&\r\n !firstLine.startsWith(\"-\") &&\r\n !firstLine.startsWith(\"@@\") &&\r\n !firstLine.startsWith(\" \");\r\n\r\n const lang = hasLangLine ? firstLine : undefined;\r\n const codeLines = hasLangLine ? lines.slice(1) : lines;\r\n\r\n // 空内容\r\n if (codeLines.length === 0) {\r\n return hasLangLine ? (\r\n <Box marginLeft={2}>\r\n <Text color=\"#888888\">┌ {lang}</Text>\r\n </Box>\r\n ) : null;\r\n }\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginLeft={2} marginTop={1}>\r\n {lang && <Text color=\"#888888\">┌ {lang}</Text>}\r\n {codeLines.map((line, i) => {\r\n if (line.startsWith(\"+\")) {\r\n return (\r\n <Text key={i} color={DIFF_ADD_COLOR}>\r\n {line}\r\n </Text>\r\n );\r\n }\r\n if (line.startsWith(\"-\")) {\r\n return (\r\n <Text key={i} color={DIFF_DEL_COLOR}>\r\n {line}\r\n </Text>\r\n );\r\n }\r\n if (line.startsWith(\"@@\")) {\r\n return (\r\n <Text key={i} color={DIFF_HUNK_COLOR}>\r\n {line}\r\n </Text>\r\n );\r\n }\r\n // 上下文行或非 diff 代码行\r\n return <Text key={i}>{line}</Text>;\r\n })}\r\n </Box>\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 表格渲染(计算逻辑已抽到 table-layout.ts,本文件仅负责把结果印到 Ink)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 渲染 Markdown 表格为终端框线表格。\r\n *\r\n * 输出示例(终端宽度足够时):\r\n * ┌──────────┬──────────────────────────┬──────────────────────────┐\r\n * │ 位置 │ 原内容 │ 新内容 │\r\n * ├──────────┼──────────────────────────┼──────────────────────────┤\r\n * │ 第 12 行 │ interface FileDiffs { │ interface Diffs { │\r\n * └──────────┴──────────────────────────┴──────────────────────────┘\r\n *\r\n * 行为:\r\n * - 列宽跟随终端宽度自适应(不再硬编码 90)\r\n * - 单 cell 超长时按视觉宽度自动换行;表格行高 = 该行最大 wrap 行数\r\n * - 内容不会被截断丢失(不像之前的 cell.slice)\r\n * - 终端宽度未知时使用 80 列兜底\r\n */\r\nfunction TableRenderer({ text }: { text: string }): ReactNode {\r\n const termWidth = detectTermWidth();\r\n const layout = layoutTable(text, { termWidth });\r\n\r\n // 无效输入(无分隔行)→ 降级为纯文本\r\n if (layout.colWidths.length === 0) {\r\n return <Text>{text}</Text>;\r\n }\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n {layout.lines.map((line, i) => {\r\n // 框线(top / mid / bot)用灰色;数据行用默认色\r\n const isFirst = i === 0;\r\n const isLast = i === layout.lines.length - 1;\r\n const isMid = !isFirst && !isLast && line.startsWith(\"├\");\r\n const color = isFirst || isLast || isMid ? \"#888888\" : undefined;\r\n return (\r\n <Text key={i} color={color}>{line}</Text>\r\n );\r\n })}\r\n </Box>\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Emoji 检测 — 跳过图标字符的颜色包裹\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 匹配 emoji 相关 code point 的正则 */\r\nconst 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;\r\n\r\n/**\r\n * 检测一个 grapheme cluster 是否属于 emoji 或图标字符。\r\n * 此类字符在 bold 渲染中应跳过颜色包裹,避免终端渲染异常。\r\n */\r\nfunction isEmojiCluster(text: string): boolean {\r\n return EMOJI_CP_RE.test(text);\r\n}\r\n\r\n/**\r\n * 将文本按 grapheme cluster 分割,标记每个 cluster 是否为 emoji。\r\n * 降级策略:若 Intl.Segmenter 不可用,将整个文本视为纯文本。\r\n */\r\nfunction splitEmojiClusters(\r\n text: string,\r\n): Array<{ text: string; isEmoji: boolean }> {\r\n try {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const segmenter = new (Intl as any).Segmenter(\"en\", {\r\n granularity: \"grapheme\",\r\n });\r\n const segments = [...segmenter.segment(text)];\r\n return segments.map((s) => ({\r\n text: s.segment,\r\n isEmoji: isEmojiCluster(s.segment),\r\n }));\r\n } catch {\r\n // 降级:整个文本作为一个纯文本块\r\n return [{ text, isEmoji: false }];\r\n }\r\n}\r\n\r\n/**\r\n * 渲染 bold(紫色)文本,对 emoji 字符跳过颜色包裹。\r\n */\r\nfunction renderBoldText(text: string, key: number | string): ReactNode {\r\n const clusters = splitEmojiClusters(text);\r\n\r\n // 不含 emoji — 简单路径(和之前一致)\r\n if (clusters.length === 1 && !clusters[0]!.isEmoji) {\r\n return (\r\n <Text key={key} color={BOLD_COLOR}>\r\n {text}\r\n </Text>\r\n );\r\n }\r\n\r\n // 合并相邻的同类 cluster 减少嵌套\r\n const groups: Array<{ text: string; isEmoji: boolean }> = [];\r\n for (const c of clusters) {\r\n const last = groups[groups.length - 1];\r\n if (last && last.isEmoji === c.isEmoji) {\r\n last.text += c.text;\r\n } else {\r\n groups.push({ text: c.text, isEmoji: c.isEmoji });\r\n }\r\n }\r\n\r\n return (\r\n <Text key={key}>\r\n {groups.map((g, i) =>\r\n g.isEmoji ? (\r\n g.text\r\n ) : (\r\n <Text key={i} color={BOLD_COLOR}>\r\n {g.text}\r\n </Text>\r\n ),\r\n )}\r\n </Text>\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 高亮颜色常量\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 行内代码高亮色 — 天蓝 */\r\nconst CODE_COLOR = \"#00BFFF\";\r\n/** 加粗强调色 — 紫色 */\r\nconst BOLD_COLOR = \"#A855F7\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 主组件\r\n// ---------------------------------------------------------------------------\r\n\r\ninterface HighlightedTextProps {\r\n children: string;\r\n}\r\n\r\n/**\r\n * 渲染 AI 回复文本,支持四种标记高亮:\r\n * - `` `code` `` → 天蓝色\r\n * - `**bold**` → 紫色\r\n * - ```` ```diff ```` → 独立块,`+` 绿色 / `-` 红色 / `@@` 青色\r\n * - `| ... |` Markdown → 框线表格\r\n */\r\nexport function HighlightedText({ children: text }: HighlightedTextProps): ReactNode {\r\n // 第 0 遍:提取 ```...``` 代码块\r\n const codeBlockSegments = parseCodeBlocks(text);\r\n\r\n // 第 0.5 遍:在非代码块的 plain 段中检测 Markdown 表格\r\n const tableSegments: Segment[] = [];\r\n for (const seg of codeBlockSegments) {\r\n if (seg.type === \"code_block\") {\r\n tableSegments.push(seg);\r\n } else {\r\n tableSegments.push(...parseTables(seg.text));\r\n }\r\n }\r\n\r\n // 第 1 遍:在 inline plain 段内解析 **bold**\r\n const boldSegments: Segment[] = [];\r\n for (const seg of tableSegments) {\r\n if (seg.type === \"code_block\" || seg.type === \"table\") {\r\n boldSegments.push(seg);\r\n } else {\r\n boldSegments.push(...parseBoldPairs(seg.text));\r\n }\r\n }\r\n\r\n // 第 2 遍:在 inline plain 段内解析 `code`\r\n const segments = parseCodePairs(boldSegments);\r\n\r\n // 检查是否包含块级元素\r\n const hasBlock = segments.some(\r\n (s) => s.type === \"code_block\" || s.type === \"table\",\r\n );\r\n\r\n // ---- 无块级元素:纯内联渲染 ----\r\n if (!hasBlock) {\r\n const isSimple = segments.length === 1 && segments[0]!.type === \"plain\";\r\n if (isSimple) {\r\n return <Text wrap=\"wrap\">{text}</Text>;\r\n }\r\n\r\n return (\r\n <Text wrap=\"wrap\">\r\n {segments.map((seg, i) => {\r\n if (seg.type === \"code\") {\r\n return (\r\n <Text key={i} color={CODE_COLOR}>\r\n {seg.text}\r\n </Text>\r\n );\r\n }\r\n if (seg.type === \"bold\") {\r\n return renderBoldText(seg.text, i);\r\n }\r\n return seg.text;\r\n })}\r\n </Text>\r\n );\r\n }\r\n\r\n // ---- 有块级元素:分组渲染(inline 组 + 块级渲染) ----\r\n const rendered: ReactNode[] = [];\r\n let inlineGroup: Segment[] = [];\r\n\r\n function flushInline(groupIdx: number) {\r\n if (inlineGroup.length === 0) return;\r\n const isSimpleInline =\r\n inlineGroup.length === 1 && inlineGroup[0]!.type === \"plain\";\r\n if (isSimpleInline) {\r\n rendered.push(\r\n <Text key={groupIdx} wrap=\"wrap\">\r\n {inlineGroup[0]!.text}\r\n </Text>,\r\n );\r\n } else {\r\n rendered.push(\r\n <Text key={groupIdx} wrap=\"wrap\">\r\n {inlineGroup.map((seg, j) => {\r\n if (seg.type === \"code\") {\r\n return (\r\n <Text key={j} color={CODE_COLOR}>\r\n {seg.text}\r\n </Text>\r\n );\r\n }\r\n if (seg.type === \"bold\") {\r\n return renderBoldText(seg.text, j);\r\n }\r\n return seg.text;\r\n })}\r\n </Text>,\r\n );\r\n }\r\n inlineGroup = [];\r\n }\r\n\r\n for (const seg of segments) {\r\n if (seg.type === \"code_block\") {\r\n flushInline(rendered.length);\r\n rendered.push(\r\n <CodeBlockRenderer key={`b${rendered.length}`} code={seg.text} />,\r\n );\r\n } else if (seg.type === \"table\") {\r\n flushInline(rendered.length);\r\n rendered.push(\r\n <TableRenderer key={`t${rendered.length}`} text={seg.text} />,\r\n );\r\n } else {\r\n inlineGroup.push(seg);\r\n }\r\n }\r\n flushInline(rendered.length);\r\n\r\n return <Box flexDirection=\"column\">{rendered}</Box>;\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// Table layout — 纯计算模块(无 React/无 IO,方便单测)\r\n//\r\n// 设计目标:\r\n// - 终端宽度自适应:不再硬编码最大宽度,改为读取 stdout.columns\r\n// - 单 cell 超长时按视觉宽度换行(不截断),表格行高自适应\r\n// - 中文字符宽度补全(0x3000 系列 CJK 标点 / ZWJ / 全角空格等)\r\n// - 函数全 @pure,方便单测覆盖布局算法\r\n//\r\n// 函数注释规范见仓库根 AGENTS.md「函数注释规范」一节。\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 表格列最小宽度(视觉宽度) */\r\nexport const TABLE_MIN_COL_WIDTH = 3;\r\n\r\n/** 默认终端宽度(process.stdout.columns 不可用时的兜底) */\r\nexport const DEFAULT_TERM_WIDTH = 80;\r\n\r\n/** 表格在终端里左右各留的边距(视觉宽度),避免贴边 */\r\nexport const TABLE_MARGIN = 2;\r\n\r\n/** 对齐方式 */\r\nexport type Alignment = \"left\" | \"center\" | \"right\";\r\n\r\n/** 表格布局选项 */\r\nexport interface TableLayoutOptions {\r\n /** 终端可用宽度(含 margin 前的总宽),默认 80 */\r\n termWidth?: number;\r\n /** 表格本身左右各留多少列(默认 2),留 0 可贴边 */\r\n outerMargin?: number;\r\n}\r\n\r\n/** 表格布局结果:所有要按行打印的字符串(含框线) */\r\nexport interface TableLayout {\r\n /** 总行数(含 top/mid/bot 框线) */\r\n lines: string[];\r\n /** 计算出的列宽(视觉宽度,含 margin 不含 │) */\r\n colWidths: number[];\r\n /** 表格总视觉宽度 */\r\n totalWidth: number;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 视觉宽度\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 单个码点在终端里的显示宽度。\r\n *\r\n * CJK / 全角 / Emoji 算 2 格;其他算 1 格。\r\n *\r\n * @param cp — Unicode 码点\r\n * @returns 显示宽度\r\n */\r\nfunction cpWidth(cp: number): number {\r\n if (\r\n (cp >= 0x1100 && cp <= 0x115F) || // Hangul Jamo\r\n cp === 0x2329 || cp === 0x232A ||\r\n (cp >= 0x2E80 && cp <= 0xA4CF) || // CJK Radicals ~ Yi\r\n (cp >= 0xA960 && cp <= 0xA97F) || // Hangul Extended\r\n (cp >= 0xAC00 && cp <= 0xD7AF) || // Hangul Syllables\r\n (cp >= 0xF900 && cp <= 0xFAFF) || // CJK Compat\r\n (cp >= 0xFE10 && cp <= 0xFE1F) || // Vertical forms\r\n (cp >= 0xFF01 && cp <= 0xFF60) || // Fullwidth ASCII / 标点\r\n (cp >= 0xFFE0 && cp <= 0xFFE6) || // Fullwidth signs\r\n (cp >= 0x1F000 && cp <= 0x1FFFF) || // Emoji / Symbols\r\n (cp >= 0x3000 && cp <= 0x303F) || // CJK Symbols & Punctuation(含「、。·」等)\r\n (cp >= 0x20000 && cp <= 0x2FFFF) // CJK Ext B~F + 罕用区\r\n ) {\r\n return 2;\r\n }\r\n // 控制字符不算宽度\r\n if (cp < 0x20 || (cp >= 0x7F && cp < 0xA0)) return 0;\r\n return 1;\r\n}\r\n\r\n/**\r\n * 字符串在终端里的视觉宽度。\r\n * 跳过零宽连接符(ZWJ 0x200D)和变体选择符(FE0F~FE0F)——这些是组合 emoji 的辅助字符,不占格。\r\n *\r\n * @param text — 任意字符串\r\n * @returns 视觉宽度\r\n */\r\nexport function visualWidth(text: string): number {\r\n let w = 0;\r\n for (const ch of text) {\r\n const cp = ch.codePointAt(0);\r\n if (cp === undefined) continue;\r\n if (cp === 0x200D) continue; // ZWJ\r\n if (cp >= 0xFE00 && cp <= 0xFE0F) continue; // Variation selectors\r\n w += cpWidth(cp);\r\n }\r\n return w;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 字符串按视觉宽度切分\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 遍历字符串,产出「码点 + 视觉宽度增量」。用于 wrap / slice 的按视觉宽度操作。\r\n */\r\nfunction* iterateChars(text: string): Generator<{ ch: string; width: number }> {\r\n for (const ch of text) {\r\n const cp = ch.codePointAt(0);\r\n if (cp === undefined) continue;\r\n if (cp === 0x200D) continue;\r\n if (cp >= 0xFE00 && cp <= 0xFE0F) continue;\r\n yield { ch, width: cpWidth(cp) };\r\n }\r\n}\r\n\r\n/**\r\n * 在给定的视觉宽度限制内切分字符串为多行(不丢字符)。\r\n *\r\n * 策略:\r\n * - 在空格 / 零宽非断字点处优先换行;找不到则在任意字符处硬切\r\n * - 末行若仍超宽,逐字符截到 maxWidth\r\n *\r\n * @param text — 原文本(可含 CJK / 换行)\r\n * @param maxWidth — 每行最大视觉宽度\r\n * @returns 切分后的多行\r\n */\r\nexport function wrapByWidth(text: string, maxWidth: number): string[] {\r\n if (maxWidth <= 0) return [text];\r\n\r\n // 先按 \\n 切,再对每段做视觉宽度 wrap\r\n const out: string[] = [];\r\n for (const para of text.split(\"\\n\")) {\r\n if (para.length === 0) {\r\n out.push(\"\");\r\n continue;\r\n }\r\n const wrapped = wrapOneLine(para, maxWidth);\r\n for (const w of wrapped) out.push(w);\r\n }\r\n return out.length === 0 ? [\"\"] : out;\r\n}\r\n\r\nfunction wrapOneLine(text: string, maxWidth: number): string[] {\r\n if (visualWidth(text) <= maxWidth) return [text];\r\n\r\n const lines: string[] = [];\r\n let current = \"\";\r\n let currentW = 0;\r\n // 候选断点:在最近的「空格后」处换行(保留英文 / 拼音整词)\r\n let breakCandidate = -1; // current 中最后一个空格的位置\r\n let breakCandidateW = 0;\r\n\r\n for (const { ch, width } of iterateChars(text)) {\r\n if (ch === \" \" || ch === \"\\t\") {\r\n // 记录断点:若后续再加字符会超宽,就在这里切\r\n breakCandidate = current.length;\r\n breakCandidateW = currentW + width;\r\n current += ch;\r\n currentW += width;\r\n continue;\r\n }\r\n if (currentW + width > maxWidth) {\r\n // 需要换行\r\n if (breakCandidate > 0) {\r\n // 在上一个空格处切\r\n const split = current.slice(0, breakCandidate).replace(/[ \\t]+$/, \"\");\r\n lines.push(split);\r\n const rest = current.slice(breakCandidate).replace(/^[ \\t]+/, \"\");\r\n current = rest + ch;\r\n currentW = visualWidth(current);\r\n breakCandidate = -1;\r\n breakCandidateW = 0;\r\n } else {\r\n // 硬切:当前字符单独成行\r\n lines.push(current);\r\n current = ch;\r\n currentW = width;\r\n }\r\n } else {\r\n current += ch;\r\n currentW += width;\r\n }\r\n }\r\n if (current.length > 0) lines.push(current);\r\n return lines;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 表格布局\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 把单行单元格按列宽换行成等高的多行,返回每行最终显示的字符串数组。\r\n * 不足行用空格补齐。\r\n *\r\n * @param cells — 单元格内容\r\n * @param colWidths — 每列宽度(视觉宽度)\r\n * @param alignments — 每列对齐方式\r\n * @returns 与原 cells 等长的、可能含多行的字符串数组\r\n */\r\nfunction wrapRowCells(\r\n cells: string[],\r\n colWidths: number[],\r\n alignments: Alignment[],\r\n): string[][] {\r\n return cells.map((cell, ci) => {\r\n const w = colWidths[ci] ?? TABLE_MIN_COL_WIDTH;\r\n const lines = wrapByWidth(cell, w);\r\n return lines.map((l) => padToWidth(l, w, alignments[ci] ?? \"left\"));\r\n });\r\n}\r\n\r\n/**\r\n * 按视觉宽度和对齐方式把字符串填充到目标宽度。\r\n *\r\n * @param text — 原文本\r\n * @param targetWidth — 目标视觉宽度\r\n * @param align — 对齐方式\r\n * @returns 填充后的字符串\r\n */\r\nexport function padToWidth(\r\n text: string,\r\n targetWidth: number,\r\n align: Alignment = \"left\",\r\n): string {\r\n const vw = visualWidth(text);\r\n const delta = targetWidth - vw;\r\n if (delta <= 0) return text;\r\n const leftPad = align === \"right\" ? delta : align === \"center\" ? Math.floor(delta / 2) : 0;\r\n const rightPad = delta - leftPad;\r\n return \" \".repeat(leftPad) + text + \" \".repeat(rightPad);\r\n}\r\n\r\n/**\r\n * 解析 Markdown 表格一行为单元格数组。\r\n *\r\n * @param line — 类似 `| a | b |` 的行\r\n * @returns 单元格内容数组\r\n */\r\nexport function parseTableCells(line: string): string[] {\r\n const t = line.trim();\r\n if (!t.startsWith(\"|\") || !t.endsWith(\"|\")) return [];\r\n const inner = t.slice(1, t.length - 1);\r\n return inner.split(\"|\").map((c) => c.trim());\r\n}\r\n\r\n/**\r\n * 从分隔行(如 `| :--- | :---: | ---: |`)解析每列对齐方式。\r\n *\r\n * @param sepLine — 分隔行\r\n * @returns 每列的对齐方式数组\r\n */\r\nexport function parseAlignments(sepLine: string): Alignment[] {\r\n return parseTableCells(sepLine).map((c) => {\r\n const l = c.startsWith(\":\");\r\n const r = c.endsWith(\":\");\r\n if (l && r) return \"center\";\r\n if (r) return \"right\";\r\n return \"left\";\r\n });\r\n}\r\n\r\n/**\r\n * 把多行原表格内容(含表头 / 分隔行 / 数据行)布局成终端框线表格。\r\n *\r\n * 行为:\r\n * 1. 列宽优先用各列最长 cell 的视觉宽度\r\n * 2. 超出终端可用宽度 → 等比缩列宽,下限 TABLE_MIN_COL_WIDTH\r\n * 3. 单 cell 仍超过列宽 → 在列内按视觉宽度 wrap 成多行;表格行高 = 该行最大 wrap 行数\r\n * 4. 内容绝不截断(不像 slice)\r\n *\r\n * @param text — 表格原始多行文本(含分隔行)\r\n * @param options — 布局选项\r\n * @returns 布局结果(已包含 top / mid / bot 框线)\r\n */\r\nexport function layoutTable(text: string, options: TableLayoutOptions = {}): TableLayout {\r\n const termWidth = options.termWidth ?? DEFAULT_TERM_WIDTH;\r\n const outerMargin = options.outerMargin ?? 0; // 表格整体在终端中的左右边距,由调用方负责\r\n const usable = Math.max(termWidth - outerMargin, 20);\r\n\r\n const rawLines = text.split(\"\\n\").filter((l) => l.trim().length > 0);\r\n if (rawLines.length < 2) {\r\n return { lines: rawLines, colWidths: [], totalWidth: 0 };\r\n }\r\n\r\n // 找分隔行\r\n let sepIdx = -1;\r\n for (let k = 0; k < rawLines.length; k++) {\r\n const t = (rawLines[k] ?? \"\").trim();\r\n if (/^\\|[-: |]+\\|$/.test(t) && t.includes(\"-\")) {\r\n sepIdx = k;\r\n break;\r\n }\r\n }\r\n if (sepIdx === -1) {\r\n return { lines: rawLines, colWidths: [], totalWidth: 0 };\r\n }\r\n\r\n // 解析表头、对齐、数据行\r\n const headerCells = parseTableCells(rawLines.slice(0, sepIdx).join(\"\"));\r\n const alignments = parseAlignments(rawLines[sepIdx] ?? \"\");\r\n const dataRows = rawLines.slice(sepIdx + 1).map(parseTableCells);\r\n const colCount = headerCells.length;\r\n if (colCount === 0) {\r\n return { lines: rawLines, colWidths: [], totalWidth: 0 };\r\n }\r\n\r\n // 每列最大视觉宽度\r\n const colMaxWidths: number[] = [];\r\n for (let ci = 0; ci < colCount; ci++) {\r\n let max = visualWidth(headerCells[ci] ?? \"\");\r\n for (const row of dataRows) {\r\n const w = visualWidth(row[ci] ?? \"\");\r\n if (w > max) max = w;\r\n }\r\n colMaxWidths.push(Math.max(max, TABLE_MIN_COL_WIDTH));\r\n }\r\n\r\n // 列宽决策:usable 内尽量用 colMaxWidths,超出再等比缩\r\n const margin = TABLE_MARGIN;\r\n const totalMinWidth = colMaxWidths.reduce((s, w) => s + w + margin, 1); // +1 for leading │\r\n let colWidths: number[];\r\n if (totalMinWidth <= usable) {\r\n colWidths = colMaxWidths;\r\n } else {\r\n // 等比缩\r\n const available = usable - 1 - margin * colCount;\r\n const sum = colMaxWidths.reduce((s, w) => s + w, 0);\r\n colWidths = colMaxWidths.map((w) =>\r\n Math.max(TABLE_MIN_COL_WIDTH, Math.floor((w / sum) * available)),\r\n );\r\n // 修正舍入误差:若总宽仍超,再缩最后一列\r\n let total = colWidths.reduce((s, w) => s + w + margin, 1);\r\n while (total > usable && colWidths.length > 0) {\r\n const last = colWidths.length - 1;\r\n const cur = colWidths[last] ?? TABLE_MIN_COL_WIDTH;\r\n if (cur <= TABLE_MIN_COL_WIDTH) break;\r\n colWidths[last] = cur - 1;\r\n total = colWidths.reduce((s, w) => s + w + margin, 1);\r\n }\r\n }\r\n\r\n // 框线字符\r\n const H = \"─\";\r\n const makeSep = (l: string, m: string, r: string) =>\r\n l + colWidths.map((w) => H.repeat(w + margin)).join(m) + r;\r\n\r\n const topBorder = makeSep(\"┌\", \"┬\", \"┐\");\r\n const midBorder = makeSep(\"├\", \"┼\", \"┤\");\r\n const botBorder = makeSep(\"└\", \"┴\", \"┘\");\r\n\r\n // 渲染一行(含 wrap)\r\n function buildRow(cells: string[]): string[] {\r\n const wrapped = wrapRowCells(cells, colWidths, alignments);\r\n const rowHeight = Math.max(1, ...wrapped.map((lines) => lines.length));\r\n const lines: string[] = [];\r\n for (let li = 0; li < rowHeight; li++) {\r\n // 数据行总视觉宽 = \"│\" + sum(\" \" + segment + \" \") + \"│\" + 各列中间额外的\"│\"\r\n // 简化:每列拼 \" \" + segment + \" │\",末列单独拼 \" \" + segment + \"│\"\r\n let row = \"│\";\r\n for (let ci = 0; ci < colCount; ci++) {\r\n const cellLines = wrapped[ci] ?? [];\r\n const segment = cellLines[li] ?? \" \".repeat(colWidths[ci] ?? TABLE_MIN_COL_WIDTH);\r\n row += \" \" + segment + \" \";\r\n if (ci < colCount - 1) row += \"│\";\r\n }\r\n row += \"│\";\r\n lines.push(row);\r\n }\r\n return lines;\r\n }\r\n\r\n const headerRow = buildRow(headerCells);\r\n const dataRowsOut = dataRows.map(buildRow);\r\n const totalWidth = colWidths.reduce((s, w) => s + w + margin, 1);\r\n\r\n return {\r\n lines: [\r\n topBorder,\r\n ...headerRow,\r\n midBorder,\r\n ...dataRowsOut.flat(),\r\n botBorder,\r\n ],\r\n colWidths,\r\n totalWidth,\r\n };\r\n}\r\n\r\n/**\r\n * 探测当前进程 stdout 宽度(同步、SSR 安全)。失败时返回 undefined。\r\n */\r\nexport function detectTermWidth(): number | undefined {\r\n const c = (process.stdout as { columns?: number } | undefined)?.columns;\r\n return typeof c === \"number\" && c > 0 ? c : undefined;\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 思考链展示工具\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 独立框最多保留多少行;超过则折叠为单行省略提示 */\r\nexport const DEFAULT_REASONING_MAX_LINES = 8;\r\n\r\n/**\r\n * 把多段思考链拼接为单段文本,段之间用单换行连接(不空行)。\r\n *\r\n * 配合“滚动窗口”使用:思考内容越多越空行越浪费一行,改为紧凑连接,\r\n * 整个思考信息统一在一个框内呈现。自动过滤空段和纯空白段。\r\n */\r\nexport function joinReasoningSegments(segments: string[]): string {\r\n return segments\r\n .map((s) => s.trim())\r\n .filter((s) => s.length > 0)\r\n .join(\"\\n\");\r\n}\r\n\r\n/**\r\n * 按行\"滚动\"截断文本,超过 maxLines 时保留**末尾** maxLines 行(最新思考),\r\n * 丢弃开头的旧行。与终端自动滚屏语义一致。\r\n *\r\n * @returns\r\n * - visible: 要展示的字符串(已用 \\n 重组)\r\n * - hiddenLines: 被滚出窗口的旧行数(0 表示未截断;保留供未来统计 / 调试)\r\n * - totalLines: 原始总行数\r\n *\r\n * @pure\r\n */\r\nexport function truncateReasoningLines(\r\n text: string,\r\n maxLines: number,\r\n): { visible: string; hiddenLines: number; totalLines: number } {\r\n const lines = text.split(\"\\n\");\r\n if (lines.length <= maxLines) {\r\n return { visible: text, hiddenLines: 0, totalLines: lines.length };\r\n }\r\n return {\r\n visible: lines.slice(-maxLines).join(\"\\n\"),\r\n hiddenLines: lines.length - maxLines,\r\n totalLines: lines.length,\r\n };\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// Diff 预览组件 — 终端中渲染带色彩的 unified diff\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\nimport type { FileDiff } from \"../tool/types.js\";\r\n\r\ninterface DiffPreviewProps {\r\n /** FileDiff 对象 */\r\n diff: FileDiff;\r\n}\r\n\r\n/**\r\n * 解析 unified diff 文本,按行着色渲染。\r\n *\r\n * 颜色方案:\r\n * - `@@ ... @@` → 青色(hunk 头)\r\n * - `+` 添加行 → 绿色\r\n * - `-` 删除行 → 红色\r\n * - ` ` 上下文行 → 灰色\r\n *\r\n * 注意:文件头“--- a/...” / “+++ b/...”由摘要行展示,不重复渲染。\r\n */\r\nexport function DiffPreview({ diff }: DiffPreviewProps) {\r\n const { patch, additions, deletions, existedBefore, filePath } = diff;\r\n\r\n // 无变更\r\n if (!patch || patch.length === 0) {\r\n return null;\r\n }\r\n\r\n const lines = patch.split(\"\\n\");\r\n const fileName = filePath.replace(/\\\\/g, \"/\").split(\"/\").pop() ?? filePath;\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n {/* 摘要行 */}\r\n <Box flexDirection=\"row\" gap={1}>\r\n <Text bold color=\"#00ffff\">\r\n 📝 {existedBefore ? \"修改\" : \"新建\"}:\r\n </Text>\r\n <Text bold>{fileName}</Text>\r\n <Text color=\"#555555\">\r\n (+{additions} -{deletions})\r\n </Text>\r\n </Box>\r\n\r\n {/* Diff 内容(跳过文件头两行“--- a/x” / \"+++ b/x\",避免与摘要行重复展示文件名) */}\r\n <Box flexDirection=\"column\" marginLeft={2}>\r\n {lines\r\n .filter((line) => !line.startsWith(\"---\") && !line.startsWith(\"+++\"))\r\n .map((line, i) => (\r\n <DiffLine key={i} line={line} />\r\n ))}\r\n </Box>\r\n </Box>\r\n );\r\n}\r\n\r\n/** 渲染单行 diff 内容 */\r\nfunction DiffLine({ line }: { line: string }) {\r\n // 文件头行(如 \"--- a/path\" / \"+++ b/path\" / \"--- /dev/null\")\r\n // 摘要行已展示文件名与新建/修改状态,无需重复渲染\r\n if (line.startsWith(\"---\") || line.startsWith(\"+++\")) {\r\n return null;\r\n }\r\n\r\n // Hunk 头行 @@ -x,y +a,b @@\r\n if (line.startsWith(\"@@\")) {\r\n // 将 @@ 标记和行号范围分别着色\r\n return <Text color=\"#00cccc\">{line}</Text>;\r\n }\r\n\r\n // 添加行\r\n if (line.startsWith(\"+\")) {\r\n return <Text color=\"#22c55e\">{line}</Text>;\r\n }\r\n\r\n // 删除行\r\n if (line.startsWith(\"-\")) {\r\n return <Text color=\"#ef4444\">{line}</Text>;\r\n }\r\n\r\n // 上下文行(空格开头或空行)\r\n return <Text color=\"#6b7280\">{line}</Text>;\r\n}","// ---------------------------------------------------------------------------\r\n// SkillSelector — 用户输入 / 时渲染的 skill 列表组件\r\n// 通过 selectedIndex 高亮当前选中项,按 Tab 可补全 skill 名称\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\nimport type { SkillInfo } from \"../cli/skill-import.js\";\r\n\r\nconst HIGHLIGHT_COLOR = \"#00bfff\";\r\n\r\ninterface SkillSelectorProps {\r\n /** 所有可用 skill 列表 */\r\n skills: SkillInfo[];\r\n /** 当前输入框文本 */\r\n input: string;\r\n /** 当前选中的 skill 索引 */\r\n selectedIndex: number;\r\n}\r\n\r\nexport function SkillSelector({ skills, input, selectedIndex }: SkillSelectorProps) {\r\n // 匹配规则:/ 必须在输入开头,或者前面有空格\r\n const match = input.match(/(?:^|\\s)\\/([^/]*)$/);\r\n if (!match) return null;\r\n\r\n const query = match[1]!.toLowerCase().trim();\r\n if (skills.length === 0) return null;\r\n\r\n // 斜杠在开头且无后续内容时,展示全部 skill 作为提示\r\n const matched = !query && input.startsWith(\"/\")\r\n ? skills.slice(0, 3)\r\n : skills\r\n .filter((s) => s.name.toLowerCase().includes(query))\r\n .slice(0, 3);\r\n\r\n // 精确匹配时表示已补全完成,不显示列表\r\n if (query && matched.some((s) => s.name.toLowerCase() === query)) return null;\r\n\r\n if (matched.length === 0) return null;\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginLeft={4} marginTop={1}>\r\n <Text color=\"#808080\" dimColor>\r\n 支持的 Skill:\r\n </Text>\r\n {matched.map((skill, i) => (\r\n <Box key={skill.name}>\r\n <Text color={i === selectedIndex ? HIGHLIGHT_COLOR : \"#808080\"}>\r\n {i === selectedIndex ? \" › \" : \" \"}{skill.name}\r\n </Text>\r\n </Box>\r\n ))}\r\n <Box marginTop={1}>\r\n <Text color=\"#808080\" dimColor>\r\n ↑↓ 选择 · Tab 补全\r\n </Text>\r\n </Box>\r\n </Box>\r\n );\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// FileSelector — 用户输入 @ 时渲染的文件列表组件\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\n\r\ninterface FileSelectorProps {\r\n /** 所有项目文件路径列表(相对于 CWD) */\r\n files: string[];\r\n /** 当前输入框文本 */\r\n input: string;\r\n /** 当前选中的文件索引 */\r\n selectedIndex: number;\r\n}\r\n\r\nconst HIGHLIGHT_COLOR = \"#00ff41\";\r\n\r\nexport function FileSelector({ files, input, selectedIndex }: FileSelectorProps) {\r\n // 匹配规则:@ 必须在输入开头,或者前面有空格\r\n const match = input.match(/(?:^|\\s)@([^@]*)$/);\r\n if (!match) return null;\r\n\r\n const query = match[1]!.toLowerCase().trim();\r\n if (files.length === 0) return null;\r\n\r\n // @ 在开头且无后续内容时,展示前 5 个文件作为提示\r\n const matched = !query && input.startsWith(\"@\")\r\n ? files.slice(0, 5)\r\n : files\r\n .filter((f) => f.toLowerCase().includes(query))\r\n .slice(0, 5);\r\n\r\n // 精确匹配时表示已补全完成,不显示列表\r\n if (query && matched.some((f) => f.toLowerCase() === query)) return null;\r\n\r\n if (matched.length === 0) return null;\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginLeft={4} marginTop={1}>\r\n <Text color=\"#808080\" dimColor>\r\n 项目文件:\r\n </Text>\r\n {matched.map((file, i) => (\r\n <Box key={file}>\r\n <Text color={i === selectedIndex ? HIGHLIGHT_COLOR : \"#808080\"}>\r\n {i === selectedIndex ? \" › \" : \" \"}{file}\r\n </Text>\r\n </Box>\r\n ))}\r\n <Box marginTop={1}>\r\n <Text color=\"#808080\" dimColor>\r\n ↑↓ 选择 · Tab 补全\r\n </Text>\r\n </Box>\r\n </Box>\r\n );\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// TodoListPanel — 把 TodoList 的当前状态渲染成\"任务进度\"面板\r\n//\r\n// 设计:\r\n// - 每行格式:` <图标> <步骤名>`\r\n// - running 状态在图标位置用 <Spinner/>(持续动画)替代静态三角\r\n// - done 用绿色对勾(✅)、failed 用红色(✗)、其余用 dimColor\r\n// - 不显示 # 编号(终端不渲染 #)\r\n// - 不显示 evidence / reason(保持清爽)\r\n// - 不折叠已完成(用户要求全部展示)\r\n// - todo 为空时不渲染(避免空面板噪音)\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\nimport InkSpinner from \"ink-spinner\";\r\nimport type { TodoItem, TodoStatus } from \"../harness/todo-list.js\";\r\n\r\ninterface TodoListPanelProps {\r\n items: ReadonlyArray<TodoItem>;\r\n}\r\n\r\n/** 非 running 状态用静态符号;running 单独走 Spinner 组件以保持动画 */\r\nfunction statusIcon(status: TodoStatus): string {\r\n switch (status) {\r\n case \"pending\": return \"☐\";\r\n case \"done\": return \"☑\";\r\n case \"failed\": return \"✗\";\r\n case \"skipped\": return \"⊘\";\r\n // running 不走这里,由 TodoIcon 走 Spinner 组件\r\n case \"running\": return \"▶\";\r\n }\r\n}\r\n\r\n/**\r\n * 单行图标渲染:running 走动画 Spinner,其余走静态符号。\r\n * 抽成独立组件以让 React 识别 props 变化并重绘动画帧。\r\n */\r\nfunction TodoIcon({ status }: { status: TodoStatus }) {\r\n if (status === \"running\") {\r\n return (\r\n <Text color=\"cyan\">\r\n <InkSpinner type=\"dots\" />\r\n </Text>\r\n );\r\n }\r\n // done 用绿色对勾,与 running 的 spinner / 静态状态在颜色上区分\r\n if (status === \"done\") {\r\n return <Text color=\"green\">✅</Text>;\r\n }\r\n // failed 用红色,其它状态用 dimColor\r\n if (status === \"failed\") {\r\n return <Text color=\"red\">{statusIcon(status)}</Text>;\r\n }\r\n return <Text dimColor>{statusIcon(status)}</Text>;\r\n}\r\n\r\n/**\r\n * 把 todo 列表渲染成多行\"任务进度\"面板。\r\n * 步骤按原列表顺序展示(id 升序),不做截断 / 折叠。\r\n */\r\nexport function TodoListPanel({ items }: TodoListPanelProps) {\r\n if (items.length === 0) return null;\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n <Box>\r\n <Text dimColor>🔧 任务进度</Text>\r\n </Box>\r\n {items.map((it) => (\r\n <Box key={it.id}>\r\n <Text dimColor>{\" \"}</Text>\r\n <TodoIcon status={it.status} />\r\n <Text dimColor>{` ${it.content}`}</Text>\r\n </Box>\r\n ))}\r\n </Box>\r\n );\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// Provider 注册表 — 工厂模式管理 Provider 实例\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type { Provider, ModelId } from \"./types.js\";\r\nimport { isSupportedModel } from \"./models.js\";\r\nimport { ModelNotSupportedError } from \"./errors.js\";\r\nimport { DeepSeekProvider } from \"./deepseek.js\";\r\n\r\n/** Provider 工厂函数类型 */\r\nexport type ProviderFactory = (config: {\r\n apiKey: string;\r\n baseUrl: string;\r\n model: string;\r\n}) => Provider;\r\n\r\n/**\r\n * Provider 注册表 — 工厂模式管理 Provider 实例。\r\n *\r\n * 使用方式:\r\n * ```ts\r\n * const provider = registry.get(\"deepseek\", { apiKey, baseUrl, model });\r\n * for await (const chunk of provider.chat(messages)) { ... }\r\n * ```\r\n */\r\nexport class ProviderRegistry {\r\n readonly #factories = new Map<string, ProviderFactory>();\r\n readonly #instances = new Map<string, Provider>();\r\n\r\n /** 注册一个 Provider 工厂 */\r\n register(name: string, factory: ProviderFactory): void {\r\n this.#factories.set(name, factory);\r\n }\r\n\r\n /**\r\n * 获取或创建一个 Provider 实例(单例)。\r\n *\r\n * 相同的 name + baseUrl + model 组合会复用同一实例。\r\n * 如果 model 不在支持列表中,抛出 ModelNotSupportedError。\r\n */\r\n get(\r\n name: string,\r\n config: { apiKey: string; baseUrl: string; model: string },\r\n ): Provider {\r\n if (!isSupportedModel(config.model)) {\r\n throw new ModelNotSupportedError(config.model);\r\n }\r\n\r\n // 缓存键 = name:baseUrl:model(不同配置会创建不同实例)\r\n const cacheKey = `${name}:${config.baseUrl}:${config.model}`;\r\n\r\n const cached = this.#instances.get(cacheKey);\r\n if (cached) return cached;\r\n\r\n const factory = this.#factories.get(name);\r\n if (!factory) {\r\n const available = [...this.#factories.keys()].join(\", \");\r\n throw new Error(`未注册的 Provider: \"${name}\"。可用: ${available}`);\r\n }\r\n\r\n const instance = factory(config);\r\n this.#instances.set(cacheKey, instance);\r\n return instance;\r\n }\r\n\r\n /** 列出已注册的 Provider 名称 */\r\n list(): string[] {\r\n return [...this.#factories.keys()];\r\n }\r\n\r\n /** 清除缓存的实例(用于配置热重载) */\r\n clear(): void {\r\n this.#instances.clear();\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 全局默认注册表 + 预注册内置 Provider\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 全局默认 Provider 注册表 */\r\nexport const defaultRegistry = new ProviderRegistry();\r\n\r\ndefaultRegistry.register(\"deepseek\", (config) => {\r\n // 模型合法性已在 registry.get() 中通过 isSupportedModel 校验\r\n return new DeepSeekProvider({\r\n apiKey: config.apiKey,\r\n baseUrl: config.baseUrl,\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n model: config.model as unknown as ModelId,\r\n });\r\n});\r\n\r\n/**\r\n * 从配置快速创建 Provider 实例的快捷方式。\r\n *\r\n * 内部委托给 defaultRegistry,会在注册表中校验模型并缓存实例。\r\n */\r\nexport function createProvider(config: {\r\n name: string;\r\n apiKey: string;\r\n baseUrl: string;\r\n model: string;\r\n}): Provider {\r\n return defaultRegistry.get(config.name, config);\r\n}","// ---------------------------------------------------------------------------\r\n// 工具系统核心类型定义\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 工具的分类语义,替代原来的 boolean readOnly */\r\nexport enum ToolKind {\r\n /** 纯读操作,无副作用,可并行执行 */\r\n Read = \"read\",\r\n /** 文件/目录编辑(修改内容) */\r\n Edit = \"edit\",\r\n /** 文件/目录删除 */\r\n Delete = \"delete\",\r\n /** 移动/重命名 */\r\n Move = \"move\",\r\n /** 其他(bash、fetch 等) */\r\n Other = \"other\",\r\n}\r\n\r\n/** 工具参数的 JSON Schema 表示 */\r\nexport interface JSONSchema {\r\n type: \"object\";\r\n properties?: Record<string, unknown>;\r\n required?: string[];\r\n additionalProperties?: boolean | Record<string, unknown>;\r\n /** Schema 自带的 description,自动用作 tool description */\r\n description?: string;\r\n}\r\n\r\n/** 每次工具执行时传入的上下文 */\r\nexport interface ToolContext {\r\n /** 当前工作目录,用于路径约束和相对路径解析 */\r\n cwd: string;\r\n /** 中止信号,用于取消正在执行的工具 */\r\n signal?: AbortSignal;\r\n /** 命令执行超时(毫秒),默认 30000 */\r\n timeout?: number;\r\n /**\r\n * 写操作的允许根目录列表(绝对路径)。\r\n * 非空时,写工具必须确保目标路径在其中一个根下;\r\n * 为空数组表示不限制(保持兼容)。\r\n */\r\n writeRoots?: string[];\r\n}\r\n\r\n/** 文件变更的 diff 信息 */\r\nexport interface FileDiff {\r\n /** 文件路径(绝对路径) */\r\n filePath: string;\r\n /** unified diff 文本 */\r\n patch: string;\r\n /** 变更前是否存在(新建文件时为 false) */\r\n existedBefore: boolean;\r\n /** 变更统计:新增行数 */\r\n additions: number;\r\n /** 变更统计:删除行数 */\r\n deletions: number;\r\n}\r\n\r\n/**\r\n * 工具执行结果。\r\n *\r\n * 设计要点:\r\n * - 成功和失败共用相同结构,LLM 总能读到可理解的 data\r\n * - error 字段仅做分类标记(如 TEXT_NOT_FOUND),不暴露内部细节\r\n * - diff 仅在文件修改工具携带\r\n * - summary 给 UI 展示,不暴露完整 data 给 UI\r\n */\r\nexport interface ToolResult {\r\n /** 是否执行成功 */\r\n success: boolean;\r\n /** 结果内容(成功时为输出,失败时为错误信息),LLM 可读 */\r\n data: string;\r\n /** 错误分类标记(仅在 success=false 时有意义) */\r\n error?: string;\r\n /** 文件变更 diff(仅文件修改工具携带) */\r\n diff?: FileDiff;\r\n /**\r\n * 给 UI 展示的简短摘要(一行)。\r\n * 与 data 区别:data 喂给 LLM 保留完整内容,summary 仅做一行回显。\r\n * 未设置时 UI 自动降级到 data 的前 500 字符。\r\n */\r\n summary?: string;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// AgentTool — 静态类型安全的工具定义\r\n//\r\n// 每个工具定义为一个对象,提供类型安全的泛型参数。\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * AgentTool 定义 — 一个工具的完整声明。\r\n *\r\n * @template I 工具输入参数类型\r\n * @template O 工具输出类型(从 ToolResult 继承)\r\n */\r\nexport interface AgentTool<I, O extends ToolResult = ToolResult> {\r\n /** 工具名称,全局唯一标识符 */\r\n readonly name: string;\r\n\r\n /** 工具的分类语义 */\r\n readonly kind: ToolKind;\r\n\r\n /** 参数 JSON Schema 定义 */\r\n readonly parameters: JSONSchema;\r\n\r\n /**\r\n * 工具描述 — 默认从 parameters.description 自动提取,\r\n * 也可显式覆盖以提供更详细的说明。\r\n */\r\n readonly description: string;\r\n\r\n /** 工具执行逻辑 */\r\n execute(args: I, ctx: ToolContext): Promise<O>;\r\n\r\n /**\r\n * UI 初始标题(可选)。\r\n * 用于在 UI 中显示正在执行的工具名称。\r\n */\r\n initialTitle?(args: I): string;\r\n\r\n /**\r\n * 是否支持输入流式传输(可选)。\r\n * 用于支持 LLM 流式传输工具调用参数。\r\n */\r\n supportsInputStreaming?: boolean;\r\n\r\n /**\r\n * 工具支持的 LLM Provider(可选)。\r\n * 返回空/undefined 表示支持所有 provider。\r\n */\r\n supportedProviders?: string[];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// AnyAgentTool — 动态分发接口(Type Erasure 模式)\r\n//\r\n// 将类型参数擦除,用于 Registry 等需要存储异构工具集合的场景。\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * AnyAgentTool — 类型擦除后的工具接口。\r\n * Registry 存储的是此类型,执行时内部做反序列化。\r\n */\r\nexport interface AnyAgentTool {\r\n readonly name: string;\r\n readonly description: string;\r\n readonly kind: ToolKind;\r\n readonly parameters: JSONSchema;\r\n readonly supportsInputStreaming: boolean;\r\n readonly supportedProviders: string[];\r\n\r\n execute(args: unknown, ctx: ToolContext): Promise<ToolResult>;\r\n initialTitle?(args: unknown): string;\r\n}\r\n\r\n/**\r\n * 将类型安全的 AgentTool 擦除为 AnyAgentTool。\r\n */\r\nexport function eraseTool<I, O extends ToolResult = ToolResult>(\r\n tool: AgentTool<I, O>,\r\n): AnyAgentTool {\r\n return {\r\n get name() { return tool.name; },\r\n get description() { return tool.description; },\r\n get kind() { return tool.kind; },\r\n get parameters() { return tool.parameters; },\r\n get supportsInputStreaming() { return tool.supportsInputStreaming ?? false; },\r\n get supportedProviders() { return tool.supportedProviders ?? []; },\r\n\r\n async execute(args: unknown, ctx: ToolContext): Promise<ToolResult> {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n return tool.execute(args as I, ctx);\r\n },\r\n\r\n initialTitle(args: unknown): string {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n return tool.initialTitle?.(args as I) ?? tool.name;\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * 从 JSON Schema 中提取 description。\r\n * 若 schema.properties 的顶层有 description,使用它;\r\n * 否则使用 schema 本身的 description。\r\n */\r\nexport function extractDescription(schema: JSONSchema, fallback?: string): string {\r\n if (schema.description) return schema.description;\r\n if (fallback) return fallback;\r\n return \"\";\r\n}\r\n\r\n/**\r\n * 根据 ToolKind 判断是否只读(可并行执行)。\r\n * Read 一定只读;Edit/Delete/Move 一定写;Other 默认非只读。\r\n */\r\nexport function isReadOnly(kind: ToolKind): boolean {\r\n return kind === ToolKind.Read;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Permission/Gate 接口\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Gate 权限门接口 — 控制工具执行前是否需要审批。\r\n */\r\nexport interface Gate {\r\n check(toolName: string, args: unknown): boolean | Promise<boolean>;\r\n}\r\n\r\n/**\r\n * AlwaysAllowGate — 默认权限门,所有工具调用默认放行。\r\n */\r\nexport class AlwaysAllowGate implements Gate {\r\n check(_toolName: string, _args: unknown): boolean {\r\n return true;\r\n }\r\n}\r\n\r\n/** 工具调用记录(用于风暴检测等) */\r\nexport interface ToolCallRecord {\r\n name: string;\r\n success: boolean;\r\n error?: string;\r\n timestamp: number;\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 系统提示词构建 — 使用 Handlebars 模板引擎渲染\r\n// ---------------------------------------------------------------------------\r\n\r\nimport Handlebars from \"handlebars\";\r\nimport type { SystemPromptOptions, ToolDescription } from \"./types.js\";\r\nimport systemPromptTemplate from \"./prompts/system-prompt.hbs\";\r\nimport planPromptTemplate from \"./prompts/plan-prompt.hbs\";\r\n\r\nconst compiledTemplate = Handlebars.compile(systemPromptTemplate);\r\nconst compiledPlanTemplate = Handlebars.compile(planPromptTemplate);\r\n\r\ninterface ToolView {\r\n name: string;\r\n description: string;\r\n params?: string;\r\n}\r\n\r\nfunction formatTools(tools: ToolDescription[]): ToolView[] {\r\n return tools.map((t) => {\r\n const params =\r\n t.parameters &&\r\n Object.keys((t.parameters).properties ?? {})\r\n .join(\", \");\r\n return {\r\n name: t.name,\r\n description: t.description,\r\n params: params || undefined,\r\n };\r\n });\r\n}\r\n\r\n/** 构建公共模板变量 */\r\nfunction buildTemplateVars(opts: SystemPromptOptions) {\r\n const now = new Date();\r\n const date = now.toLocaleDateString(\"zh-CN\", {\r\n year: \"numeric\",\r\n month: \"long\",\r\n day: \"numeric\",\r\n weekday: \"long\",\r\n });\r\n const time = now.toLocaleTimeString(\"zh-CN\", {\r\n hour: \"2-digit\",\r\n minute: \"2-digit\",\r\n });\r\n\r\n return {\r\n model: opts.model,\r\n maxToolRounds: opts.maxToolRounds,\r\n cwd: opts.cwd,\r\n date,\r\n time,\r\n tools: opts.tools ? formatTools(opts.tools) : undefined,\r\n projectContext: opts.projectContext ?? undefined,\r\n };\r\n}\r\n\r\n/**\r\n * 构建 Agent 的系统提示词(代码模式)。\r\n *\r\n * 使用 Handlebars 模板 `prompts/system-prompt.hbs` 渲染,\r\n * 动态注入模型信息、工具描述、时间、项目上下文。\r\n */\r\nexport function buildSystemPrompt(opts: SystemPromptOptions): string {\r\n return compiledTemplate(buildTemplateVars(opts));\r\n}\r\n\r\n/**\r\n * 构建计划模式(Plan Mode)的系统提示词。\r\n *\r\n * 使用 Handlebars 模板 `prompts/plan-prompt.hbs` 渲染,\r\n * 聚焦于代码分析、架构设计和任务规划,不包含写工具信息。\r\n */\r\nexport function buildPlanSystemPrompt(opts: SystemPromptOptions): string {\r\n return compiledPlanTemplate(buildTemplateVars(opts));\r\n}\r\n","你是 dskcode,一个基于 DeepSeek 的 AI 编程助手,运行在用户终端中。你的职责是帮助开发者编写、理解、调试和重构代码。\r\n\r\n## 核心原则\r\n- 回答使用中文,代码标识符保持英文\r\n- **思考过程(CoT)也使用中文**,不要用英文思考。如果你发现自己在用英文\r\n 想,请切换到中文。你的所有内心独白和最终回答都应用中文表达。\r\n- 提供准确、实用、可操作的建议\r\n- 对不确定的内容明确标注,不编造事实\r\n- 代码示例保持简洁,附带必要的注释\r\n\r\n## 用户输入语法\r\n- `@<路径>` 表示一个**文件路径**的引用标识。`@` 不是邮箱符号、也不是装饰后缀。\r\n - `@test.ts` = 引用当前目录下名为 `test.ts` 的文件\r\n - `@src/foo/bar.ts` = 引用 `src/foo/bar.ts`\r\n - 多个引用:`请读 @a.ts 和 @b/d.ts`\r\n - 路径可以是相对路径(相对工作目录)或绝对路径\r\n- `\\/<命令>` 表示一个**斜杠命令**(如 `\\/help`、`\\/plan`、`\\/code`),不是目录。\r\n - 收到 `/xxx` 开头的内容先判定是不是已注册命令;不是则当作普通文本处理。\r\n- 上述两种前缀是**用户输入面**语法,你**不要在回复中输出**它们(不要伪造 `@/path` 这种伪引用)。如果要引用文件,直接用单反引号包裹路径(如 `src/foo.ts`)。\r\n\r\n## 工作流程\r\n\r\n你处于一个工具调用循环中:\r\n1. 收到用户请求\r\n2. 调用工具获取信息或修改文件\r\n3. 工具结果返回给你\r\n4. 根据结果决定下一步(继续调用工具或给出最终回答)\r\n5. 一轮最多 {{maxToolRounds}} 次工具调用\r\n\r\n## 工具使用策略\r\n\r\n{{#if tools}}\r\n当前可用的工具:\r\n{{#each tools}}\r\n- **{{name}}**:{{description}}{{#if params}}(参数:{{params}}){{/if}}\r\n{{/each}}\r\n\r\n使用原则:\r\n- **先搜索再读取** — 不确定目标位置时先用 grep/glob 定位,再用 read_file 读取\r\n- **先读取再修改** — 编辑文件前先 read_file 确认当前内容\r\n- **批量操作一次完成** — 同一文件的多处改动在单次 edit_file 中完成\r\n- **工具出错时** — 分析错误原因(路径不对?格式不对?权限不足?),修正后重试,不要重复相同的错误调用\r\n- **审慎执行** — bash 执行危险命令(rm -rf、强制写入等)前先确认\r\n{{/if}}\r\n\r\n## 工作流(重要)— 复杂任务判定\r\n\r\n收到用户请求后,**先判定复杂度**,再决定要不要拆 todo。\r\n\r\n### 默认假设是「复杂」\r\n\r\n除非明确属于下面「简单」三选一,**默认按复杂处理,先 todo_add 拆 3-7 步**。\r\n\r\n### 判定问卷(逐条 yes/no)\r\n\r\n问自己以下问题,**任一回答 yes → 拆 todo**:\r\n- 1️⃣ 要写/改/创建 **超过 1 个**文件?\r\n- 2️⃣ 要先 **读后改**(edit_file / write_file 之前必须 read_file)?\r\n- 3️⃣ 要跨 **2 种以上**工具(read + edit + bash + ...)?\r\n- 4️⃣ 要 **对比 2 个以上事实源**(如 README 说的 vs 代码实际写的、这里 vs 那里)?\r\n- 5️⃣ 句子超过 **20 个字** 且 **包含「改成 / 加个 / 实现 / 写一个 / 重构 / 修复 / 调整」** 这类动词?\r\n\r\n### 「简单」三选一(可以不拆 todo)\r\n\r\n- 纯查询:「列出 X」「看看 Y」「搜一下 Z」→ 1 个工具 1 轮搞定\r\n- 1 个工具 1 轮能搞定的读/列/查操作\r\n- 闲聊 / 解释性问题(不调任何工具)\r\n\r\n### 判定决策树\r\n\r\n```\r\n用户输入\r\n ├─ 是「简单三选一」? → 直接调工具,不拆 todo\r\n └─ 不是? → 先 todo_add 拆 3-7 步再动手(默认行为)\r\n```\r\n\r\n### todo 拆解模板\r\n\r\n**改动型任务**(最常见):\r\n1. `todo_add(\"读 <目标文件>\")`\r\n2. `todo_add(\"<具体改动>\", deps=[0])`\r\n3. `todo_add(\"检查改动\", deps=[1])`(重新读改过的文件,确认修改正确且无遗漏;必要时跑 type-check)\r\n\r\n**新建型任务**:\r\n1. `todo_add(\"看现有同类文件怎么写\")`\r\n2. `todo_add(\"实现新 <X>\", deps=[0])`\r\n3. `todo_add(\"验证\", deps=[1])`\r\n\r\n**调试型任务**:\r\n1. `todo_add(\"复现 bug\")`\r\n2. `todo_add(\"定位原因\", deps=[0])`\r\n3. `todo_add(\"修复\", deps=[1])`\r\n4. `todo_add(\"检查修复\", deps=[2])`(读改过的文件,确认修复正确没有引入新问题)\r\n\r\n### 执行流(严格遵守)\r\n\r\n- 拆完 todo 后,**先 `todo_mark_running(id)`** → 调实际工具 → **`todo_mark_done(id, \"证据\")`**\r\n- 失败:`todo_mark_failed(id, \"原因\")`;想重试:`todo_retry(id)` → 再走一遍\r\n走完所有 todo,Harness 会自动做任务检查——重新读取本次改过的文件,确认修改内容正确、无遗漏、类型兼容\r\n\r\n### 反例(别这样)\r\n\r\n- ❌ 用户说「把 X 改成 Y」→ 跳过 todo 直接 read + edit(撞墙率高)\r\n- ❌ 用户说「写个新工具」→ 跳过 todo 直接 write_file(容易写错接口)\r\n- ❌ 拆了 todo 但忘了 mark_running 就 mark_done(依赖检查会失败)\r\n\r\n{{#if projectContext}}\r\n## 项目上下文\r\n\r\n{{projectContext}}\r\n{{/if}}\r\n\r\n## 回答格式\r\n\r\n终端不支持 Markdown 渲染。回复格式要求:\r\n\r\n- 用 emoji 做视觉标记(📌 要点 ⚠️ 注意 📝 待办 ✅ 完成)\r\n- 用数字序号或短横线组织列表\r\n- 代码标识符、文件路径、类型名用单个反引号 ` 包裹,会被自动高亮\r\n- 强调内容用 **加粗** 包裹,会被自动高亮\r\n- 多行代码用缩进 4 个空格表示,不要用三反引号代码块\r\n- 段落间用空行分隔\r\n- **不要使用 `#` 符号** — 终端不渲染 Markdown 标题,请用 emoji + 加粗替代(如 `📌 **标题**`)\r\n\r\n正确示例:\r\n\r\n📌 **修改计划**\r\n\r\n📌 需要修改两个地方:\r\n1. 第 12 行的 `interface FileDiff` 改为 `interface Diff`\r\n2. 第 432 行的 `): FileDiff` 改为 `): Diff`\r\n\r\n 代码示例:\r\n const pool = new Pool({ max: 20 });\r\n await pool.query(\"SELECT * FROM users\");\r\n\r\n## 行为约束\r\n- 如果用户请求不明确,主动询问澄清\r\n- 对不确定的信息明确标注,不编造事实\r\n- 不执行可能造成不可逆损害的操作(如 rm -rf),除非用户明确逐条确认\r\n- 工具调用返回错误时,先分析原因再重试,不要盲目重试\r\n\r\n## 当前模型\r\n- 模型:{{model}}\r\n\r\n## 时间上下文\r\n- 当前日期:{{date}}\r\n- 当前时间:{{time}}\r\n- 工作目录:{{cwd}}\r\n","你是 dskcode,当前工作在**计划模式(Plan Mode)**。\r\n\r\n在此模式下,**你只能读取和分析代码,不能执行任何修改操作**。你的职责是帮助用户理解代码、设计架构、规划任务、评估方案。\r\n\r\n## 核心原则\r\n- 回答使用中文,代码标识符保持英文\r\n- **思考过程(CoT)也使用中文**,不要用英文思考。\r\n- 提供准确、实用、可操作的建议\r\n- 对不确定的内容明确标注,不编造事实\r\n- **绝不执行任何写操作** — 你只能读取和分析\r\n\r\n## 用户输入语法\r\n- `@<路径>` 表示一个**文件路径**的引用标识。`@` 不是邮箱符号、也不是装饰后缀。\r\n - `@test.ts` = 引用当前目录下名为 `test.ts` 的文件\r\n - `@src/foo/bar.ts` = 引用 `src/foo/bar.ts`\r\n - 多个引用:`请读 @a.ts 和 @b/d.ts`\r\n - 路径可以是相对路径(相对工作目录)或绝对路径\r\n- `\\/<命令>` 表示一个**斜杠命令**(如 `\\/help`、`\\/plan`、`\\/code`),不是目录。\r\n - 收到 `/xxx` 开头的内容先判定是不是已注册命令;不是则当作普通文本处理。\r\n- 上述两种前缀是**用户输入面**语法,你**不要在回复中输出**它们(不要伪造 `@/path` 这种伪引用)。如果要引用文件,直接用单反引号包裹路径(如 `src/foo.ts`)。\r\n\r\n## 工作流程\r\n1. 收到用户请求\r\n2. 使用读工具(read-file, grep, glob, ls, fetch)探索代码库\r\n3. 根据发现的信息提供结构化的分析或计划\r\n4. 工具返回出错时分析原因并修正后重试\r\n5. 完成后输出最终分析结果\r\n\r\n## 工具限制\r\n**你只能使用读工具**,不能使用任何写工具(write-file, edit-file, multi-edit, delete-range, bash)。\r\n如果用户请求修改代码,请:\r\n- 提供修改建议和方案\r\n- 指出需要修改的文件和位置\r\n- 解释修改的影响\r\n- 但不执行任何修改\r\n\r\n## 计划输出格式\r\n\r\n当用户请求计划/方案时,建议按照以下结构组织输出:\r\n\r\n📋 **概述**\r\n 1-2 句话说明要做什么以及为什么要做\r\n\r\n🔍 **现状分析**\r\n 当前代码的组织结构和关键发现\r\n\r\n⚡ **影响范围**\r\n 哪些文件/模块会受到影响\r\n\r\n📝 **实施步骤**\r\n 1. 第一步(文件路径/变更说明)\r\n 2. 第二步(文件路径/变更说明)\r\n 3. ...\r\n\r\n⚠️ **风险与注意事项**\r\n 可能的风险点、依赖关系、回退策略\r\n\r\n## 回答格式\r\n\r\n终端不支持 Markdown 渲染。回复格式要求:\r\n- 用 emoji 做视觉标记\r\n- 用数字序号或短横线组织列表\r\n- 代码标识符、文件路径用单个反引号 ` 包裹\r\n- 多行代码用缩进 4 个空格表示\r\n- 段落间用空行分隔\r\n- **不要使用 `#` 符号** — 终端不渲染 Markdown 标题,请用 emoji + 加粗替代\r\n\r\n## 当前模型\r\n- 模型:{{model}}\r\n\r\n## 时间上下文\r\n- 当前日期:{{date}}\r\n- 当前时间:{{time}}\r\n- 工作目录:{{cwd}}\r\n","// ---------------------------------------------------------------------------\r\n// 工具注册表 — 注册、查找、列出、过滤工具\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { ToolKind, type AnyAgentTool, type ToolContext, type ToolResult, type AgentTool, eraseTool, isReadOnly } from \"./types.js\";\r\n\r\n/** 工具注册表配置 */\r\nexport interface ToolRegistryOptions {\r\n /** 工具名称黑名单,这些工具不会被 list() 返回 */\r\n disabledTools?: string[];\r\n /**\r\n * Feature Flag 检查函数,返回 true 表示该工具可用。\r\n * 不在列表中的工具默认启用。\r\n */\r\n featureFlagChecker?: (toolName: string) => boolean;\r\n /**\r\n * 当前使用的 LLM Provider ID。\r\n * 提供后只会列出支持该 provider 的工具。\r\n */\r\n provider?: string;\r\n}\r\n\r\n/**\r\n * ToolRegistry — 管理所有已注册的工具。\r\n *\r\n * 设计要点:\r\n * - 注册时检查名称唯一性\r\n * - 支持 disable/enable 工具(基于配置项过滤)\r\n * - 支持 Feature Flag 门控\r\n * - 支持 Provider 过滤\r\n * - get() 返回 undefined 而非抛异常,调用方自行处理\r\n * - ALL_TOOL_NAMES 静态常量列表\r\n */\r\nexport class ToolRegistry {\r\n readonly #tools = new Map<string, AnyAgentTool>();\r\n readonly #disabledNames: Set<string>;\r\n readonly #featureFlagChecker: (toolName: string) => boolean;\r\n readonly #provider: string | undefined;\r\n\r\n /** 所有已注册工具的名称列表(编译期等效) */\r\n static readonly ALL_TOOL_NAMES: string[] = [];\r\n\r\n constructor(opts?: ToolRegistryOptions) {\r\n this.#disabledNames = new Set(opts?.disabledTools ?? []);\r\n this.#featureFlagChecker = opts?.featureFlagChecker ?? (() => true);\r\n this.#provider = opts?.provider;\r\n }\r\n\r\n /**\r\n * 注册一个类型安全的 AgentTool。\r\n * 自动擦除类型参数后存储。\r\n * 如果同名工具已存在则抛出错误。\r\n */\r\n register<I, O extends ToolResult = ToolResult>(tool: AgentTool<I, O>): this {\r\n return this.registerErased(eraseTool(tool));\r\n }\r\n\r\n /**\r\n * 注册一个已经擦除类型 AnyAgentTool。\r\n * 如果同名工具已存在则抛出错误。\r\n */\r\n registerErased(tool: AnyAgentTool): this {\r\n if (this.#tools.has(tool.name)) {\r\n throw new Error(`工具 \"${tool.name}\" 已注册,不能重复注册`);\r\n }\r\n this.#tools.set(tool.name, tool);\r\n return this;\r\n }\r\n\r\n /**\r\n * 批量注册工具。\r\n */\r\n registerAll(tools: AnyAgentTool[]): this {\r\n for (const tool of tools) {\r\n this.registerErased(tool);\r\n }\r\n return this;\r\n }\r\n\r\n /**\r\n * 注销一个工具。\r\n */\r\n unregister(name: string): boolean {\r\n return this.#tools.delete(name);\r\n }\r\n\r\n /**\r\n * 按名称获取工具(未擦除类型版本,调用方自行断言类型)。\r\n * 如果工具被禁用、被 feature flag 拦截、或不支持当前 provider,返回 undefined。\r\n */\r\n get(name: string): AnyAgentTool | undefined {\r\n if (!this.#isToolEnabled(name)) return undefined;\r\n return this.#tools.get(name);\r\n }\r\n\r\n /**\r\n * 获取所有启用的工具列表。\r\n * 依次应用:禁用列表 → Feature Flag → Provider 过滤。\r\n */\r\n list(): AnyAgentTool[] {\r\n const result: AnyAgentTool[] = [];\r\n for (const [name, tool] of this.#tools) {\r\n if (this.#isToolEnabled(name)) {\r\n result.push(tool);\r\n }\r\n }\r\n return result;\r\n }\r\n\r\n /**\r\n * 按 ToolKind 分类获取工具列表。\r\n */\r\n listByKind(kind: ToolKind): AnyAgentTool[] {\r\n return this.list().filter((t) => t.kind === kind);\r\n }\r\n\r\n /**\r\n * 获取读工具列表(可并行执行)。\r\n */\r\n listReadTools(): AnyAgentTool[] {\r\n return this.listByKind(ToolKind.Read);\r\n }\r\n\r\n /**\r\n * 获取写工具列表(需串行执行)。\r\n */\r\n listWriteTools(): AnyAgentTool[] {\r\n return this.list().filter((t) => !isReadOnly(t.kind));\r\n }\r\n\r\n /**\r\n * 获取所有已注册的工具名称(含禁用的)。\r\n */\r\n names(): string[] {\r\n return [...this.#tools.keys()];\r\n }\r\n\r\n /**\r\n * 检查工具是否已注册(含禁用的)。\r\n */\r\n has(name: string): boolean {\r\n return this.#tools.has(name);\r\n }\r\n\r\n /**\r\n * 检查工具是否已启用(注册 + 未禁用 + 通过 feature flag + 支持 provider)。\r\n */\r\n isEnabled(name: string): boolean {\r\n return this.#tools.has(name) && this.#isToolEnabled(name);\r\n }\r\n\r\n /**\r\n * 禁用一个工具。\r\n */\r\n disable(name: string): void {\r\n this.#disabledNames.add(name);\r\n }\r\n\r\n /**\r\n * 启用一个之前被禁用的工具。\r\n */\r\n enable(name: string): void {\r\n this.#disabledNames.delete(name);\r\n }\r\n\r\n /**\r\n * 获取工具的分类语义。\r\n */\r\n kindOf(name: string): ToolKind | undefined {\r\n return this.#tools.get(name)?.kind;\r\n }\r\n\r\n /**\r\n * 执行指定工具。\r\n *\r\n * @returns 工具执行结果;如果工具不存在或被禁用,返回失败结果\r\n */\r\n async execute(name: string, args: unknown, ctx: ToolContext): Promise<ToolResult> {\r\n const tool = this.get(name);\r\n if (!tool) {\r\n return {\r\n success: false,\r\n data: `工具 \"${name}\" 不存在或已被禁用`,\r\n error: \"TOOL_NOT_FOUND\",\r\n };\r\n }\r\n\r\n try {\r\n return await tool.execute(args, ctx);\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `工具 \"${name}\" 执行异常:${message}`,\r\n error: \"EXECUTION_ERROR\",\r\n };\r\n }\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // 内部方法\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * 判断工具是否应该启用。\r\n * 依次检查:是否在禁用列表 → 是否通过 Feature Flag → 是否支持当前 Provider。\r\n */\r\n #isToolEnabled(name: string): boolean {\r\n const tool = this.#tools.get(name);\r\n if (!tool) return false;\r\n\r\n // 1. 禁用列表检查\r\n if (this.#disabledNames.has(name)) return false;\r\n\r\n // 2. Feature Flag 检查\r\n if (!this.#featureFlagChecker(name)) return false;\r\n\r\n // 3. Provider 兼容性检查\r\n if (this.#provider && tool.supportedProviders.length > 0) {\r\n if (!tool.supportedProviders.includes(this.#provider)) return false;\r\n }\r\n\r\n return true;\r\n }\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// ToolExecutor — 纯执行层:解析参数、查工具、走权限门、跑工具、捕异常\r\n//\r\n// 设计原则:\r\n// - 不持有 Session / messages 状态,只依赖注入的 registry / gate / ctx\r\n// - 输入:一批 ProviderToolCall\r\n// - 输出:执行结果项(含 result)+ 风暴检测用的 records\r\n// - 异常安全:单个工具的异常不会中断其他工具\r\n//\r\n// 函数注释规范见仓库根 AGENTS.md「函数注释规范」一节。\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type { ProviderToolCall } from \"../provider/index.js\";\r\nimport type { ToolRegistry } from \"../tool/registry.js\";\r\nimport type {\r\n ToolCallRecord,\r\n ToolContext,\r\n ToolResult,\r\n Gate,\r\n AnyAgentTool,\r\n} from \"../tool/types.js\";\r\nimport { isReadOnly } from \"../tool/types.js\";\r\n\r\n/**\r\n * 单个工具执行的产出(喂回 Session 协调层)。\r\n *\r\n * @field name — 工具名(与 ProviderToolCall.name 一致)\r\n * @field callId — LLM 侧 call id,用于 messages.push 时回填 toolCallId\r\n * @field result — 工具执行结果(含 success / data / error / diff)\r\n */\r\nexport interface ToolExecutionItem {\r\n name: string;\r\n callId: string;\r\n result: ToolResult;\r\n}\r\n\r\n/**\r\n * 一次批量执行的完整产出。\r\n *\r\n * @field items — 所有工具执行结果(成功 + 失败)\r\n * @field records — 仅失败的 record(喂给 StormDetector 判断是否中断)\r\n */\r\nexport interface ToolExecutionResult {\r\n items: ToolExecutionItem[];\r\n /** 仅失败的 record(用于风暴检测) */\r\n records: ToolCallRecord[];\r\n}\r\n\r\n/**\r\n * ToolExecutor 的注入依赖。\r\n *\r\n * @field registry — 工具注册表(用于按名查找)\r\n * @field gate — 权限门(决定是否放行某个工具调用)\r\n * @field baseCtx — 工具执行的上下文(cwd / signal / writeRoots)\r\n * @field maxParallel — 并行执行的最大同时执行数(默认 8)\r\n */\r\nexport interface ToolExecutorDeps {\r\n registry: ToolRegistry;\r\n gate: Gate;\r\n baseCtx: Omit<ToolContext, \"signal\"> & { signal?: AbortSignal };\r\n /** 并行执行的最大同时执行数(默认 8) */\r\n maxParallel?: number;\r\n}\r\n\r\n/**\r\n * ToolExecutor:把\"工具调用 batch\"映射为\"执行结果 + 失败记录\"。\r\n *\r\n * 行为:\r\n * - 若全部为只读工具(Read 类)且数量 > 1:分批并行(每批最多 maxParallel 个)\r\n * - 其他情况:串行执行(保证有副作用的工具按 LLM 给出的顺序执行)\r\n *\r\n * 异常路径:工具不存在 / 权限门拒绝 / 执行抛错,都会被捕获并包装成失败 ToolResult,\r\n * 不会让一个工具的错误中断其他工具的执行。\r\n */\r\nexport class ToolExecutor {\r\n /** 工具注册表(只读引用) */\r\n readonly #registry: ToolRegistry;\r\n /** 权限门(只读引用) */\r\n readonly #gate: Gate;\r\n /** 工具执行上下文(每次执行都共用) */\r\n readonly #baseCtx: ToolContext;\r\n /** 并行执行的最大同时执行数(默认 8) */\r\n readonly #maxParallel: number;\r\n\r\n /**\r\n * 构造一个 ToolExecutor。\r\n *\r\n * @param deps — 注入依赖(registry / gate / baseCtx / maxParallel)\r\n * @pure 仅保存引用,不调用任何外部 IO\r\n */\r\n constructor(deps: ToolExecutorDeps) {\r\n this.#registry = deps.registry;\r\n this.#gate = deps.gate;\r\n this.#baseCtx = {\r\n cwd: deps.baseCtx.cwd,\r\n signal: deps.baseCtx.signal,\r\n writeRoots: deps.baseCtx.writeRoots,\r\n ...(deps.baseCtx.timeout !== undefined ? { timeout: deps.baseCtx.timeout } : {}),\r\n };\r\n this.#maxParallel = deps.maxParallel ?? 8;\r\n }\r\n\r\n /**\r\n * 执行一批工具调用。\r\n *\r\n * 自动选择并行 / 串行:\r\n * - 全部为只读工具 且 calls.length > 1:分批并行(每批最多 #maxParallel 个)\r\n * - 其他:严格串行\r\n *\r\n * @param calls — LLM 决定的本轮工具调用列表\r\n * @returns items(全部结果)+ records(仅失败项,用于风暴检测)\r\n *\r\n * @sideEffect 调用 registry / gate / tool.execute;不修改 Session.messages\r\n */\r\n async executeBatch(calls: ProviderToolCall[]): Promise<ToolExecutionResult> {\r\n if (calls.length === 0) return { items: [], records: [] };\r\n\r\n if (this.#allReadOnly(calls) && calls.length > 1) {\r\n return this.#executeParallel(calls);\r\n }\r\n return this.#executeSequential(calls);\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 内部\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * 判定这一批调用是否全部为只读工具。\r\n * 工具未找到时按\"只读\"处理(不会因为没注册就拒绝并行)。\r\n *\r\n * @pure 不修改任何状态\r\n */\r\n #allReadOnly(calls: ProviderToolCall[]): boolean {\r\n return calls.every((tc) => {\r\n const tool = this.#registry.get(tc.name);\r\n return tool ? isReadOnly(tool.kind) : true;\r\n });\r\n }\r\n\r\n /**\r\n * 并行执行:按 #maxParallel 分批,每批内 Promise.all。\r\n * 用于\"全部只读 + 多于 1 个\"的场景。\r\n *\r\n * @param calls — 全部只读的工具调用\r\n * @returns items + records\r\n *\r\n * @sideEffect 调用 tool.execute\r\n */\r\n async #executeParallel(calls: ProviderToolCall[]): Promise<ToolExecutionResult> {\r\n const items: ToolExecutionItem[] = [];\r\n const records: ToolCallRecord[] = [];\r\n\r\n for (let i = 0; i < calls.length; i += this.#maxParallel) {\r\n const batch = calls.slice(i, i + this.#maxParallel);\r\n const results = await Promise.all(batch.map((tc) => this.#executeOne(tc)));\r\n for (const r of results) {\r\n items.push(r.item);\r\n if (!r.record.success) records.push(r.record);\r\n }\r\n }\r\n return { items, records };\r\n }\r\n\r\n /**\r\n * 串行执行:按 calls 顺序逐个执行。\r\n * 用于含写工具的场景(保证副作用顺序与 LLM 给出顺序一致)。\r\n *\r\n * @param calls — 工具调用列表\r\n * @returns items + records\r\n *\r\n * @sideEffect 按序调用 tool.execute\r\n */\r\n async #executeSequential(calls: ProviderToolCall[]): Promise<ToolExecutionResult> {\r\n const items: ToolExecutionItem[] = [];\r\n const records: ToolCallRecord[] = [];\r\n for (const tc of calls) {\r\n const r = await this.#executeOne(tc);\r\n items.push(r.item);\r\n if (!r.record.success) records.push(r.record);\r\n }\r\n return { items, records };\r\n }\r\n\r\n /**\r\n * 执行单个工具调用,包含查找 / 参数解析 / 权限门 / 写工具 preview / 异常捕获。\r\n *\r\n * 失败映射:\r\n * - 工具不存在或被禁用 → `TOOL_NOT_FOUND`\r\n * - 权限门拒绝 → `GATE_DENIED`\r\n * - 工具 execute 抛错 → `EXECUTION_ERROR`\r\n *\r\n * @param tc — 单个工具调用(含 id / name / arguments 字符串)\r\n * @returns { item, record } — item 喂回 Session;record 用于风暴检测\r\n *\r\n * @sideEffect 调 tool.execute、可能调 tool.preview;不抛错给调用方\r\n */\r\n async #executeOne(\r\n tc: ProviderToolCall,\r\n ): Promise<{ item: ToolExecutionItem; record: ToolCallRecord }> {\r\n const toolName = tc.name;\r\n const timestamp = Date.now();\r\n const tool = this.#registry.get(toolName);\r\n if (!tool) {\r\n const errMsg = `工具 \"${toolName}\" 不存在或已被禁用`;\r\n return {\r\n item: { name: toolName, callId: tc.id, result: { success: false, data: errMsg, error: \"TOOL_NOT_FOUND\" } },\r\n record: { name: toolName, success: false, error: \"TOOL_NOT_FOUND\", timestamp },\r\n };\r\n }\r\n\r\n // 解析参数:arguments 是 JSON 字符串,解析失败则降级为空对象(不阻塞对话)\r\n let toolArgs: unknown;\r\n try { toolArgs = tc.arguments ? JSON.parse(tc.arguments) : {}; } catch { toolArgs = {}; }\r\n\r\n const gateResult = await this.#gate.check(toolName, toolArgs);\r\n if (!gateResult) {\r\n const errMsg = `工具 \"${toolName}\" 被权限门拒绝`;\r\n return {\r\n item: { name: toolName, callId: tc.id, result: { success: false, data: errMsg, error: \"GATE_DENIED\" } },\r\n record: { name: toolName, success: false, error: \"GATE_DENIED\", timestamp },\r\n };\r\n }\r\n\r\n // 写工具的 preview:用于在执行前预热 / 副作用预览(仅写工具支持)\r\n if (!isReadOnly(tool.kind)) {\r\n const maybePreview = (tool as { preview?: (args: unknown, ctx: ToolContext) => Promise<unknown> }).preview;\r\n if (typeof maybePreview === \"function\") {\r\n try { await maybePreview(toolArgs, this.#baseCtx); } catch { /* ignore preview errors */ }\r\n }\r\n }\r\n\r\n try {\r\n const result = await this.#invokeTool(tool, toolArgs);\r\n return {\r\n item: { name: toolName, callId: tc.id, result },\r\n record: { name: toolName, success: result.success, error: result.error, timestamp },\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n const errorResult: ToolResult = {\r\n success: false,\r\n data: `工具 \"${toolName}\" 执行异常:${message}`,\r\n error: \"EXECUTION_ERROR\",\r\n };\r\n return {\r\n item: { name: toolName, callId: tc.id, result: errorResult },\r\n record: { name: toolName, success: false, error: \"EXECUTION_ERROR\", timestamp },\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * 真正调 tool.execute 的薄包装(拆出来便于以后插入横切关注点,如重试 / 计时)。\r\n *\r\n * @param tool — 类型擦除后的工具\r\n * @param args — 解析后的参数(unknown)\r\n * @returns 工具的 ToolResult\r\n *\r\n * @sideEffect 调 tool.execute\r\n */\r\n async #invokeTool(tool: AnyAgentTool, args: unknown): Promise<ToolResult> {\r\n return tool.execute(args, this.#baseCtx);\r\n }\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// StormDetector — 纯判断层:检测「同一工具连续失败」风暴\r\n//\r\n// 设计原则:\r\n// - 纯函数 / 纯类:输入历史 records + 当前 calls → boolean\r\n// - 不持有 Session 状态,不写 messages\r\n// - 触发条件:最近 N 条失败记录「工具名 + 错误码」完全相同 且 当前轮仍调用该工具\r\n//\r\n// 函数注释规范见仓库根 AGENTS.md「函数注释规范」一节。\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type { ProviderToolCall } from \"../provider/index.js\";\r\nimport type { ToolCallRecord } from \"../tool/types.js\";\r\n\r\n/**\r\n * 风暴判定选项。\r\n *\r\n * @field threshold — 触发风暴需要的最少连续失败次数(默认 3)\r\n */\r\nexport interface StormDetectorOptions {\r\n /** 触发风暴需要的最少连续失败次数(默认 3) */\r\n threshold?: number;\r\n}\r\n\r\n/**\r\n * StormDetector:纯判断「本轮是否应该中断风暴」。\r\n *\r\n * 判定规则(按顺序检查,任一不满足则返回 false):\r\n * 1. 历史失败记录数 ≥ threshold\r\n * 2. 取最后 threshold 条记录,必须全部失败,且工具名 / 错误码完全一致\r\n * 3. 当前轮至少有 1 个工具名匹配那个失败工具\r\n *\r\n * 用途:当 LLM 在同一工具上反复撞同一错误时,强制提示\"切换策略\",\r\n * 避免无限循环。\r\n */\r\nexport class StormDetector {\r\n /** 触发风暴需要的最少连续失败次数(默认 3) */\r\n readonly #threshold: number;\r\n\r\n /**\r\n * 构造一个 StormDetector。\r\n *\r\n * @param opts — 判定选项(threshold)\r\n * @pure 仅保存配置\r\n */\r\n constructor(opts: StormDetectorOptions = {}) {\r\n this.#threshold = opts.threshold ?? 3;\r\n }\r\n\r\n /**\r\n * 判定本轮是否应该中断风暴。\r\n *\r\n * @param recentRecords — 历史的失败记录(不含成功的;通常来自 ToolExecutor 的 records)\r\n * @param currentCalls — 本轮 LLM 决定调用的工具\r\n * @returns true 表示触发风暴,调用方应放弃本轮工具执行并提示切换策略\r\n *\r\n * @pure 不修改任何状态,仅做比较判断\r\n */\r\n shouldBreak(recentRecords: readonly ToolCallRecord[], currentCalls: readonly ProviderToolCall[]): boolean {\r\n if (recentRecords.length < this.#threshold) return false;\r\n const lastN = recentRecords.slice(-this.#threshold);\r\n if (lastN.length < this.#threshold) return false;\r\n\r\n const first = lastN[0]!;\r\n const allSame = lastN.every((r) => r.name === first.name && r.error === first.error && !r.success);\r\n if (!allSame) return false;\r\n\r\n return currentCalls.some((tc) => tc.name === first.name);\r\n }\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// Reflector — 失败归因 → 改写下一轮 prompt\r\n//\r\n// 设计原则:\r\n// - 纯类:输入(工具执行结果 + 历史 + 上下文)→ 输出(反射列表 / 拼装后的 prompt)\r\n// - 不持有 Session / messages 状态\r\n// - 4 条规则(按优先级检查,先匹配先返回,单条目不重复触发):\r\n// R1 连续失败 — 同一工具连续 ≥threshold 次相同错误码失败\r\n// R2 文件不存在 — TOOL_NOT_FOUND 或 ENOENT / not found / No such file\r\n// R3 权限拒绝 — GATE_DENIED 或 EACCES / permission / denied\r\n// R4 写根外 — kind ∈ Edit/Delete/Move 且失败\r\n// - 注入策略:拼到 system prompt 尾部,不修改 messages\r\n//\r\n// 函数注释规范见仓库根 AGENTS.md「函数注释规范」一节。\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { ToolKind, type ToolResult } from \"../tool/types.js\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 类型\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 反射类别(与日志里的 category 字段对齐) */\r\nexport type ReflectionCategory =\r\n | \"repeated_failure\"\r\n | \"file_not_found\"\r\n | \"permission_denied\"\r\n | \"out_of_write_root\";\r\n\r\n/** 单条反射 */\r\nexport interface Reflection {\r\n /** 给 LLM 看的中文提示 */\r\n hint: string;\r\n /** 反射分类(用于日志 / 调试) */\r\n category: ReflectionCategory;\r\n /** 命中的工具名(便于 UI 展示) */\r\n toolName: string;\r\n}\r\n\r\n/** Reflector 配置 */\r\nexport interface ReflectorOptions {\r\n /** 触发 R1 需要的最小连续失败次数(默认 2) */\r\n repeatThreshold?: number;\r\n /** 注入到 prompt 的最大反射数(默认 5) */\r\n maxReflections?: number;\r\n /** 单条 hint 的最大字符数(默认 800) */\r\n maxHintChars?: number;\r\n}\r\n\r\n/** analyze() 接收的单条工具结果 */\r\nexport interface AnalyzeItem {\r\n /** 工具名 */\r\n name: string;\r\n /** 工具执行结果 */\r\n result: ToolResult;\r\n /** 工具分类(从 registry 拿,未注册时为 Other) */\r\n kind: ToolKind;\r\n /** 该工具在本轮之前的失败历史(仅失败项;用于 R1) */\r\n recentSameTool: Array<{ success: boolean; error?: string }>;\r\n}\r\n\r\n/** analyze() 接收的上下文 */\r\nexport interface AnalyzeContext {\r\n /** 写工具允许的根目录列表(用于 R4 hint) */\r\n writeRoots: string[];\r\n /** 当前工作目录(用于 R2 hint) */\r\n cwd: string;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Reflector\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Reflector:把本轮工具执行的失败结果归因为结构化反射,拼到下一轮 prompt。\r\n *\r\n * 行为:\r\n * - analyze:每个失败项最多产生 1 条反射(按 R1 → R2 → R3 → R4 顺序检查,先命中先用)\r\n * - injectIntoPrompt:把反射数组拼成\"反思\"section 追加到 system prompt 末尾\r\n *\r\n * 不写 messages,不调 LLM,不持有可变状态。\r\n */\r\nexport class Reflector {\r\n /** 触发 R1 需要的最小连续失败次数 */\r\n readonly #repeatThreshold: number;\r\n /** 注入到 prompt 的最大反射数 */\r\n readonly #maxReflections: number;\r\n /** 单条 hint 的最大字符数 */\r\n readonly #maxHintChars: number;\r\n\r\n /**\r\n * 构造一个 Reflector。\r\n *\r\n * @param options — 限流与阈值配置\r\n * @pure 仅保存配置\r\n */\r\n constructor(options: ReflectorOptions = {}) {\r\n this.#repeatThreshold = options.repeatThreshold ?? 2;\r\n this.#maxReflections = options.maxReflections ?? 5;\r\n this.#maxHintChars = options.maxHintChars ?? 800;\r\n }\r\n\r\n /**\r\n * 分析一批工具执行结果,返回要注入的反射列表。\r\n *\r\n * 行为:\r\n * - 成功项直接跳过\r\n * - 失败项按 R1 → R2 → R3 → R4 顺序检查,先命中先用,单条目最多产生 1 条\r\n * - 总数超过 maxReflections 时截断到前 N 条\r\n *\r\n * @param items — 本轮工具执行结果列表\r\n * @param ctx — 上下文(writeRoots / cwd,用于生成 hint)\r\n * @returns 反射列表(可能为空;最多 maxReflections 条)\r\n *\r\n * @pure 不修改任何状态\r\n */\r\n analyze(items: ReadonlyArray<AnalyzeItem>, ctx: AnalyzeContext): Reflection[] {\r\n const reflections: Reflection[] = [];\r\n for (const item of items) {\r\n if (item.result.success) continue;\r\n const r =\r\n this.#ruleRepeatedFailure(item) ??\r\n this.#ruleFileNotFound(item) ??\r\n this.#rulePermissionDenied(item) ??\r\n this.#ruleOutOfWriteRoot(item, ctx.writeRoots);\r\n if (r) reflections.push(r);\r\n if (reflections.length >= this.#maxReflections) break;\r\n }\r\n return reflections;\r\n }\r\n\r\n /**\r\n * 把反射列表拼成\"反思\"section,追加到 system prompt 末尾。\r\n *\r\n * 输出格式:\r\n * ```\r\n * {原 prompt}\r\n *\r\n * ## ⚠️ 上一轮工具调用的反思\r\n * - 工具 `<tool>`:<hint>\r\n * - 工具 `<tool>`:<hint>\r\n * ```\r\n *\r\n * @param systemPrompt — 原始 system prompt\r\n * @param reflections — 反射列表(空数组时原样返回)\r\n * @returns 拼装后的新 prompt\r\n *\r\n * @pure 不修改入参\r\n */\r\n injectIntoPrompt(systemPrompt: string, reflections: Reflection[]): string {\r\n if (reflections.length === 0) return systemPrompt;\r\n const lines = reflections.map((r) => {\r\n const hint = this.#truncate(r.hint);\r\n return `- 工具 \\`${r.toolName}\\` 失败:${hint}`;\r\n });\r\n const section = [\r\n \"## ⚠️ 上一轮工具调用的反思\",\r\n \"以下是本轮工具执行失败的原因分析。请在下一次调用前先解决这些问题,不要重复相同的错误调用。\",\r\n ...lines,\r\n ].join(\"\\n\");\r\n return systemPrompt + \"\\n\\n\" + section;\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 规则实现(按 R1 → R2 → R3 → R4 顺序)\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * R1 连续失败:同一工具连续 ≥#repeatThreshold 次相同错误码失败。\r\n *\r\n * @pure 仅读入参\r\n */\r\n #ruleRepeatedFailure(item: AnalyzeItem): Reflection | null {\r\n const recent = item.recentSameTool;\r\n if (recent.length < this.#repeatThreshold) return null;\r\n const lastN = recent.slice(-this.#repeatThreshold);\r\n const first = lastN[0]!;\r\n if (!lastN.every((r) => !r.success && r.error === first.error)) {\r\n return null;\r\n }\r\n const errText = first.error ?? \"未知错误\";\r\n return {\r\n category: \"repeated_failure\",\r\n toolName: item.name,\r\n hint: `你已连续 ${this.#repeatThreshold} 次调用 \\`${item.name}\\` 失败(错误:${errText})。请先 \\`read_file\\` 看清现场或换其他工具,不要继续同样的错误调用。`,\r\n };\r\n }\r\n\r\n /**\r\n * R2 文件不存在:TOOL_NOT_FOUND 错误码 或 data 含 ENOENT / not found / No such file。\r\n *\r\n * @pure 仅读入参\r\n */\r\n #ruleFileNotFound(item: AnalyzeItem): Reflection | null {\r\n const isToolNotFound = item.result.error === \"TOOL_NOT_FOUND\";\r\n const data = item.result.data ?? \"\";\r\n const hasKeyword =\r\n data.includes(\"ENOENT\") ||\r\n data.includes(\"No such file\") ||\r\n /\\bnot found\\b/i.test(data);\r\n if (!isToolNotFound && !hasKeyword) return null;\r\n return {\r\n category: \"file_not_found\",\r\n toolName: item.name,\r\n hint: `\\`${item.name}\\` 失败:文件不存在。请先用 \\`ls\\` / \\`glob\\` 看一下目录结构,或确认路径拼写(当前工作目录见 system prompt)。`,\r\n };\r\n }\r\n\r\n /**\r\n * R3 权限拒绝:GATE_DENIED 错误码 或 data 含 EACCES / permission / denied(不区分大小写)。\r\n *\r\n * @pure 仅读入参\r\n */\r\n #rulePermissionDenied(item: AnalyzeItem): Reflection | null {\r\n const isGateDenied = item.result.error === \"GATE_DENIED\";\r\n const data = item.result.data ?? \"\";\r\n const hasKeyword =\r\n data.includes(\"EACCES\") || /\\bpermission\\b/i.test(data) || /\\bdenied\\b/i.test(data);\r\n if (!isGateDenied && !hasKeyword) return null;\r\n return {\r\n category: \"permission_denied\",\r\n toolName: item.name,\r\n hint: `\\`${item.name}\\` 被权限系统拒绝。请检查:1) 路径是否在允许根目录内;2) 是否需要切换授权模式;3) 改用其他不需要权限的工具。`,\r\n };\r\n }\r\n\r\n /**\r\n * R4 写根外:kind ∈ Edit/Delete/Move 且失败。\r\n *\r\n * @pure 仅读入参\r\n */\r\n #ruleOutOfWriteRoot(item: AnalyzeItem, writeRoots: readonly string[]): Reflection | null {\r\n const isWriteKind =\r\n item.kind === ToolKind.Edit ||\r\n item.kind === ToolKind.Delete ||\r\n item.kind === ToolKind.Move;\r\n if (!isWriteKind) return null;\r\n const primary = writeRoots[0] ?? \"<未配置>\";\r\n return {\r\n category: \"out_of_write_root\",\r\n toolName: item.name,\r\n hint: `\\`${item.name}\\` 是写操作但目标路径不在允许的根目录内。请改用 \\`${primary}\\` 下的路径,或调整项目的 \\`writeRoots\\` 配置。`,\r\n };\r\n }\r\n\r\n /**\r\n * 截断超长 hint,末尾加省略号。\r\n *\r\n * @pure 字符串处理\r\n */\r\n #truncate(text: string): string {\r\n if (text.length <= this.#maxHintChars) return text;\r\n return text.slice(0, this.#maxHintChars - 3) + \"...\";\r\n }\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// buildToolDefinitions — 纯计算层:把 ToolRegistry 转成 Provider 需要的工具定义\r\n//\r\n// 设计原则:\r\n// - 纯函数:无副作用,输入 registry + mode → ToolDefinition[]\r\n// - mode 为 \"plan\" 时只暴露只读工具,强制走「只读 → 计划」模式\r\n//\r\n// 函数注释规范见仓库根 AGENTS.md「函数注释规范」一节。\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type { ToolDefinition } from \"../provider/index.js\";\r\nimport type { ToolRegistry } from \"../tool/registry.js\";\r\nimport type { SessionMode } from \"./types.js\";\r\n\r\n/**\r\n * 构建 Provider 工具定义(喂给 provider.chat() 的 tools 字段)。\r\n *\r\n * 行为:\r\n * - mode === \"plan\":只列出只读工具(read_file / list_dir / grep / ...)\r\n * - mode === \"code\":列出全部启用工具\r\n *\r\n * @param registry — 工具注册表\r\n * @param mode — 会话模式(\"plan\" → 只读;\"code\" → 全部)\r\n * @returns Provider 兼容的 ToolDefinition 数组\r\n *\r\n * @pure 无副作用:仅读 registry 状态,不修改任何东西\r\n */\r\nexport function buildToolDefinitions(\r\n registry: ToolRegistry,\r\n mode: SessionMode,\r\n): ToolDefinition[] {\r\n const tools = mode === \"plan\" ? registry.listReadTools() : registry.list();\r\n return tools.map((t) => ({\r\n type: \"function\" as const,\r\n function: {\r\n name: t.name,\r\n description: t.description,\r\n parameters: t.parameters as unknown as Record<string, unknown>,\r\n },\r\n }));\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// TodoList — Harness 规划层的最小骨架\r\n//\r\n// 设计原则:\r\n// - 纯数据 + 纯方法(除状态转换外不调任何 IO)\r\n// - 状态机:pending → running → done | failed | skipped\r\n// - 依赖关系:todo 声明 deps(其它 todo 的 id),deps 全 done 才进入 pending\r\n// - 输出:toMarkdown() 拼到 system prompt,让模型每轮看到自己进度\r\n//\r\n// 函数注释规范见仓库根 AGENTS.md「函数注释规范」一节。\r\n// ---------------------------------------------------------------------------\r\n\r\n/** Todo 状态 */\r\nexport type TodoStatus = \"pending\" | \"running\" | \"done\" | \"failed\" | \"skipped\";\r\n\r\n/** Todo 项 */\r\nexport interface TodoItem {\r\n /** 唯一 id(生成时分配 0/1/2...) */\r\n id: number;\r\n /** 给 LLM 看的人类可读步骤描述(中文) */\r\n content: string;\r\n /** 当前状态 */\r\n status: TodoStatus;\r\n /** 依赖的其它 todo id 列表(这些都 done 后本项才可 running) */\r\n deps: number[];\r\n /** 完成时的证据(如 \"type-check 通过\"、\"read_file 拿到 8633 字符\");失败时为错误信息 */\r\n evidence?: string;\r\n /** 状态最近一次变更的时间戳(毫秒) */\r\n updatedAt: number;\r\n}\r\n\r\n/**\r\n * TodoList — 任务列表状态机。\r\n *\r\n * 用法(典型流程):\r\n * const todo = new TodoList();\r\n * const a = todo.add(\"读 edit-file.ts\");\r\n * const b = todo.add(\"修改错误信息\", [a]);\r\n * const c = todo.add(\"跑 type-check\", [b]);\r\n * todo.markRunning(a); todo.markDone(a, \"读取成功\");\r\n * // ... 依次推进\r\n *\r\n * 状态转换:\r\n * pending → running (markRunning,前提是 deps 全 done)\r\n * running → done (markDone,附 evidence)\r\n * running → failed (markFailed,附 reason)\r\n * pending → skipped (markSkipped,附 reason)\r\n */\r\nexport class TodoList {\r\n #items: TodoItem[] = [];\r\n #nextId = 0;\r\n\r\n /**\r\n * 新增一个 todo,返回分配的 id。\r\n *\r\n * @param content — 步骤描述(中文)\r\n * @param deps — 依赖的 todo id 列表;为空表示无依赖,立即可 running\r\n * @returns 新 todo 的 id\r\n * @throws 当 deps 引用了不存在的 id 时抛出,避免 LLM 错把不存在的依赖塞进计划\r\n */\r\n add(content: string, deps: number[] = []): number {\r\n // 校验 deps:所有 id 必须已存在(避免引用未来 / 不存在的 id)\r\n for (const d of deps) {\r\n if (!this.#items.find((it) => it.id === d)) {\r\n throw new Error(`todo 依赖 #${d} 不存在(已分配的 id: ${this.#items.map((it) => it.id).join(\", \") || \"(空)\"})`);\r\n }\r\n }\r\n const id = this.#nextId++;\r\n this.#items.push({\r\n id,\r\n content,\r\n status: \"pending\",\r\n deps: [...deps],\r\n updatedAt: Date.now(),\r\n });\r\n return id;\r\n }\r\n\r\n /**\r\n * 把失败的 todo 重置回 pending(用于\"重试\"工作流)。\r\n *\r\n * 不允许把 done / skipped 重置(一旦完成不应反悔)。\r\n * 不允许把 running 重置(应该先 markFailed 再重试)。\r\n *\r\n * @param id — todo id\r\n * @returns 是否成功\r\n */\r\n resetForRetry(id: number): boolean {\r\n const item = this.#find(id);\r\n if (!item) return false;\r\n if (item.status !== \"failed\") return false;\r\n item.status = \"pending\";\r\n item.evidence = undefined;\r\n item.updatedAt = Date.now();\r\n return true;\r\n }\r\n\r\n /**\r\n * 把 todo 标记为 running。\r\n *\r\n * 前提:该 todo 存在 + 当前是 pending + 依赖全 done。\r\n * 不满足时静默返回 false(不抛错,避免污染 agent 主循环)。\r\n *\r\n * @param id — todo id\r\n * @returns 是否成功转换\r\n */\r\n markRunning(id: number): boolean {\r\n const item = this.#find(id);\r\n if (!item) return false;\r\n if (item.status !== \"pending\") return false;\r\n if (!this.#depsAllDone(item.deps)) return false;\r\n item.status = \"running\";\r\n item.updatedAt = Date.now();\r\n return true;\r\n }\r\n\r\n /**\r\n * 标记完成。\r\n *\r\n * @param id — todo id\r\n * @param evidence — 完成证据(如 \"读取成功\"、\"type-check 通过\")\r\n * @returns 是否成功\r\n */\r\n markDone(id: number, evidence?: string): boolean {\r\n const item = this.#find(id);\r\n if (!item) return false;\r\n if (item.status !== \"running\" && item.status !== \"pending\") return false;\r\n item.status = \"done\";\r\n if (evidence !== undefined) item.evidence = evidence;\r\n item.updatedAt = Date.now();\r\n return true;\r\n }\r\n\r\n /**\r\n * 标记失败。\r\n *\r\n * @param id — todo id\r\n * @param reason — 失败原因(人类可读)\r\n * @returns 是否成功\r\n */\r\n markFailed(id: number, reason: string): boolean {\r\n const item = this.#find(id);\r\n if (!item) return false;\r\n if (item.status === \"done\") return false;\r\n item.status = \"failed\";\r\n item.evidence = reason;\r\n item.updatedAt = Date.now();\r\n return true;\r\n }\r\n\r\n /**\r\n * 标记跳过。\r\n *\r\n * @param id — todo id\r\n * @param reason — 跳过原因(如 \"项目无测试基建\")\r\n * @returns 是否成功\r\n */\r\n markSkipped(id: number, reason: string): boolean {\r\n const item = this.#find(id);\r\n if (!item) return false;\r\n if (item.status === \"done\" || item.status === \"failed\") return false;\r\n item.status = \"skipped\";\r\n item.evidence = reason;\r\n item.updatedAt = Date.now();\r\n return true;\r\n }\r\n\r\n /**\r\n * 取出所有\"立即可做\"的 todo(deps 全 done 且 pending)。\r\n *\r\n * @returns 可执行的 todo 列表\r\n */\r\n pending(): TodoItem[] {\r\n return this.#items.filter(\r\n (it) => it.status === \"pending\" && this.#depsAllDone(it.deps),\r\n );\r\n }\r\n\r\n /**\r\n * 取出所有未完成项(pending / running / failed)。\r\n */\r\n unfinished(): TodoItem[] {\r\n return this.#items.filter(\r\n (it) => it.status === \"pending\" || it.status === \"running\" || it.status === \"failed\",\r\n );\r\n }\r\n\r\n /**\r\n * 是否全部结束(done / failed / skipped)。\r\n */\r\n isAllTerminated(): boolean {\r\n return this.#items.every(\r\n (it) => it.status === \"done\" || it.status === \"failed\" || it.status === \"skipped\",\r\n );\r\n }\r\n\r\n /** 全部条目(只读快照) */\r\n get items(): ReadonlyArray<TodoItem> {\r\n return this.#items;\r\n }\r\n\r\n /**\r\n * 把 todo 列表拼成 markdown,用于注入 system prompt。\r\n *\r\n * @param maxItems — 最多展示多少条;超出时保留\"最近 N 条 + 进行中\",折叠已完成\r\n * @returns markdown 字符串\r\n */\r\n toMarkdown(maxItems = 20): string {\r\n if (this.#items.length === 0) return \"\";\r\n\r\n // 截断策略:保留所有 running/pending/failed(活跃项),剩下的按时间倒序补到 maxItems\r\n const active = this.#items.filter(\r\n (it) => it.status === \"pending\" || it.status === \"running\" || it.status === \"failed\",\r\n );\r\n const finished = this.#items.filter(\r\n (it) => it.status === \"done\" || it.status === \"skipped\",\r\n );\r\n const finishedSorted = [...finished].sort((a, b) => b.updatedAt - a.updatedAt);\r\n\r\n const truncated = finishedSorted.length > maxItems;\r\n const finishedToShow = truncated\r\n ? finishedSorted.slice(0, Math.max(0, maxItems - active.length))\r\n : finishedSorted;\r\n\r\n const lines = [...active, ...finishedToShow].map((it) => {\r\n const box = this.#statusBox(it.status);\r\n const depStr = it.deps.length > 0 ? ` (依赖: #${it.deps.join(\", #\")})` : \"\";\r\n const eviStr = it.evidence ? ` — ${it.evidence}` : \"\";\r\n return `- ${box} #${it.id} ${it.content}${depStr}${eviStr}`;\r\n });\r\n if (truncated) {\r\n const hidden = finishedSorted.length - finishedToShow.length;\r\n lines.push(`- ...(另有 ${hidden} 条已完成已折叠)`);\r\n }\r\n\r\n return [\r\n \"## 📋 当前任务进度\",\r\n ...lines,\r\n \"\",\r\n \"规则:按顺序推进;未完成依赖时不要跳步;完成请调用 todo_mark_done,失败请调用 todo_mark_failed;重试失败项先调 todo_retry 再重跑。\",\r\n ].join(\"\\n\");\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 内部\r\n // -------------------------------------------------------------------------\r\n\r\n #find(id: number): TodoItem | undefined {\r\n return this.#items.find((it) => it.id === id);\r\n }\r\n\r\n #depsAllDone(deps: readonly number[]): boolean {\r\n return deps.every((d) => {\r\n const item = this.#items.find((it) => it.id === d);\r\n return item?.status === \"done\" || item?.status === \"skipped\";\r\n });\r\n }\r\n\r\n #statusBox(status: TodoStatus): string {\r\n switch (status) {\r\n case \"pending\": return \"☐\";\r\n case \"running\": return \"▶\";\r\n case \"done\": return \"✅\";\r\n case \"failed\": return \"❌\";\r\n case \"skipped\": return \"⏭\";\r\n }\r\n }\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// Harness 工具 — 把 TodoList 暴露给 LLM 主动调用的工具集\r\n//\r\n// 设计:\r\n// - 3 个工具:todo_add / todo_mark_done / todo_mark_failed\r\n// - 每个工具都接收一个 Session-side 的 TodoList 引用(通过闭包)\r\n// - 不让 LLM 直接读 todo(用 system prompt 注入,避免增加决策负担)\r\n//\r\n// 函数注释规范见仓库根 AGENTS.md「函数注释规范」一节。\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { ToolKind, type AgentTool, type ToolResult } from \"../tool/types.js\";\r\nimport type { TodoList } from \"./todo-list.js\";\r\n\r\n/**\r\n * 把 TodoList 包装成 5 个 AgentTool,供 Session 注入到 ToolRegistry。\r\n *\r\n * 工具集:\r\n * - `todo_add` — 添加新步骤\r\n * - `todo_mark_running` — 把 pending 推进到 running(依赖必须已 done/skipped)\r\n * - `todo_mark_done` — 把 running 标记完成(不允许从 pending 跳过 running)\r\n * - `todo_mark_failed` — 把 running 标记失败(不允许从 pending 跳过 running)\r\n * - `todo_retry` — 把 failed 重置回 pending\r\n *\r\n * @param todoList — 共享的 TodoList 实例\r\n * @returns 5 个工具的数组\r\n */\r\nexport function createHarnessTools(todoList: TodoList): AgentTool<unknown>[] {\r\n return [\r\n makeTodoAddTool(todoList),\r\n makeTodoMarkRunningTool(todoList),\r\n makeTodoMarkDoneTool(todoList),\r\n makeTodoMarkFailedTool(todoList),\r\n makeTodoRetryTool(todoList),\r\n ];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// todo_add\r\n// ---------------------------------------------------------------------------\r\n\r\ninterface TodoAddArgs {\r\n /** 步骤描述(中文) */\r\n content: string;\r\n /** 依赖的 todo id 列表(可选) */\r\n deps?: number[];\r\n}\r\n\r\nfunction makeTodoAddTool(todoList: TodoList): AgentTool<TodoAddArgs> {\r\n return {\r\n name: \"todo_add\",\r\n kind: ToolKind.Other,\r\n description:\r\n \"⚠️ 【复杂任务第一步】添加一个待办到当前任务进度。\\n\\n\" +\r\n \"什么情况下调我:\\n\" +\r\n \" - 需要多步才能完成(改多个文件 / 跨多轮)\\n\" +\r\n \" - 需先读后改(edit/write 之前)\\n\" +\r\n \" - 依赖外部状态(文件、API、运行结果)\\n\\n\" +\r\n \"什么情况下不要调:\\n\" +\r\n \" - 一句话能完成的事(查、列、问)\\n\\n\" +\r\n \"用法:每次加一个 step,多步就调多次。有依赖关系传 deps=[前步 id]。返回分配的 id,后续用 todo_mark_done / todo_mark_failed 引用。\\n\\n\" +\r\n \"示例:用户说「把 X 改成 Y」→ 调 1) todo_add(\\\"读 X\\\") 2) todo_add(\\\"改 X\\\", deps=[0]) 3) todo_add(\\\"检查改动\\\", deps=[1])\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n content: { type: \"string\", description: \"步骤描述(中文,建议简洁)\" },\r\n deps: {\r\n type: \"array\",\r\n description: \"依赖的 todo id 数组(这些都 done 后本项才可执行)\",\r\n items: { type: \"number\" },\r\n },\r\n },\r\n required: [\"content\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args): Promise<ToolResult> {\r\n if (typeof args?.content !== \"string\" || args.content.length === 0) {\r\n return { success: false, data: \"缺少 content 参数\", error: \"INVALID_ARGS\" };\r\n }\r\n const deps = Array.isArray(args.deps) ? args.deps.filter((d) => typeof d === \"number\") : [];\r\n try {\r\n const id = todoList.add(args.content, deps);\r\n return {\r\n success: true,\r\n data: `已添加 todo #${id}:${args.content}${deps.length > 0 ? `(依赖: ${deps.join(\", \")})` : \"\"}`,\r\n summary: `📋 添加 #${id}`,\r\n };\r\n } catch (err) {\r\n return {\r\n success: false,\r\n data: err instanceof Error ? err.message : String(err),\r\n error: \"TODO_DEPS_INVALID\",\r\n };\r\n }\r\n },\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// todo_mark_running\r\n// ---------------------------------------------------------------------------\r\n\r\ninterface TodoMarkRunningArgs {\r\n id: number;\r\n}\r\n\r\n/**\r\n * 把 pending 推进到 running。\r\n *\r\n * 不允许从其他状态转换(避免破坏依赖语义:必须先 mark_running,再 mark_done/failed)。\r\n */\r\nfunction makeTodoMarkRunningTool(todoList: TodoList): AgentTool<TodoMarkRunningArgs> {\r\n return {\r\n name: \"todo_mark_running\",\r\n kind: ToolKind.Other,\r\n description:\r\n \"把 todo 从 pending 推进到 running,表示你「正在做」这一步。\\n\" +\r\n \"前提:该 todo 存在、当前是 pending、且所有 deps 已 done 或 skipped。\\n\" +\r\n \"工作流:先调 todo_mark_running,再去调实际的工具(read_file / edit_file 等),最后用 todo_mark_done 或 todo_mark_failed 收尾。\\n\" +\r\n \"**不要跳过 mark_running 直接 mark_done** — 会破坏依赖检查,下游步骤可能误以为依赖已满足。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n id: { type: \"number\", description: \"todo 的 id\" },\r\n },\r\n required: [\"id\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args): Promise<ToolResult> {\r\n if (typeof args?.id !== \"number\") {\r\n return { success: false, data: \"缺少 id 参数\", error: \"INVALID_ARGS\" };\r\n }\r\n const item = todoList.items.find((it) => it.id === args.id);\r\n if (!item) {\r\n return { success: false, data: `todo #${args.id} 不存在`, error: \"TODO_NOT_FOUND\" };\r\n }\r\n if (item.status !== \"pending\") {\r\n return {\r\n success: false,\r\n data: `todo #${args.id} 当前状态是 ${item.status},只有 pending 可以推进到 running`,\r\n error: \"TODO_INVALID_STATE\",\r\n };\r\n }\r\n const ok = todoList.markRunning(args.id);\r\n if (!ok) {\r\n return {\r\n success: false,\r\n data: `todo #${args.id} 依赖未全部完成(依赖: #${item.deps.join(\", #\")})`,\r\n error: \"TODO_DEPS_NOT_READY\",\r\n };\r\n }\r\n return {\r\n success: true,\r\n data: `已开始 todo #${args.id}:${item.content}`,\r\n summary: `▶ 开始 #${args.id}`,\r\n };\r\n },\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// todo_mark_done\r\n// ---------------------------------------------------------------------------\r\n\r\ninterface TodoMarkDoneArgs {\r\n id: number;\r\n /** 可选:完成证据(一句话) */\r\n evidence?: string;\r\n}\r\n\r\n/**\r\n * 把 running 标记完成。\r\n *\r\n * 故意不允许从 pending 直接 done:必须先 todo_mark_running,避免 LLM 跳过「声明开始」这一步。\r\n */\r\nfunction makeTodoMarkDoneTool(todoList: TodoList): AgentTool<TodoMarkDoneArgs> {\r\n return {\r\n name: \"todo_mark_done\",\r\n kind: ToolKind.Other,\r\n description:\r\n \"把 todo 标记为完成。evidence 填「完成证据」(如「读到 120 行」、「typecheck 通过」、「edit 成功」),不填也行。\\n\" +\r\n \"前提:当前状态必须是 running。若还是 pending,先调 todo_mark_running。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n id: { type: \"number\", description: \"todo 的 id\" },\r\n evidence: { type: \"string\", description: \"完成证据(一句话,可选)\" },\r\n },\r\n required: [\"id\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args): Promise<ToolResult> {\r\n if (typeof args?.id !== \"number\") {\r\n return { success: false, data: \"缺少 id 参数\", error: \"INVALID_ARGS\" };\r\n }\r\n const item = todoList.items.find((it) => it.id === args.id);\r\n if (!item) {\r\n return { success: false, data: `todo #${args.id} 不存在`, error: \"TODO_NOT_FOUND\" };\r\n }\r\n if (item.status !== \"running\") {\r\n return {\r\n success: false,\r\n data: `todo #${args.id} 当前状态是 ${item.status},只有 running 可以 mark_done;如需重试请先 todo_retry`,\r\n error: \"TODO_INVALID_STATE\",\r\n };\r\n }\r\n const ok = todoList.markDone(args.id, args.evidence);\r\n if (!ok) {\r\n return { success: false, data: `todo #${args.id} 状态转换失败`, error: \"TODO_INVALID\" };\r\n }\r\n return {\r\n success: true,\r\n data: `已标记 todo #${args.id} 完成${args.evidence ? `:${args.evidence}` : \"\"}`,\r\n summary: `✅ 完成 #${args.id}`,\r\n };\r\n },\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// todo_mark_failed\r\n// ---------------------------------------------------------------------------\r\n\r\ninterface TodoMarkFailedArgs {\r\n id: number;\r\n /** 失败原因(一句话) */\r\n reason: string;\r\n}\r\n\r\n/**\r\n * 把 running 标记失败。\r\n *\r\n * 故意不允许从 pending 直接 failed:必须先 todo_mark_running。\r\n */\r\nfunction makeTodoMarkFailedTool(todoList: TodoList): AgentTool<TodoMarkFailedArgs> {\r\n return {\r\n name: \"todo_mark_failed\",\r\n kind: ToolKind.Other,\r\n description:\r\n \"把 todo 标记为失败,reason 填「失败原因」(如「old_text 拼错」、「文件不存在」)。\\n\" +\r\n \"前提:当前状态必须是 running。若还是 pending,先调 todo_mark_running。\\n\" +\r\n \"调用后该 step 状态变为 failed。想重试:调 todo_retry(id) 重置回 pending,再 mark_running → 跑工具 → mark_done。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n id: { type: \"number\", description: \"todo 的 id\" },\r\n reason: { type: \"string\", description: \"失败原因(一句话)\" },\r\n },\r\n required: [\"id\", \"reason\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args): Promise<ToolResult> {\r\n if (typeof args?.id !== \"number\") {\r\n return { success: false, data: \"缺少 id 参数\", error: \"INVALID_ARGS\" };\r\n }\r\n if (typeof args?.reason !== \"string\") {\r\n return { success: false, data: \"缺少 reason 参数\", error: \"INVALID_ARGS\" };\r\n }\r\n const item = todoList.items.find((it) => it.id === args.id);\r\n if (!item) {\r\n return { success: false, data: `todo #${args.id} 不存在`, error: \"TODO_NOT_FOUND\" };\r\n }\r\n if (item.status !== \"running\") {\r\n return {\r\n success: false,\r\n data: `todo #${args.id} 当前状态是 ${item.status},只有 running 可以 mark_failed`,\r\n error: \"TODO_INVALID_STATE\",\r\n };\r\n }\r\n const ok = todoList.markFailed(args.id, args.reason);\r\n if (!ok) {\r\n return { success: false, data: `todo #${args.id} 状态转换失败`, error: \"TODO_INVALID\" };\r\n }\r\n return {\r\n success: true,\r\n data: `已标记 todo #${args.id} 失败:${args.reason}`,\r\n summary: `❌ 失败 #${args.id}`,\r\n };\r\n },\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// todo_retry\r\n// ---------------------------------------------------------------------------\r\n\r\ninterface TodoRetryArgs {\r\n id: number;\r\n}\r\n\r\n/**\r\n * 把 failed 重置回 pending,用于「修完 bug 再重跑一次」。\r\n */\r\nfunction makeTodoRetryTool(todoList: TodoList): AgentTool<TodoRetryArgs> {\r\n return {\r\n name: \"todo_retry\",\r\n kind: ToolKind.Other,\r\n description:\r\n \"把失败的 todo 重置回 pending(清空 evidence),然后你可以重新走 todo_mark_running → 跑工具 → todo_mark_done 的流程。\\n\" +\r\n \"前提:当前状态必须是 failed。done / skipped / running 不允许重试(done 不可反悔、running 应先 mark_failed)。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n id: { type: \"number\", description: \"todo 的 id\" },\r\n },\r\n required: [\"id\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args): Promise<ToolResult> {\r\n if (typeof args?.id !== \"number\") {\r\n return { success: false, data: \"缺少 id 参数\", error: \"INVALID_ARGS\" };\r\n }\r\n const item = todoList.items.find((it) => it.id === args.id);\r\n if (!item) {\r\n return { success: false, data: `todo #${args.id} 不存在`, error: \"TODO_NOT_FOUND\" };\r\n }\r\n if (item.status !== \"failed\") {\r\n return {\r\n success: false,\r\n data: `todo #${args.id} 当前状态是 ${item.status},只有 failed 可以重试`,\r\n error: \"TODO_INVALID_STATE\",\r\n };\r\n }\r\n const ok = todoList.resetForRetry(args.id);\r\n if (!ok) {\r\n return { success: false, data: `todo #${args.id} 状态转换失败`, error: \"TODO_INVALID\" };\r\n }\r\n return {\r\n success: true,\r\n data: `已重置 todo #${args.id} 为 pending,可重新跑`,\r\n summary: `🔄 重试 #${args.id}`,\r\n };\r\n },\r\n };\r\n}\r\n","import { execFile } from \"node:child_process\";\r\nimport { promisify } from \"node:util\";\r\nimport type { Checkpoint } from \"./types.js\";\r\n\r\nconst execFileAsync = promisify(execFile);\r\nconst EXEC_OPTIONS = { windowsHide: true } as const;\r\n\r\nexport async function isGitRepo(cwd: string): Promise<boolean> {\r\n try {\r\n await execFileAsync(\"git\", [\"rev-parse\", \"--is-inside-work-tree\"], { cwd, ...EXEC_OPTIONS });\r\n return true;\r\n } catch { return false; }\r\n}\r\n\r\nasync function hasCommits(cwd: string): Promise<boolean> {\r\n try {\r\n await execFileAsync(\"git\", [\"rev-parse\", \"--verify\", \"HEAD\"], { cwd, ...EXEC_OPTIONS });\r\n return true;\r\n } catch { return false; }\r\n}\r\n\r\nasync function hasWorkingChanges(cwd: string): Promise<boolean> {\r\n const out = await execFileAsync(\"git\", [\"status\", \"--porcelain\"], { cwd, ...EXEC_OPTIONS });\r\n return out.stdout.trim().length > 0;\r\n}\r\n\r\nasync function git(args: string[], cwd: string): Promise<string> {\r\n try {\r\n const { stdout } = await execFileAsync(\"git\", args, { cwd, ...EXEC_OPTIONS });\r\n return stdout;\r\n } catch (err: unknown) {\r\n const msg = err instanceof Error ? err.message : String(err);\r\n throw new Error(`git ${args.join(\" \")} 失败: ${msg}`);\r\n }\r\n}\r\n\r\nasync function listStashShas(cwd: string): Promise<string[]> {\r\n const out = await git([\"stash\", \"list\", \"--format=%H\"], cwd);\r\n return out.split(\"\\n\").map((l) => l.trim()).filter(Boolean);\r\n}\r\n\r\nexport async function createCheckpoint(cwd: string): Promise<Checkpoint> {\r\n const timestamp = Date.now();\r\n const inRepo = await isGitRepo(cwd);\r\n if (!inRepo) return { stashSha: \"\", timestamp, cwd, isGitRepo: false };\r\n if (!(await hasCommits(cwd))) return { stashSha: \"\", timestamp, cwd, isGitRepo: true };\r\n if (!(await hasWorkingChanges(cwd))) return { stashSha: \"\", timestamp, cwd, isGitRepo: true };\r\n\r\n // 为了既登记检查点供 rewind 恢复、又**不**让工作区被 stash \"挪走\"\r\n // (`stash push` 默认会把工作区清空,导致后续对话看不到前面对话产生的修改 —— 这就是 bug),\r\n // 这里使用 `stash push -u` 暂存 + 立即 `stash apply` 还原到工作区。\r\n // stash entry 仍保留在 list 中,rewind 时按 SHA 仍可被精准 drop / apply。\r\n const beforeShas = await listStashShas(cwd);\r\n await git([\"stash\", \"push\", \"-m\", `dskcode-cp-${timestamp}`, \"-u\"], cwd);\r\n\r\n const newShas = await listStashShas(cwd);\r\n if (newShas.length === 0) throw new Error(\"git stash push 未能创建 stash entry\");\r\n const newSha = newShas.find((s) => !beforeShas.includes(s)) ?? newShas[0]!;\r\n\r\n // 立即把 stash 还原到工作区:这样前面对话产生的修改不会因为本次 chat 开始\r\n // 而被\"隐藏\"到 stash 里。stash entry 不会被 `apply` 消耗(仅 `pop`/`drop` 才会),\r\n // 所以后续 rewind 仍能基于 newSha 找回这个快照。\r\n await git([\"stash\", \"apply\", newSha], cwd);\r\n\r\n return { stashSha: newSha, timestamp, cwd, isGitRepo: true };\r\n}\r\n\r\nexport async function restoreCheckpointForce(checkpoint: Checkpoint): Promise<void> {\r\n if (!checkpoint.isGitRepo) throw new Error(\"非 git 仓库,无法恢复文件状态\");\r\n if (!checkpoint.stashSha) throw new Error(\"检查点为空(工作区原本就干净),无需恢复\");\r\n\r\n const { cwd, stashSha } = checkpoint;\r\n const currentShas = await listStashShas(cwd);\r\n if (!currentShas.includes(stashSha)) {\r\n throw new Error(\"检查点已失效(stash entry 已被消费或 GC),无法恢复\");\r\n }\r\n await git([\"checkout\", \"--\", \".\"], cwd);\r\n await git([\"clean\", \"-fd\"], cwd);\r\n await git([\"stash\", \"apply\", stashSha], cwd);\r\n const refIndex = currentShas.indexOf(stashSha);\r\n if (refIndex >= 0) await git([\"stash\", \"drop\", `stash@{${refIndex}}`], cwd);\r\n}\r\n\r\n/**\r\n * 将工作区重置为 HEAD 干净状态(丢弃所有未提交修改与未跟踪文件)。\r\n * 用于「目标检查点 stashSha 为空」的 rewind 场景——那时工作区本就干净,\r\n * 但累积的后续对话修改需要被丢弃。\r\n */\r\nexport async function restoreToClean(cwd: string): Promise<void> {\r\n const inRepo = await isGitRepo(cwd);\r\n if (!inRepo) throw new Error(\"非 git 仓库,无法恢复文件状态\");\r\n if (!(await hasCommits(cwd))) throw new Error(\"仓库无 commit,无法恢复\");\r\n await git([\"checkout\", \"--\", \".\"], cwd);\r\n await git([\"clean\", \"-fd\"], cwd);\r\n}\r\n\r\nexport async function discardCheckpoint(checkpoint: Checkpoint): Promise<void> {\r\n if (!checkpoint.isGitRepo || !checkpoint.stashSha) return;\r\n const { cwd, stashSha } = checkpoint;\r\n const shas = await listStashShas(cwd);\r\n const idx = shas.indexOf(stashSha);\r\n if (idx < 0) return;\r\n try { await git([\"stash\", \"drop\", `stash@{${idx}}`], cwd); } catch { /* ignore */ }\r\n}\r\n","import { mkdir, readFile, writeFile, readdir, unlink, rename } from \"node:fs/promises\";\r\nimport { join } from \"node:path\";\r\nimport { randomUUID } from \"node:crypto\";\r\nimport type { StoredSession, SessionSummary } from \"./types.js\";\r\n\r\nexport function defaultSessionsDir(): string {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n return join(home, \".dskcode\", \"sessions\");\r\n}\r\n\r\nexport class SessionStore {\r\n readonly #dir: string;\r\n constructor(dir?: string) { this.#dir = dir ?? defaultSessionsDir(); }\r\n get dir(): string { return this.#dir; }\r\n\r\n async save(session: StoredSession): Promise<void> {\r\n await mkdir(this.#dir, { recursive: true });\r\n const finalPath = join(this.#dir, `${session.id}.json`);\r\n const tmpPath = join(this.#dir, `.${session.id}.json.tmp`);\r\n await writeFile(tmpPath, JSON.stringify(session, null, 2), \"utf-8\");\r\n await rename(tmpPath, finalPath);\r\n }\r\n\r\n async load(id: string): Promise<StoredSession | null> {\r\n const path = join(this.#dir, `${id}.json`);\r\n try {\r\n const content = await readFile(path, \"utf-8\");\r\n return JSON.parse(content) as StoredSession;\r\n } catch (err: unknown) {\r\n if (isENOENT(err)) return null;\r\n throw err;\r\n }\r\n }\r\n\r\n async list(): Promise<SessionSummary[]> {\r\n let files: string[];\r\n try { files = await readdir(this.#dir); }\r\n catch (err: unknown) { if (isENOENT(err)) return []; throw err; }\r\n\r\n const results: SessionSummary[] = [];\r\n for (const file of files) {\r\n if (!file.endsWith(\".json\") || file.startsWith(\".\")) continue;\r\n try {\r\n const content = await readFile(join(this.#dir, file), \"utf-8\");\r\n const s = JSON.parse(content) as StoredSession;\r\n results.push({\r\n id: s.id, title: s.title || \"(无标题)\", updatedAt: s.updatedAt,\r\n cwd: s.cwd, messageCount: s.messages?.length ?? 0,\r\n });\r\n } catch { /* skip corrupted */ }\r\n }\r\n return results.sort((a, b) => b.updatedAt - a.updatedAt);\r\n }\r\n\r\n async delete(id: string): Promise<void> {\r\n const path = join(this.#dir, `${id}.json`);\r\n try { await unlink(path); }\r\n catch (err: unknown) { if (!isENOENT(err)) throw err; }\r\n }\r\n\r\n async exists(id: string): Promise<boolean> { return (await this.load(id)) !== null; }\r\n static newId(): string { return randomUUID(); }\r\n}\r\n\r\nfunction isENOENT(err: unknown): boolean {\r\n return typeof err === \"object\" && err !== null && \"code\" in err && (err as { code: unknown }).code === \"ENOENT\";\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 对话日志记录器 — 将用户与 AI 的完整交互过程写入人类可读的日志文件\r\n//\r\n// 设计要点:\r\n// 1. 每条事件输出为\"分隔线 + 标题行 + pretty JSON + 空行\"的多行块,\r\n// 方便人眼直接阅读;同时 pretty JSON 仍可被机器按段解析。\r\n// 2. 异步写入,错误静默处理,绝不影响主对话流程\r\n// 3. 按会话 ID 分文件,一个会话一个日志文件\r\n// 4. 工具结果数据截断到 MAX_DATA_LEN,避免日志文件膨胀\r\n// 5. 写入操作串行化(队列),保证事件顺序与实际发生顺序一致\r\n// 6. 自动从 V8 栈中提取调用方文件:行号,并写入人类可读的本地时间字符串\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { mkdir, appendFile } from \"node:fs/promises\";\r\nimport { fileURLToPath } from \"node:url\";\r\nimport { join, basename, relative, sep } from \"node:path\";\r\nimport type { LogEvent, LogEventInput } from \"./types.js\";\r\n\r\n/** logger 源文件所在的目录名,用于从栈中过滤掉 logger 内部帧 */\r\nconst LOGGER_DIR = \"logger\";\r\n\r\n/** 工具结果数据的最大记录长度(超出截断并标记) */\r\nconst MAX_DATA_LEN = 2000;\r\n\r\n/** 写入队列最大长度,超过则丢弃最旧的事件(防止极端情况下无限堆积) */\r\nconst MAX_QUEUE = 500;\r\n\r\n/** 默认日志根目录:~/.dskcode/logs */\r\nexport function defaultLogsDir(): string {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n return join(home, \".dskcode\", \"logs\");\r\n}\r\n\r\n/** ConversationLogger 构造选项 */\r\nexport interface ConversationLoggerOptions {\r\n /** 是否启用日志记录,默认 true */\r\n enabled?: boolean;\r\n /**\r\n * 日志根目录,默认 ~/.dskcode/logs。\r\n * 实际日志路径为 `<logsDir>/<项目名>/<sessionId>.jsonl`。\r\n * 测试中可指定临时目录。\r\n */\r\n logsDir?: string;\r\n}\r\n\r\n/**\r\n * 从工作目录提取项目名(用于日志分组)。\r\n * 取 cwd 的 basename,根目录等边界情况兜底为 \"_root\"。\r\n */\r\nfunction projectNameFromCwd(cwd: string): string {\r\n const name = basename(cwd);\r\n return name || \"_root\";\r\n}\r\n\r\n/**\r\n * 对话日志记录器。\r\n *\r\n * 每个会话创建一个实例,日志写入 `~/.dskcode/logs/<项目名>/<sessionId>.jsonl`。\r\n * 所有写入操作异步串行执行,调用方无需 await(fire-and-forget)。\r\n */\r\nexport class ConversationLogger {\r\n /** 所有活跃的 logger 实例(用于退出时统一 flush) */\r\n static readonly #instances = new Set<ConversationLogger>();\r\n\r\n /**\r\n * 刷新所有活跃 logger 实例的缓冲,确保事件落盘。\r\n * 进程退出前调用。\r\n */\r\n static async flushAll(): Promise<void> {\r\n const promises: Promise<void>[] = [];\r\n for (const logger of ConversationLogger.#instances) {\r\n promises.push(logger.flush());\r\n }\r\n await Promise.allSettled(promises);\r\n }\r\n\r\n readonly #logPath: string;\r\n readonly #enabled: boolean;\r\n #queue: Promise<void> = Promise.resolve();\r\n #queueLen = 0;\r\n #closed = false;\r\n\r\n constructor(sessionId: string, cwd: string, options?: ConversationLoggerOptions) {\r\n this.#enabled = options?.enabled ?? true;\r\n if (!this.#enabled) {\r\n this.#logPath = \"\";\r\n return;\r\n }\r\n const baseDir = options?.logsDir ?? defaultLogsDir();\r\n const projectName = projectNameFromCwd(cwd);\r\n this.#logPath = join(baseDir, projectName, `${sessionId}.jsonl`);\r\n ConversationLogger.#instances.add(this);\r\n }\r\n\r\n /** 日志文件完整路径(禁用时返回空字符串) */\r\n get logPath(): string {\r\n return this.#logPath;\r\n }\r\n\r\n /** 是否启用日志记录 */\r\n get enabled(): boolean {\r\n return this.#enabled;\r\n }\r\n\r\n /**\r\n * 记录一个事件。\r\n *\r\n * 内部将事件序列化为 pretty JSON 并以多行块的形式追加到日志文件。\r\n * 写入操作串行排队,保证事件顺序。调用方无需 await。\r\n */\r\n log(event: LogEventInput): void {\r\n if (!this.#enabled || this.#closed) return;\r\n\r\n // 队列过长时丢弃最旧事件,防止内存无限增长\r\n if (this.#queueLen >= MAX_QUEUE) return;\r\n\r\n // 自动注入可读时间与调用方位置(栈捕获)\r\n const enriched: LogEvent = {\r\n ...event,\r\n time: event.time ?? formatTime(event.ts),\r\n loc: event.loc ?? captureCallerLocation(),\r\n };\r\n\r\n this.#queueLen++;\r\n this.#queue = this.#queue\r\n .then(() => this.#writeBlock(enriched))\r\n .catch(() => {\r\n /* 日志写入失败静默忽略 */\r\n })\r\n .finally(() => this.#queueLen--);\r\n }\r\n\r\n /**\r\n * 关闭日志记录器。\r\n *\r\n * 标记关闭,不再接受新事件,但会等待队列中已有事件写入完成。\r\n * 返回的 Promise resolve 后保证所有已 log 的事件都已落盘。\r\n */\r\n async flush(): Promise<void> {\r\n this.#closed = true;\r\n await this.#queue.catch(() => {});\r\n ConversationLogger.#instances.delete(this);\r\n }\r\n\r\n /** 将一条事件以多行块的形式写入日志文件 */\r\n async #writeBlock(event: LogEvent): Promise<void> {\r\n const dir = join(this.#logPath, \"..\");\r\n await mkdir(dir, { recursive: true });\r\n const block = formatBlock(event);\r\n await appendFile(this.#logPath, block, \"utf-8\");\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 便捷方法 — 封装常见事件类型,减少调用方样板代码\r\n // -----------------------------------------------------------------------\r\n\r\n /** 记录会话开始 */\r\n logSessionStart(sessionId: string, cwd: string, model: string, mode: string): void {\r\n this.log({ ts: Date.now(), type: \"session_start\", sessionId, cwd, model, mode });\r\n }\r\n\r\n /** 记录用户消息 */\r\n logUserMessage(content: string): void {\r\n this.log({ ts: Date.now(), type: \"user_message\", content });\r\n }\r\n\r\n /** 记录助手文本(一轮中模型输出的完整文本) */\r\n logAssistantText(content: string, round: number): void {\r\n this.log({ ts: Date.now(), type: \"assistant_text\", content, round });\r\n }\r\n\r\n /** 记录思考链(thinking 模式下模型的 CoT) */\r\n logReasoning(content: string, round: number): void {\r\n this.log({ ts: Date.now(), type: \"reasoning\", content, round });\r\n }\r\n\r\n /** 记录工具调用 */\r\n logToolCall(name: string, callId: string, args: string, round: number): void {\r\n this.log({ ts: Date.now(), type: \"tool_call\", name, callId, arguments: args, round });\r\n }\r\n\r\n /** 记录工具结果 */\r\n logToolResult(\r\n name: string,\r\n callId: string,\r\n success: boolean,\r\n data: string,\r\n error: string | undefined,\r\n elapsed: number | undefined,\r\n round: number,\r\n ): void {\r\n this.log({\r\n ts: Date.now(),\r\n type: \"tool_result\",\r\n name,\r\n callId,\r\n success,\r\n data: truncate(data),\r\n ...(error ? { error } : {}),\r\n ...(elapsed !== undefined ? { elapsed } : {}),\r\n round,\r\n });\r\n }\r\n\r\n /** 记录 Token 用量与费用 */\r\n logUsage(\r\n model: string,\r\n promptTokens: number,\r\n completionTokens: number,\r\n cachedPromptTokens: number | undefined,\r\n cost: number,\r\n round: number,\r\n ): void {\r\n this.log({\r\n ts: Date.now(),\r\n type: \"usage\",\r\n model,\r\n promptTokens,\r\n completionTokens,\r\n ...(cachedPromptTokens !== undefined ? { cachedPromptTokens } : {}),\r\n cost,\r\n round,\r\n });\r\n }\r\n\r\n /** 记录错误 */\r\n logError(message: string, stack?: string): void {\r\n this.log({ ts: Date.now(), type: \"error\", message, ...(stack ? { stack } : {}) });\r\n }\r\n\r\n /** 记录一轮对话完成 */\r\n logTurnDone(elapsed: number, toolRounds: number): void {\r\n this.log({ ts: Date.now(), type: \"turn_done\", elapsed, toolRounds });\r\n }\r\n\r\n /** 记录会话结束 */\r\n logSessionEnd(elapsed: number): void {\r\n this.log({ ts: Date.now(), type: \"session_end\", elapsed });\r\n }\r\n\r\n /** 记录反射(工具失败归因注入到下一轮 prompt 时) */\r\n logReflections(\r\n items: Array<{ category: string; toolName: string; hint: string }>,\r\n ): void {\r\n this.log({ ts: Date.now(), type: \"reflection\", items });\r\n }\r\n}\r\n\r\n/**\r\n * 截断过长的字符串,超出部分用占位符标记。\r\n */\r\nfunction truncate(text: string): string {\r\n if (text.length <= MAX_DATA_LEN) return text;\r\n return text.slice(0, MAX_DATA_LEN) + `...[已截断,原始长度 ${text.length}]`;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 时间格式化\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 把毫秒时间戳格式化为 `YYYY-MM-DD HH:mm:ss.SSS`(本地时区) */\r\nfunction formatTime(ts: number): string {\r\n const d = new Date(ts);\r\n const pad = (n: number, w = 2) => String(n).padStart(w, \"0\");\r\n return (\r\n `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ` +\r\n `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}.` +\r\n `${pad(d.getMilliseconds(), 3)}`\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 调用方位置捕获(V8 栈解析)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 缓存项目根目录:用于将栈中绝对路径转换为项目相对路径。\r\n * 第一次调用时基于 import.meta.url 推断。\r\n */\r\nlet projectRootCache: string | null = null;\r\nfunction getProjectRoot(): string {\r\n if (projectRootCache !== null) return projectRootCache;\r\n try {\r\n // 当前 logger.ts 的 URL,形如 file:///D:/projects/.../ts-version/src/logger/logger.ts\r\n const here = fileURLToPath(import.meta.url);\r\n // 向上找 src/ 的父目录作为项目根\r\n const segments = here.split(sep);\r\n const srcIdx = segments.lastIndexOf(\"src\");\r\n projectRootCache = srcIdx > 0 ? segments.slice(0, srcIdx).join(sep) : here;\r\n } catch {\r\n projectRootCache = \"\";\r\n }\r\n return projectRootCache;\r\n}\r\n\r\n/**\r\n * 匹配 V8 栈帧的 `at ... (file:line:col)` 或 `at file:line:col`。\r\n * 捕获组:1=文件路径(可能为 file:// URL),2=行号。\r\n */\r\nconst STACK_FRAME_RE = /\\s+at\\s+.+?\\((.+):(\\d+):\\d+\\)|\\s+at\\s+(.+):(\\d+):\\d+/;\r\n\r\n/**\r\n * 从 V8 栈中查找第一个不在 logger 目录下的栈帧,返回其文件:行号。\r\n * 如果解析失败,返回 `{ file: \"<unknown>\", line: 0 }`。\r\n */\r\nfunction captureCallerLocation(): { file: string; line: number } {\r\n let stack: string | undefined;\r\n try {\r\n const err = new Error();\r\n Error.captureStackTrace?.(err, captureCallerLocation);\r\n stack = err.stack;\r\n } catch {\r\n return { file: \"<unknown>\", line: 0 };\r\n }\r\n if (!stack) return { file: \"<unknown>\", line: 0 };\r\n\r\n const root = getProjectRoot();\r\n const lines = stack.split(\"\\n\");\r\n\r\n for (const raw of lines) {\r\n const m = STACK_FRAME_RE.exec(raw);\r\n if (!m) continue;\r\n const filePath = m[1] ?? m[3] ?? \"\";\r\n const lineNo = Number(m[2] ?? m[4] ?? \"0\");\r\n if (!filePath) continue;\r\n\r\n // 跳过 logger 目录内部的帧\r\n const normalized = filePath.replace(/^file:\\/\\/\\//, \"\").replace(/^file:\\/\\//, \"\");\r\n if (\r\n normalized.includes(`${sep}${LOGGER_DIR}${sep}`) ||\r\n normalized.endsWith(`${sep}${LOGGER_DIR}`)\r\n ) {\r\n continue;\r\n }\r\n // 跳过 node_modules\r\n if (normalized.includes(`${sep}node_modules${sep}`)) continue;\r\n\r\n let rel = normalized;\r\n if (root && normalized.startsWith(root)) {\r\n rel = normalized.slice(root.length).replace(/^[/\\\\]/, \"\") || basename(normalized);\r\n }\r\n return { file: rel || basename(normalized), line: lineNo };\r\n }\r\n return { file: \"<unknown>\", line: 0 };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 多行块格式化\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 分隔线宽度 */\r\nconst SEPARATOR_WIDTH = 80;\r\nconst SEPARATOR = \"─\".repeat(SEPARATOR_WIDTH);\r\n\r\n/**\r\n * 将事件渲染为多行文本块:\r\n *\r\n * ────────...\r\n * [2026-06-29 14:30:45.123] [user_message] @ src/agent/index.ts:253\r\n * {\r\n * \"ts\": 1782702446439,\r\n * \"time\": \"2026-06-29 14:30:45.123\",\r\n * \"loc\": { \"file\": \"src/agent/index.ts\", \"line\": 253 },\r\n * \"type\": \"user_message\",\r\n * \"content\": \"你好\"\r\n * }\r\n *\r\n * (末尾保留一个空行)\r\n */\r\nfunction formatBlock(event: LogEvent): string {\r\n const header = `[${event.time}] [${event.type}] @ ${event.loc.file}:${event.loc.line}`;\r\n const body = JSON.stringify(event, null, 2);\r\n return `${SEPARATOR}\\n${header}\\n${body}\\n\\n`;\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// Agent 会话 — 协调者(Coordinator)\r\n//\r\n// 职责(瘦身后):\r\n// 1. 编排 chat() 主循环(用户输入 → LLM → 工具 → 持久化)\r\n// 2. 协调三个纯职责子模块:\r\n// - ToolExecutor — 纯执行(tool-executor.ts)\r\n// - StormDetector — 纯判断(storm-detector.ts)\r\n// - buildToolDefinitions / trimMessages — 纯计算(tool-definitions.ts / message-builder.ts)\r\n// 3. 维护会话级状态:消息历史、checkpoints、模式、成本追踪、日志\r\n// 4. 持久化与恢复\r\n//\r\n// 函数注释规范见仓库根 AGENTS.md「函数注释规范」一节。\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type {\r\n ChatMessage,\r\n ChatOptions,\r\n Provider,\r\n ProviderToolCall,\r\n UsageInfo,\r\n ModelId,\r\n} from \"../provider/index.js\";\r\nimport { CostTracker, calculateCost } from \"../provider/index.js\";\r\nimport type { AnyAgentTool } from \"../tool/index.js\";\r\nimport type { Gate, ToolCallRecord } from \"../tool/types.js\";\r\nimport { AlwaysAllowGate, ToolKind, eraseTool } from \"../tool/types.js\";\r\nimport type { AgentEvent, SessionMode, SystemPromptOptions } from \"./types.js\";\r\nimport { buildSystemPrompt, buildPlanSystemPrompt } from \"./system-prompt.js\";\r\nimport { trimMessages, buildApiMessages } from \"./message-builder.js\";\r\nimport { ToolRegistry } from \"../tool/registry.js\";\r\nimport { ToolExecutor } from \"./tool-executor.js\";\r\nimport { StormDetector } from \"./storm-detector.js\";\r\nimport { Reflector, type AnalyzeItem } from \"./reflector.js\";\r\nimport { buildToolDefinitions } from \"./tool-definitions.js\";\r\nimport { TodoList } from \"../harness/todo-list.js\";\r\nimport { createHarnessTools } from \"../harness/tools.js\";\r\nimport {\r\n createCheckpoint,\r\n restoreCheckpointForce,\r\n restoreToClean,\r\n discardCheckpoint,\r\n type Checkpoint,\r\n} from \"../checkpoint/index.js\";\r\nimport { SessionStore, type StoredSession } from \"../session-store/index.js\";\r\nimport { ConversationLogger } from \"../logger/index.js\";\r\n\r\n/**\r\n * Session 构造选项。\r\n *\r\n * @field cwd — 当前工作目录,决定 checkpoint/工具路径解析的根\r\n * @field maxToolRounds — 单次 chat() 内允许的最大工具调用轮数(防无限循环)\r\n * @field reservedForOutput — 上下文裁剪时为模型输出预留的 token 数\r\n * @field preserveRecentRounds — 上下文裁剪时强制保留的最近回合数\r\n * @field projectContext — 项目级背景(如 AGENTS.md 内容),注入到 system prompt\r\n * @field gate — 工具权限门;不传则使用 AlwaysAllowGate(全部放行)\r\n * @field writeRoots — 写工具允许的根目录列表(绝对路径)\r\n * @field sessionId — 复用会话 ID;不传则生成新 UUID\r\n * @field store — 会话存储实例;传 false 禁用持久化(用于测试)\r\n * @field enableCheckpoint — 是否启用 git 检查点(/rewind 需要),默认 true\r\n * @field enableLog — 是否启用对话日志(写入 .dskcode/logs/),默认 true\r\n */\r\nexport interface SessionOptions {\r\n cwd?: string;\r\n maxToolRounds?: number;\r\n reservedForOutput?: number;\r\n preserveRecentRounds?: number;\r\n projectContext?: string;\r\n gate?: Gate;\r\n writeRoots?: string[];\r\n /**\r\n * 会话 ID。传入则复用,不传则生成新 UUID。\r\n * 使用 Session.resume() 恢复会话时必须传。\r\n */\r\n sessionId?: string;\r\n /**\r\n * 会话存储实例。不传则使用默认 ~/.dskcode/sessions/。\r\n * 传 false 禁用持久化(用于测试和不需要保存的场景)。\r\n */\r\n store?: SessionStore | false;\r\n /** 是否启用 checkpoint(/rewind 需要),默认 true */\r\n enableCheckpoint?: boolean;\r\n /** 是否启用对话日志记录(写入 .dskcode/logs/),默认 true */\r\n enableLog?: boolean;\r\n /** 是否启用 Reflector 失败归因注入(默认 true)。传 false 可彻底关闭 */\r\n enableReflection?: boolean;\r\n /** 是否启用 Harness(TodoList 规划 + 任务自检提示)。默认 true */\r\n enableHarness?: boolean;\r\n}\r\n\r\n/**\r\n * 消息检查点信息(对外暴露给 UI 展示 /rewind 列表用)。\r\n *\r\n * @field index — 该 user 消息在 messages 数组中的索引\r\n * @field preview — 用户消息前 80 字\r\n * @field timestamp — checkpoint 创建时间(毫秒)\r\n * @field isGitRepo — 该检查点对应的工作区是否为 git 仓库(决定能否文件回退)\r\n */\r\nexport interface MessageCheckpointInfo {\r\n index: number;\r\n preview: string;\r\n timestamp: number;\r\n isGitRepo: boolean;\r\n}\r\n\r\n/**\r\n * rewind 操作结果。\r\n * - `ok: true` 时 `fileRestored` 标明文件工作区是否被回退\r\n * - `ok: false` 时 `error` 为人类可读的错误描述\r\n */\r\nexport type RewindResult =\r\n | { ok: true; fileRestored: boolean }\r\n | { ok: false; error: string };\r\n\r\n/**\r\n * Session — 单个对话会话的协调者。\r\n *\r\n * 责任范围(瘦身后):\r\n * 1. 编排 chat() 主循环:用户输入 → 构建 prompt → 调 LLM → 工具执行 → 持久化\r\n * 2. 维护会话级状态:messages、checkpoints、mode、cost、log\r\n * 3. 不再直接实现工具执行与风暴检测,而是委托给 ToolExecutor / StormDetector\r\n */\r\nexport class Session {\r\n /** 当前会话的消息历史(含 system/user/assistant/tool),可外部只读 */\r\n readonly #messages: ChatMessage[] = [];\r\n /** LLM Provider(DeepSeek 等) */\r\n readonly #provider: Provider;\r\n /** 工具注册表(所有可用工具) */\r\n readonly #toolRegistry: ToolRegistry;\r\n /** 成本追踪器(今日 + 本会话) */\r\n readonly #costTracker: CostTracker;\r\n /** 归一化后的构造选项(带默认值) */\r\n readonly #options: Required<\r\n Pick<\r\n SessionOptions,\r\n \"cwd\" | \"maxToolRounds\" | \"reservedForOutput\" | \"preserveRecentRounds\"\r\n >\r\n > & {\r\n projectContext?: string;\r\n gate: Gate;\r\n writeRoots: string[];\r\n enableCheckpoint: boolean;\r\n enableLog: boolean;\r\n enableReflection: boolean;\r\n enableHarness: boolean;\r\n };\r\n /** 中止信号控制器(abort() 时触发,传递给 LLM 和工具) */\r\n readonly #abortController = new AbortController();\r\n\r\n /** 会话唯一 ID(UUID) */\r\n readonly #sessionId: string;\r\n /** 持久化存储;传 false 时为 null */\r\n readonly #store: SessionStore | null;\r\n /** 会话创建时间(毫秒) */\r\n #createdAt: number;\r\n /** 节流持久化定时器(500ms debounce) */\r\n #persistTimer: NodeJS.Timeout | null = null;\r\n\r\n /** user 消息 → Checkpoint 映射(仅给 user 消息建点) */\r\n #checkpoints = new Map<number, Checkpoint>();\r\n /** 最近的失败记录(仅失败的,用于风暴检测) */\r\n #stormRecords: ToolCallRecord[] = [];\r\n /** 当前会话模式:code(默认)/ plan(只读) */\r\n #mode: SessionMode = \"code\";\r\n /** 对话日志记录器(写入 .dskcode/logs/) */\r\n readonly #logger: ConversationLogger;\r\n /** 风暴检测器(连续 3 次同工具同错码失败时中断本轮) */\r\n readonly #stormDetector: StormDetector;\r\n /** 失败归因 Reflector(将本轮失败原因拼到下一轮 prompt;null 表示已关闭) */\r\n readonly #reflector: Reflector | null;\r\n /** 本轮工具执行结果(仅在 chat() 循环内使用;用于下一轮注入 reflection) */\r\n #lastRoundResults: AnalyzeItem[] | null = null;\r\n /** Harness 任务列表(仅在 enableHarness 时非 null) */\r\n readonly #todoList: TodoList | null;\r\n /** 本会话内是否已发出过「未拆 todo」护栏提示(避免每轮骚扰) */\r\n #harnessHintEmitted = false;\r\n\r\n /**\r\n * 构造一个 Session。\r\n *\r\n * @param provider — LLM Provider 实例(必须)\r\n * @param tools — 工具列表或已初始化的 ToolRegistry(默认空)\r\n * @param costTracker — 成本追踪器(默认新建)\r\n * @param options — 会话选项(详见 SessionOptions)\r\n *\r\n * @sideEffect 创建 .dskcode/logs/ 下的日志文件并写入 session_start 事件\r\n */\r\n constructor(\r\n provider: Provider,\r\n tools: AnyAgentTool[] | ToolRegistry = [],\r\n costTracker?: CostTracker,\r\n options?: SessionOptions,\r\n ) {\r\n this.#provider = provider;\r\n if (tools instanceof ToolRegistry) {\r\n this.#toolRegistry = tools;\r\n } else {\r\n this.#toolRegistry = new ToolRegistry();\r\n this.#toolRegistry.registerAll(tools);\r\n }\r\n this.#costTracker = costTracker ?? new CostTracker();\r\n this.#options = {\r\n cwd: options?.cwd ?? process.cwd(),\r\n maxToolRounds: options?.maxToolRounds ?? 20,\r\n reservedForOutput: options?.reservedForOutput ?? 4096,\r\n preserveRecentRounds: options?.preserveRecentRounds ?? 10,\r\n projectContext: options?.projectContext,\r\n gate: options?.gate ?? new AlwaysAllowGate(),\r\n writeRoots: options?.writeRoots ?? [options?.cwd ?? process.cwd()],\r\n enableCheckpoint: options?.enableCheckpoint ?? true,\r\n enableLog: options?.enableLog ?? true,\r\n enableReflection: options?.enableReflection !== false,\r\n enableHarness: options?.enableHarness !== false,\r\n };\r\n this.#sessionId = options?.sessionId ?? SessionStore.newId();\r\n this.#store =\r\n options?.store === false ? null : (options?.store ?? new SessionStore());\r\n this.#createdAt = Date.now();\r\n this.#logger = new ConversationLogger(this.#sessionId, this.#options.cwd, {\r\n enabled: this.#options.enableLog,\r\n });\r\n this.#logger.logSessionStart(\r\n this.#sessionId,\r\n this.#options.cwd,\r\n this.#provider.model(),\r\n this.#mode,\r\n );\r\n this.#stormDetector = new StormDetector({ threshold: 3 });\r\n this.#reflector = this.#options.enableReflection ? new Reflector() : null;\r\n\r\n // Harness 初始化(TodoList + 5 个新工具:todo_add / mark_running / mark_done / mark_failed / retry)\r\n if (this.#options.enableHarness) {\r\n const todoList = new TodoList();\r\n this.#todoList = todoList;\r\n for (const t of createHarnessTools(todoList)) {\r\n this.#toolRegistry.registerErased(eraseTool(t));\r\n }\r\n } else {\r\n this.#todoList = null;\r\n }\r\n }\r\n\r\n /** 当前会话的完整消息历史(只读视图) */\r\n get messages(): readonly ChatMessage[] {\r\n return this.#messages;\r\n }\r\n /** 本次会话累计成本(人民币元) */\r\n get accumulatedCost(): number {\r\n return this.#costTracker.sessionTotalCost;\r\n }\r\n /** 成本追踪器(含今日总成本、会话成本、模型维度统计) */\r\n get costTracker(): CostTracker {\r\n return this.#costTracker;\r\n }\r\n /** 当前模型标识(如 \"deepseek-v4-flash\") */\r\n get model(): string {\r\n return this.#provider.model();\r\n }\r\n /** 工具注册表(可外部读 / 运行时 register) */\r\n get toolRegistry(): ToolRegistry {\r\n return this.#toolRegistry;\r\n }\r\n /** 当前模式:\"code\" | \"plan\" */\r\n get mode(): SessionMode {\r\n return this.#mode;\r\n }\r\n /** 会话 ID(UUID) */\r\n get id(): string {\r\n return this.#sessionId;\r\n }\r\n /** 持久化存储实例(禁用持久化时为 null) */\r\n get store(): SessionStore | null {\r\n return this.#store;\r\n }\r\n /** 会话创建时间戳(毫秒) */\r\n get createdAt(): number {\r\n return this.#createdAt;\r\n }\r\n\r\n /**\r\n * 切换会话模式。\r\n *\r\n * @param mode — \"code\":全部工具;\"plan\":只暴露只读工具\r\n * @returns 设置后的模式(便于链式调用)\r\n */\r\n setMode(mode: SessionMode): SessionMode {\r\n this.#mode = mode;\r\n return this.#mode;\r\n }\r\n\r\n /**\r\n * chat() — 接收一轮用户输入,串起 LLM 调用 / 工具执行 / 风暴中断 / 持久化。\r\n *\r\n * 主循环每轮做:\r\n * 1. 构建 system prompt + 裁剪消息 + 拼装 apiMessages\r\n * 2. 调 provider.chat() 拿流式 chunks,向外 yield text_delta / usage 事件\r\n * 3. 若本轮有 tool_calls:\r\n * a. 先调 StormDetector.shouldBreak 判定是否中断风暴\r\n * b. 调 ToolExecutor.executeBatch 执行工具\r\n * c. 把工具结果 yield 出去并写入 messages\r\n * 4. 持续到 LLM 不再调工具 或 达到 maxToolRounds\r\n *\r\n * @param userInput — 用户本轮输入\r\n * @param opts — 透传给 provider.chat() 的 ChatOptions(thinking / tool_choice 等)\r\n * @yields AgentEvent — text_delta / tool_calls / tool_result / usage / done / error\r\n *\r\n * @sideEffect 写入 messages、logger、costTracker、可能触发 checkpoint / persist\r\n */\r\n async *chat(userInput: string, opts?: ChatOptions): AsyncGenerator<AgentEvent> {\r\n this.#messages.push({ role: \"user\", content: userInput });\r\n this.#logger.logUserMessage(userInput);\r\n const userMsgIndex = this.#messages.length - 1;\r\n if (this.#options.enableCheckpoint) {\r\n try {\r\n const checkpoint = await createCheckpoint(this.#options.cwd);\r\n this.#checkpoints.set(userMsgIndex, checkpoint);\r\n } catch {\r\n /* swallow — checkpoint 失败不应阻塞对话 */\r\n }\r\n }\r\n\r\n const startTime = Date.now();\r\n let toolRounds = 0;\r\n // 工具执行器:本轮内共享同一 signal,避免每次调用都重新构建\r\n const toolExecutor = this.#buildToolExecutor();\r\n // Harness 护栏提示重置:每个新用户输入重新评估一次「未拆 todo」问题\r\n this.#harnessHintEmitted = false;\r\n\r\n try {\r\n while (toolRounds < this.#options.maxToolRounds) {\r\n // 构建本轮 system prompt。若上一轮工具调用有失败,Reflector 会把归因\r\n // 信息拼到 prompt 尾部(仅在 reflector 开启 + lastRoundResults 非空时生效)\r\n let systemPrompt = this.#buildSystemPrompt();\r\n if (this.#reflector && this.#lastRoundResults) {\r\n const reflections = this.#reflector.analyze(this.#lastRoundResults, {\r\n writeRoots: this.#options.writeRoots,\r\n cwd: this.#options.cwd,\r\n });\r\n if (reflections.length > 0) {\r\n systemPrompt = this.#reflector.injectIntoPrompt(systemPrompt, reflections);\r\n this.#logger.logReflections(\r\n reflections.map((r) => ({\r\n category: r.category,\r\n toolName: r.toolName,\r\n hint: r.hint,\r\n })),\r\n );\r\n }\r\n this.#lastRoundResults = null;\r\n }\r\n const [trimmed] = trimMessages([...this.#messages], {\r\n model: this.#provider.model() as unknown as ModelId,\r\n reservedForOutput: this.#options.reservedForOutput,\r\n systemPrompt,\r\n preserveRecentRounds: this.#options.preserveRecentRounds,\r\n });\r\n const apiMessages = buildApiMessages(systemPrompt, trimmed);\r\n\r\n const toolDefs = buildToolDefinitions(this.#toolRegistry, this.#mode);\r\n const stream = this.#provider.chat(apiMessages, {\r\n signal: this.#abortController.signal,\r\n tools: toolDefs.length > 0 ? toolDefs : undefined,\r\n thinkingAllowed: opts?.thinkingAllowed,\r\n thinkingEffort: opts?.thinkingEffort,\r\n responseFormat: opts?.responseFormat,\r\n toolChoice: opts?.toolChoice,\r\n });\r\n\r\n const modelId = this.#provider.model() as unknown as ModelId;\r\n\r\n let accumulatedText = \"\";\r\n let accumulatedReasoning = \"\";\r\n let lastUsage: UsageInfo | undefined;\r\n let lastToolCalls: ProviderToolCall[] | undefined;\r\n let _lastFinishReason: string | null = null;\r\n\r\n // 本轮输入 token 估算(在流式开始前一次计算;DeepSeek 流式只在最后一个\r\n // chunk 才返回 usage,中间过程中 UI 无法得知当前消耗,因此客户端本地估算)\r\n let estimatedInputTokens = 0;\r\n for (const m of apiMessages) {\r\n estimatedInputTokens += this.#provider.countTokens(m.content) + 10;\r\n }\r\n let lastEmitMs = 0;\r\n const EST_EMIT_INTERVAL_MS = 300;\r\n\r\n for await (const chunk of stream) {\r\n if (chunk.reasoningContent) {\r\n accumulatedReasoning += chunk.reasoningContent;\r\n yield { type: \"reasoning_delta\", content: chunk.reasoningContent };\r\n }\r\n if (chunk.content) {\r\n accumulatedText += chunk.content;\r\n yield { type: \"text_delta\", content: chunk.content };\r\n\r\n // 节流推送估算 usage,让 UI 能实时显示\"已消耗 X tokens / ¥Y\"\r\n const now = Date.now();\r\n if (now - lastEmitMs >= EST_EMIT_INTERVAL_MS) {\r\n lastEmitMs = now;\r\n const estOutput = this.#provider.countTokens(accumulatedText);\r\n const estUsage: UsageInfo = {\r\n promptTokens: estimatedInputTokens,\r\n completionTokens: estOutput,\r\n };\r\n const estCost = calculateCost(estUsage, modelId);\r\n yield {\r\n type: \"usage\",\r\n usage: estUsage,\r\n model: modelId,\r\n cost: estCost.totalCost,\r\n estimated: true,\r\n };\r\n }\r\n }\r\n if (chunk.toolCalls && chunk.toolCalls.length > 0)\r\n lastToolCalls = chunk.toolCalls;\r\n if (chunk.usage) {\r\n lastUsage = chunk.usage;\r\n // 真实 usage 覆盖估算值\r\n const realCost = calculateCost(chunk.usage, modelId);\r\n yield {\r\n type: \"usage\",\r\n usage: chunk.usage,\r\n model: modelId,\r\n cost: realCost.totalCost,\r\n estimated: false,\r\n };\r\n }\r\n if (chunk.finishReason) _lastFinishReason = chunk.finishReason;\r\n }\r\n\r\n // 流式末尾再发一次最终 usage,作为权威值(此时 setCurrentUsage 会覆盖估算值)\r\n if (accumulatedText && !lastUsage) {\r\n const estOutput = this.#provider.countTokens(accumulatedText);\r\n const estUsage: UsageInfo = {\r\n promptTokens: estimatedInputTokens,\r\n completionTokens: estOutput,\r\n };\r\n const estCost = calculateCost(estUsage, modelId);\r\n yield {\r\n type: \"usage\",\r\n usage: estUsage,\r\n model: modelId,\r\n cost: estCost.totalCost,\r\n estimated: true,\r\n };\r\n }\r\n\r\n if (lastUsage) {\r\n this.#costTracker.record(lastUsage, modelId);\r\n const cost = calculateCost(lastUsage, modelId);\r\n this.#logger.logUsage(\r\n modelId,\r\n lastUsage.promptTokens,\r\n lastUsage.completionTokens,\r\n lastUsage.cachedPromptTokens,\r\n cost.totalCost,\r\n toolRounds,\r\n );\r\n yield { type: \"usage\", usage: lastUsage, model: modelId };\r\n }\r\n\r\n const assistantMsg: ChatMessage = { role: \"assistant\", content: accumulatedText };\r\n if (lastToolCalls && lastToolCalls.length > 0)\r\n assistantMsg.toolCalls = lastToolCalls;\r\n this.#messages.push(assistantMsg);\r\n this.#logger.logAssistantText(accumulatedText, toolRounds);\r\n // 思考链只入日志、不进 messages(多轮时不回传 API 也不会出错)\r\n if (accumulatedReasoning) {\r\n this.#logger.logReasoning(accumulatedReasoning, toolRounds);\r\n }\r\n\r\n if (lastToolCalls && lastToolCalls.length > 0) {\r\n yield { type: \"tool_calls\", calls: lastToolCalls };\r\n for (const tc of lastToolCalls) {\r\n this.#logger.logToolCall(tc.name, tc.id, tc.arguments, toolRounds);\r\n }\r\n\r\n const stormBroken = this.#stormDetector.shouldBreak(\r\n this.#stormRecords,\r\n lastToolCalls,\r\n );\r\n if (stormBroken) {\r\n const stormMsg = \"\\n⚠️ 同一工具重复出错,已强制切换策略\\n\";\r\n yield { type: \"text_delta\", content: stormMsg };\r\n assistantMsg.toolCalls = undefined;\r\n assistantMsg.content += stormMsg;\r\n this.#stormRecords = [];\r\n toolRounds++;\r\n continue;\r\n }\r\n\r\n const results = await toolExecutor.executeBatch(lastToolCalls);\r\n this.#stormRecords = results.records;\r\n // 保存本轮结果,供下一轮循环开始时 Reflector 分析\r\n this.#lastRoundResults = results.items.map((it) => {\r\n const tool = this.#toolRegistry.get(it.name);\r\n return {\r\n name: it.name,\r\n result: it.result,\r\n kind: tool?.kind ?? ToolKind.Other,\r\n recentSameTool: results.records\r\n .filter((r) => r.name === it.name)\r\n .map((r) => ({ success: r.success, error: r.error })),\r\n };\r\n });\r\n\r\n for (const item of results.items) {\r\n // todo_* 工具执行后,附带当前 todo 列表快照,供 UI 渲染任务进度面板\r\n const todoSnapshot =\r\n item.name.startsWith(\"todo_\") && this.#todoList\r\n ? this.#todoList.items\r\n : undefined;\r\n yield {\r\n type: \"tool_result\",\r\n name: item.name,\r\n result: item.result,\r\n todoSnapshot,\r\n };\r\n this.#logger.logToolResult(\r\n item.name,\r\n item.callId,\r\n item.result.success,\r\n item.result.data,\r\n item.result.error,\r\n undefined,\r\n toolRounds,\r\n );\r\n let toolContent = item.result.data;\r\n if (item.result.diff && item.result.diff.patch)\r\n toolContent += `\\n\\n${item.result.diff.patch}`;\r\n this.#messages.push({\r\n role: \"tool\",\r\n content: toolContent,\r\n toolCallId: item.callId,\r\n name: item.name,\r\n });\r\n }\r\n\r\n // Harness 护栏:检测到模型在完全未拆 todo 的情况下直接调了非 todo 工具,\r\n // 主动发一条提示让模型重新评估(每会话只发一次,避免啰喌)\r\n this.#maybeEmitHarnessHint(results.items);\r\n\r\n toolRounds++;\r\n continue;\r\n }\r\n\r\n // 模型不再调工具,准备退出循环。Harness 模式下若 todo 全结束,\r\n // 提示模型自行做一次任务自检(重新读改动过的文件,确认修改正确)。\r\n if (\r\n this.#todoList &&\r\n this.#todoList.items.length > 0 &&\r\n this.#todoList.isAllTerminated()\r\n ) {\r\n this.#messages.push({\r\n role: \"user\",\r\n content:\r\n \"[系统] 全部 todo 已完成。请做一次任务自检:重新读取本次修改过的文件,\" +\r\n \"确认改动正确无误、类型兼容、没有遗漏。如有问题请新增 todo 修复;\" +\r\n \"确认无误后输出最终总结。\",\r\n });\r\n }\r\n\r\n break;\r\n }\r\n } catch (err: unknown) {\r\n if (err instanceof DOMException && err.name === \"AbortError\") return;\r\n if (err instanceof Error && err.name === \"AbortError\") return;\r\n this.#logger.logError(\r\n err instanceof Error ? err.message : String(err),\r\n err instanceof Error ? err.stack : undefined,\r\n );\r\n yield { type: \"error\", error: err instanceof Error ? err : new Error(String(err)) };\r\n return;\r\n }\r\n\r\n const elapsed = Date.now() - startTime;\r\n this.#logger.logTurnDone(elapsed, toolRounds);\r\n void this.#persist();\r\n // 持久化今日成本数据,确保进程退出后重开不会丢失\r\n await this.#costTracker.flush().catch(() => {});\r\n yield { type: \"done\", elapsed };\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 持久化与恢复\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * 中止当前会话:触发 AbortController,停止流式 LLM 和正在执行的工具。\r\n * 同时清掉节流持久化定时器。\r\n *\r\n * @sideEffect abort 当前 chat 循环、取消所有 #abortController 监听者\r\n */\r\n abort(): void {\r\n this.#abortController.abort();\r\n if (this.#persistTimer) {\r\n clearTimeout(this.#persistTimer);\r\n this.#persistTimer = null;\r\n }\r\n }\r\n\r\n /**\r\n * 刷新日志缓冲,确保所有已记录的事件落盘。\r\n * 通常在进程退出前调用,避免日志丢失。\r\n *\r\n * @sideEffect 写入 .dskcode/logs/ 下的日志文件\r\n */\r\n async flushLog(): Promise<void> {\r\n await this.#logger.flush();\r\n }\r\n\r\n /**\r\n * 清空消息历史、会话成本、风暴记录、checkpoints。\r\n * 注意:不会清掉磁盘上的会话记录(用 delete() 删除),仅重置内存状态。\r\n *\r\n * @sideEffect 抹掉 messages / costTracker / stormRecords / checkpoints\r\n */\r\n reset(): void {\r\n this.#messages.length = 0;\r\n this.#costTracker.resetSession();\r\n this.#stormRecords = [];\r\n this.#checkpoints.clear();\r\n this.#lastRoundResults = null;\r\n }\r\n\r\n /**\r\n * 立即把当前状态写入持久化(不等 500ms debounce)。\r\n * 用于 UI 上\"立即保存\"按钮或异常退出前的最后兜底。\r\n *\r\n * @sideEffect 调用 #doPersist(),写入磁盘\r\n */\r\n async persistNow(): Promise<void> {\r\n if (this.#persistTimer) {\r\n clearTimeout(this.#persistTimer);\r\n this.#persistTimer = null;\r\n }\r\n await this.#doPersist();\r\n }\r\n\r\n /**\r\n * 节流持久化:500ms 内多次调用只会真正落盘一次。\r\n * 通过 setTimeout + timer.refresh() 实现 debounce。\r\n *\r\n * @sideEffect 调度一个 500ms 后的 #doPersist() 任务\r\n */\r\n #persist(): void {\r\n if (!this.#store) return;\r\n if (this.#persistTimer) {\r\n this.#persistTimer.refresh();\r\n return;\r\n }\r\n this.#persistTimer = setTimeout(() => {\r\n this.#persistTimer = null;\r\n void this.#doPersist();\r\n }, 500);\r\n this.#persistTimer.unref();\r\n }\r\n\r\n /**\r\n * 真正执行持久化:构造 StoredSession 并写入 store。\r\n * 失败仅 console.error,不抛给调用方(持久化失败不应阻塞对话)。\r\n *\r\n * @sideEffect 写磁盘 ~/.dskcode/sessions/<id>.json\r\n */\r\n async #doPersist(): Promise<void> {\r\n if (!this.#store) return;\r\n const stored: StoredSession = {\r\n id: this.#sessionId,\r\n title: this.#deriveTitle(),\r\n createdAt: this.#createdAt,\r\n updatedAt: Date.now(),\r\n cwd: this.#options.cwd,\r\n model: this.#provider.model(),\r\n messages: this.#serializeMessages(),\r\n totalCost: this.#costTracker.sessionTotalCost,\r\n };\r\n try {\r\n await this.#store.save(stored);\r\n } catch (err) {\r\n console.error(\"[Session] 持久化失败:\", err);\r\n }\r\n }\r\n\r\n /**\r\n * 从消息历史推导会话标题:取第一条非空 user 消息的前 40 字。\r\n * 无任何 user 消息时返回 \"新会话\"。\r\n *\r\n * @pure 不修改任何状态\r\n */\r\n #deriveTitle(): string {\r\n for (const m of this.#messages) {\r\n if (m.role === \"user\" && m.content.trim()) return m.content.trim().slice(0, 40);\r\n }\r\n return \"新会话\";\r\n }\r\n\r\n /**\r\n * 把内存 messages 转成可持久化的 StoredSession[\"messages\"]。\r\n * 给 user 消息挂上对应 checkpoint(若存在),用于 rewind 时文件回退。\r\n *\r\n * @pure 不修改任何状态\r\n */\r\n #serializeMessages(): StoredSession[\"messages\"] {\r\n return this.#messages.map((msg, idx) => {\r\n const checkpoint = this.#checkpoints.get(idx);\r\n if (msg.role === \"user\" && checkpoint) return { ...msg, checkpoint };\r\n return { ...msg };\r\n });\r\n }\r\n\r\n /**\r\n * 静态方法:从磁盘恢复一个之前保存的 Session。\r\n *\r\n * @param id — 之前 SessionStore 保存的会话 ID\r\n * @param provider — LLM Provider(必须)\r\n * @param tools — 工具列表/Registry(必须重新提供,因为工具不持久化)\r\n * @param costTracker — 成本追踪器(可选)\r\n * @param options — 会话选项(sessionId / store 必须与持久化 ID 对应)\r\n * @returns 恢复后的 Session 实例\r\n * @throws 持久化被禁用时、ID 不存在时抛错\r\n *\r\n * @sideEffect 从磁盘读 messages + checkpoints\r\n */\r\n static async resume(\r\n id: string,\r\n provider: Provider,\r\n tools: AnyAgentTool[] | ToolRegistry = [],\r\n costTracker?: CostTracker,\r\n options?: SessionOptions,\r\n ): Promise<Session> {\r\n const store =\r\n options?.store === false ? null : (options?.store ?? new SessionStore());\r\n if (!store) throw new Error(\"resume 需要启用持久化(options.store 不能为 false)\");\r\n const stored = await store.load(id);\r\n if (!stored) throw new Error(`会话 ${id} 不存在`);\r\n\r\n const session = new Session(provider, tools, costTracker, {\r\n ...options,\r\n sessionId: id,\r\n store,\r\n });\r\n for (const m of stored.messages) {\r\n session.#messages.push({\r\n role: m.role,\r\n content: m.content,\r\n toolCallId: m.toolCallId,\r\n name: m.name,\r\n toolCalls: m.toolCalls,\r\n });\r\n }\r\n for (let i = 0; i < stored.messages.length; i++) {\r\n const cp = stored.messages[i]?.checkpoint;\r\n if (cp) session.#checkpoints.set(i, cp);\r\n }\r\n session.#createdAt = stored.createdAt;\r\n return session;\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 检查点与 Rewind\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * 列出所有 user 消息对应的 checkpoint(按 index 升序)。\r\n * 用于 UI 展示 /rewind 列表。\r\n *\r\n * @returns 每个元素的 index = messages 数组索引,可直接传给 rewind()\r\n * @pure 不修改任何状态\r\n */\r\n listCheckpoints(): MessageCheckpointInfo[] {\r\n const result: MessageCheckpointInfo[] = [];\r\n for (const [index, checkpoint] of this.#checkpoints) {\r\n const msg = this.#messages[index];\r\n if (!msg || msg.role !== \"user\") continue;\r\n result.push({\r\n index,\r\n preview: msg.content.slice(0, 80),\r\n timestamp: checkpoint.timestamp,\r\n isGitRepo: checkpoint.isGitRepo,\r\n });\r\n }\r\n return result.sort((a, b) => a.index - b.index);\r\n }\r\n\r\n /**\r\n * rewind — 把消息历史截断到 targetIndex 对应的 user 消息,\r\n * 并尝试把文件工作区也回退到那一刻。\r\n *\r\n * @param targetIndex — 要回退到的 user 消息索引(来自 listCheckpoints())\r\n * @returns\r\n * - `{ ok: true, fileRestored }`:成功;fileRestored 表示工作区是否也被还原\r\n * - `{ ok: false, error }`:失败(无效索引 / 非 user / 无 checkpoint / 文件恢复失败)\r\n *\r\n * 行为:\r\n * 1. 截断 messages 数组到 targetIndex + 1\r\n * 2. 移除并丢弃所有 > targetIndex 的 checkpoints\r\n * 3. 若目标 checkpoint 是 git 仓库:\r\n * - 有 stashSha:恢复该 stash(force)\r\n * - 无 stashSha:把工作区 restore 到 HEAD 干净状态\r\n * 4. 触发 #persist() 把截断后的状态写盘\r\n *\r\n * @sideEffect 改写 messages、checkpoints、磁盘\r\n */\r\n async rewind(targetIndex: number): Promise<RewindResult> {\r\n if (targetIndex < 0 || targetIndex >= this.#messages.length) {\r\n return { ok: false, error: `无效的消息索引 ${targetIndex}` };\r\n }\r\n const target = this.#messages[targetIndex];\r\n if (!target || target.role !== \"user\") {\r\n return { ok: false, error: `索引 ${targetIndex} 不是 user 消息` };\r\n }\r\n const checkpoint = this.#checkpoints.get(targetIndex);\r\n if (!checkpoint) return { ok: false, error: \"该消息没有检查点\" };\r\n\r\n this.#messages.length = targetIndex + 1;\r\n const toDiscard: Checkpoint[] = [];\r\n for (const [idx, cp] of this.#checkpoints) {\r\n if (idx > targetIndex) {\r\n toDiscard.push(cp);\r\n this.#checkpoints.delete(idx);\r\n }\r\n }\r\n\r\n let fileRestored = false;\r\n if (checkpoint.isGitRepo) {\r\n try {\r\n if (checkpoint.stashSha) {\r\n // 目标检查点有 stash 快照——恢复该快照\r\n await restoreCheckpointForce(checkpoint);\r\n } else {\r\n // 目标检查点 stashSha 为空,代表「那一刻工作区就是 HEAD 干净状态」。\r\n // 但后续对话产生的修改可能还积累在工作区,需要丢弃才能真正回退到那一刻。\r\n await restoreToClean(this.#options.cwd);\r\n }\r\n fileRestored = true;\r\n } catch (err) {\r\n const msg = err instanceof Error ? err.message : String(err);\r\n return { ok: false, error: `对话已截断但文件恢复失败:${msg}` };\r\n }\r\n }\r\n\r\n // restoreCheckpointForce 内部已 drop 了该 checkpoints 的 stash entry,\r\n // 所以只需从 Map 中移除即可,无需再 discardCheckpoint。\r\n this.#checkpoints.delete(targetIndex);\r\n for (const cp of toDiscard) {\r\n void discardCheckpoint(cp);\r\n }\r\n this.#persist();\r\n return { ok: true, fileRestored };\r\n }\r\n\r\n /**\r\n * 是否存在可用的 checkpoint(决定 UI 是否显示 /rewind 入口)。\r\n *\r\n * @pure 不修改任何状态\r\n */\r\n hasCheckpoints(): boolean {\r\n return this.listCheckpoints().length > 0;\r\n }\r\n\r\n /**\r\n * 彻底删除会话:从磁盘移除 SessionStore 条目、丢弃所有 checkpoint、关闭日志。\r\n * 删除后该 Session 实例不应再被使用。\r\n *\r\n * @sideEffect 删磁盘文件、清 checkpoints、flush 并关闭 logger\r\n */\r\n async delete(): Promise<void> {\r\n if (this.#store) await this.#store.delete(this.#sessionId);\r\n for (const cp of this.#checkpoints.values()) {\r\n void discardCheckpoint(cp);\r\n }\r\n this.#checkpoints.clear();\r\n this.#logger.logSessionEnd(Date.now() - this.#createdAt);\r\n await this.#logger.flush();\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 内部方法\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * 构建本轮使用的工具执行器(每次 chat() 调用一次,绑定当前 signal)。\r\n * 每次新建确保 abort signal 是当下有效的,且 #baseCtx 不会被多次调用共享篡改。\r\n *\r\n * @returns 新的 ToolExecutor 实例\r\n * @pure 仅组装参数,不修改任何状态\r\n */\r\n #buildToolExecutor(): ToolExecutor {\r\n return new ToolExecutor({\r\n registry: this.#toolRegistry,\r\n gate: this.#options.gate,\r\n baseCtx: {\r\n cwd: this.#options.cwd,\r\n signal: this.#abortController.signal,\r\n writeRoots: this.#options.writeRoots,\r\n },\r\n });\r\n }\r\n\r\n /**\r\n * 构建 system prompt:注入当前模型、cwd、工具清单、项目上下文。\r\n * plan 模式使用 buildPlanSystemPrompt(只读工具 + 计划输出格式),\r\n * code 模式使用 buildSystemPrompt。\r\n *\r\n * @returns 渲染好的 prompt 字符串\r\n * @pure 不修改任何状态\r\n */\r\n #buildSystemPrompt(): string {\r\n const enabledTools = this.#toolRegistry.list();\r\n const toolDescs = enabledTools.map((t) => ({\r\n name: t.name,\r\n description: t.description,\r\n parameters: t.parameters as unknown as Record<string, unknown>,\r\n }));\r\n const opts: SystemPromptOptions = {\r\n model: this.#provider.model(),\r\n maxToolRounds: this.#options.maxToolRounds,\r\n tools: toolDescs.length > 0 ? toolDescs : undefined,\r\n projectContext: this.#options.projectContext ?? undefined,\r\n cwd: this.#options.cwd,\r\n };\r\n let base: string;\r\n if (this.#mode === \"plan\") base = buildPlanSystemPrompt(opts);\r\n else base = buildSystemPrompt(opts);\r\n // Harness 模式下拼接 TodoList 进度到 prompt 末尾,让模型看到自己进度\r\n if (this.#todoList && this.#todoList.items.length > 0) {\r\n base = base + \"\\n\\n\" + this.#todoList.toMarkdown();\r\n }\r\n return base;\r\n }\r\n\r\n /**\r\n * Harness 护栏:模型跳过 todo_add 直接动手时,主动发一条提示让其重评。\r\n *\r\n * 触发条件(全部需满足):\r\n * 1. Harness 开启(#todoList 非空)\r\n * 2. todoList 仍为空(未拆 todo)\r\n * 3. 本会话内未提示过(#harnessHintEmitted 为 false)\r\n * 4. 本轮调了 ≥1 个**非 todo_* 的工具**(说明模型跳过了 todo_add 直接干活)\r\n *\r\n * 动作:push 一条 user role 消息到 #messages,让下一轮 LLM 看到;设 #harnessHintEmitted = true。\r\n *\r\n * @sideEffect 写 #messages / #harnessHintEmitted\r\n */\r\n #maybeEmitHarnessHint(items: ReadonlyArray<{ name: string }>): void {\r\n if (!this.#todoList) return;\r\n if (this.#todoList.items.length > 0) return;\r\n if (this.#harnessHintEmitted) return;\r\n const hasNonTodo = items.some((it) => !it.name.startsWith(\"todo_\"));\r\n if (!hasNonTodo) return;\r\n this.#harnessHintEmitted = true;\r\n this.#messages.push({\r\n role: \"user\",\r\n content:\r\n \"[系统提示] 你还没有 todo 过,但已经调了具体工具。\\n\" +\r\n \"请回顾你刚做的事:这是「简单」任务(1 轮 1 工具能完成)还是「复杂」任务?\\n\" +\r\n \"如果是复杂(改了/创建了/要读后改/跨多种工具),请停下,先调 todo_add 拆解 3-7 步再继续。\\n\" +\r\n \"如果是简单,忽略本提示。\\n\" +\r\n \"(本提示本会话仅发一次,后续不再提醒。)\",\r\n });\r\n }\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 工具执行沙箱 — 路径约束、路径安全、超时控制、输出截断\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { resolve, relative, isAbsolute } from \"node:path\";\r\nimport { realpath } from \"node:fs/promises\";\r\nimport { spawn } from \"node:child_process\";\r\nimport process from \"node:process\";\r\n\r\n/** 沙箱默认配置 */\r\nconst DEFAULT_TIMEOUT_MS = 30_000;\r\nconst DEFAULT_MAX_OUTPUT_LENGTH = 50_000;\r\nconst DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB\r\n\r\n/** 是否为 Windows 平台 */\r\nconst isWindows = process.platform === \"win32\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 路径安全 — confine / resolveIn / realPath\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 将路径解析为绝对路径。\r\n * 相对路径基于 cwd 解析,绝对路径原样返回。\r\n */\r\nexport function resolvePath(inputPath: string, cwd: string): string {\r\n const resolved = isAbsolute(inputPath) ? inputPath : resolve(cwd, inputPath);\r\n return resolve(resolved);\r\n}\r\n\r\n/**\r\n * 获取路径的真实路径(解析符号链接)。\r\n * 如果路径不存在,则对其父目录逐级解析,尽可能获取真实路径。\r\n */\r\nasync function realPath(target: string): Promise<string> {\r\n try {\r\n return await realpath(target);\r\n } catch {\r\n // 路径可能还不存在(新建文件),尝试解析父目录\r\n const parent = resolve(target, \"..\");\r\n try {\r\n const realParent = await realpath(parent);\r\n return resolve(realParent, relative(parent, target));\r\n } catch {\r\n // 父目录也不存在,返回原路径\r\n return resolve(target);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 检查目标路径是否在允许的根目录范围内。\r\n *\r\n * @param allowedRoots 允许的根目录列表(绝对路径)\r\n * @param target 要检查的目标路径(绝对路径)\r\n * @returns 如果目标在范围内返回 true,否则返回 false 及错误信息\r\n */\r\nexport async function confine(\r\n allowedRoots: string[],\r\n target: string,\r\n): Promise<{ ok: true } | { ok: false; error: string }> {\r\n if (allowedRoots.length === 0) {\r\n return { ok: true }; // 没有限制,默认允许\r\n }\r\n\r\n const realTarget = await realPath(target);\r\n\r\n for (const root of allowedRoots) {\r\n const realRoot = await realPath(root);\r\n // 检查 target 是否在 root 之下\r\n const rel = relative(realRoot, realTarget);\r\n if (!rel.startsWith(\"..\") && rel !== \"\" && !rel.startsWith(\"/\") && !rel.startsWith(\"\\\\\")) {\r\n return { ok: true };\r\n }\r\n // 允许 target 恰好等于 root\r\n if (realTarget === realRoot) {\r\n return { ok: true };\r\n }\r\n }\r\n\r\n return {\r\n ok: false,\r\n error: `路径 \"${target}\" 不在允许的写入范围内 ${allowedRoots.join(\", \")}`,\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 输出截断\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 截断过长的输出内容。\r\n */\r\nexport function truncateOutput(content: string, maxLength = DEFAULT_MAX_OUTPUT_LENGTH): string {\r\n if (content.length <= maxLength) return content;\r\n const truncated = content.slice(0, maxLength);\r\n return `${truncated}\\n\\n... [输出过长,已截断,共 ${String(content.length)} 字符]`;\r\n}\r\n\r\n/**\r\n * 获取默认超时时间。\r\n */\r\nexport function getDefaultTimeout(): number {\r\n return DEFAULT_TIMEOUT_MS;\r\n}\r\n\r\n/**\r\n * 获取默认最大文件大小。\r\n */\r\nexport function getDefaultMaxFileSize(): number {\r\n return DEFAULT_MAX_FILE_SIZE;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 超时控制\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 创建一个带超时的 AbortController。\r\n */\r\nexport function createTimeoutSignal(signal?: AbortSignal, timeoutMs = DEFAULT_TIMEOUT_MS): AbortController {\r\n const controller = new AbortController();\r\n\r\n // 外部信号触发时,联动中止\r\n if (signal) {\r\n signal.addEventListener(\"abort\", () => {\r\n controller.abort();\r\n }, { once: true });\r\n }\r\n\r\n // 超时自动中止\r\n const timer = setTimeout(() => {\r\n controller.abort();\r\n }, timeoutMs);\r\n controller.signal.addEventListener(\"abort\", () => {\r\n clearTimeout(timer);\r\n }, { once: true });\r\n\r\n return controller;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Shell 命令执行\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 执行 shell 命令的通用封装。\r\n */\r\nexport async function execCommand(\r\n command: string,\r\n args: string[],\r\n cwd: string,\r\n timeoutMs = DEFAULT_TIMEOUT_MS,\r\n signal?: AbortSignal,\r\n /** 如果为 true,表示 command 已经是 shell 程序(如 cmd/sh),不需要再包装 */\r\n isShellCommand?: boolean,\r\n): Promise<{ stdout: string; stderr: string; exitCode: number | null }> {\r\n return new Promise((_resolve) => {\r\n let spawnCmd: string;\r\n let spawnArgs: string[];\r\n let useShell: boolean;\r\n\r\n if (isShellCommand) {\r\n spawnCmd = command;\r\n spawnArgs = args;\r\n useShell = false;\r\n\r\n // cmd.exe 默认用 GBK 输出中文错误,会在 UTF-8 终端里变成乱码。\r\n // 所以在 cmd 这条路径下,把命令带上 `chcp 65001 >nul &&` 前缀\r\n // 切到 UTF-8 代码页,stderr 才能被 LLM 读懂。\r\n // 启动期探测出的 SHELL_INFO 已不允许这种耗时切换,能在 git bash 赏i\r\n // 就不进 cmd 这条分支;这里作为“找不到 Git Bash 时的兒底”出现。\r\n //\r\n // 注意:chcp 切换只对 cmd.exe /c 调用里可见,不会污染外层进程。\r\n if (isWindows && spawnCmd === \"cmd\" && spawnArgs[0] === \"/c\") {\r\n const prefixed = `chcp 65001 >nul && ${spawnArgs[1] ?? \"\"}`;\r\n spawnArgs = [\"/c\", prefixed];\r\n }\r\n } else {\r\n useShell = !isWindows;\r\n spawnCmd = isWindows ? \"cmd\" : command;\r\n spawnArgs = isWindows\r\n ? [\"/c\", command, ...args]\r\n : args;\r\n }\r\n\r\n const child = spawn(spawnCmd, spawnArgs, {\r\n cwd,\r\n shell: useShell,\r\n env: { ...process.env },\r\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\r\n });\r\n\r\n let stdout = \"\";\r\n let stderr = \"\";\r\n\r\n child.stdout.on(\"data\", (data: Buffer) => {\r\n stdout += data.toString(\"utf8\");\r\n });\r\n\r\n child.stderr.on(\"data\", (data: Buffer) => {\r\n stderr += data.toString(\"utf8\");\r\n });\r\n\r\n const timeout = setTimeout(() => {\r\n child.kill(\"SIGTERM\");\r\n // 给 5 秒优雅退出时间\r\n setTimeout(() => {\r\n child.kill(\"SIGKILL\");\r\n }, 5000);\r\n }, timeoutMs);\r\n\r\n // 外部中止信号\r\n if (signal) {\r\n if (signal.aborted) {\r\n child.kill(\"SIGTERM\");\r\n }\r\n signal.addEventListener(\"abort\", () => {\r\n child.kill(\"SIGTERM\");\r\n }, { once: true });\r\n }\r\n\r\n child.on(\"close\", (code) => {\r\n clearTimeout(timeout);\r\n _resolve({ stdout, stderr, exitCode: code });\r\n });\r\n\r\n child.on(\"error\", (err) => {\r\n clearTimeout(timeout);\r\n stderr += `\\n进程启动失败:${err.message}`;\r\n _resolve({ stdout, stderr, exitCode: null });\r\n });\r\n });\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 文件差异计算 — Myers O(ND) 算法 + unified patch 生成\r\n//\r\n// Myers 算法与 Git 的 diff 引擎同款,产生最短编辑脚本(SES)。\r\n// 时间复杂度 O((N+M)D),空间复杂度 O(N+M),其中 D 为编辑距离。\r\n//\r\n// 实现方式:非递归的 Myers 差分算法。\r\n// 1. 正向搜索:找到从 (0,0) 到 (N,M) 的最短编辑路径\r\n// 2. 回溯路径:从 (N,M) 回溯到 (0,0),生成 DiffLine 序列\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type { FileDiff } from \"./types.js\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 内部类型\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 单行 diff 操作类型 */\r\ntype Op = \"equal\" | \"add\" | \"remove\";\r\n\r\n/** 一行 diff 记录 */\r\ninterface DiffLine {\r\n op: Op;\r\n line: string;\r\n}\r\n\r\n/** unified diff 中的一个 hunk */\r\ninterface DiffHunk {\r\n oldStart: number;\r\n oldCount: number;\r\n newStart: number;\r\n newCount: number;\r\n lines: DiffLine[];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// EOL(行尾风格)检测\r\n// ---------------------------------------------------------------------------\r\n\r\ntype EOFStyle = \"lf\" | \"crlf\" | \"no-eol\";\r\n\r\ninterface SplitResult {\r\n lines: string[];\r\n eol: string;\r\n eofStyle: EOFStyle;\r\n}\r\n\r\nfunction splitLines(text: string): SplitResult {\r\n if (text.length === 0) {\r\n return { lines: [], eol: \"\\n\", eofStyle: \"no-eol\" };\r\n }\r\n\r\n const crlfIdx = text.indexOf(\"\\r\\n\");\r\n const lfIdx = text.indexOf(\"\\n\");\r\n let eol: string;\r\n if (crlfIdx !== -1 && (lfIdx === -1 || crlfIdx <= lfIdx)) {\r\n eol = \"\\r\\n\";\r\n } else if (lfIdx !== -1) {\r\n eol = \"\\n\";\r\n } else {\r\n eol = \"\\n\";\r\n }\r\n\r\n const lines = text.split(eol);\r\n\r\n let eofStyle: EOFStyle;\r\n if (text.endsWith(eol)) {\r\n if (lines.length > 0 && lines[lines.length - 1] === \"\") {\r\n lines.pop();\r\n }\r\n eofStyle = \"lf\";\r\n } else {\r\n eofStyle = \"no-eol\";\r\n }\r\n\r\n return { lines, eol, eofStyle };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Myers 非递归差分算法\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 计算两个行数组之间的最短编辑脚本(SES)。\r\n *\r\n * 使用非递归 Myers 算法:\r\n * 1. 正向搜索 V 数组,记录每步的进度\r\n * 2. 到达终点后回溯路径生成 DiffLine 列表\r\n */\r\nfunction computeLineDiff(oldLines: string[], newLines: string[]): DiffLine[] {\r\n const N = oldLines.length;\r\n const M = newLines.length;\r\n\r\n if (N === 0 && M === 0) return [];\r\n if (N === 0) return newLines.map((line) => ({ op: \"add\" as Op, line }));\r\n if (M === 0) return oldLines.map((line) => ({ op: \"remove\" as Op, line }));\r\n\r\n // 存储每一步的 V 快照,用于回溯\r\n const trace: Map<number, number>[] = [];\r\n\r\n const maxD = N + M;\r\n // V[k] = x position;初始化为 -1 表示未访问\r\n // eslint-disable-next-line unicorn/no-new-array, @typescript-eslint/no-unsafe-type-assertion\r\n const V: number[] = new Array(2 * maxD + 2).fill(-1) as number[];\r\n // k 的偏移量,使索引非负\r\n const offset = maxD;\r\n\r\n V[offset + 1] = 0;\r\n\r\n // 正向搜索:从 d=0 开始,逐层扩展\r\n for (let d = 0; d <= maxD; d++) {\r\n // 保存当前层的 V 快照\r\n const snapshot = new Map<number, number>();\r\n\r\n const kStart = -d;\r\n const kEnd = d;\r\n\r\n for (let k = kStart; k <= kEnd; k += 2) {\r\n const ki = k + offset;\r\n let x: number;\r\n\r\n if (k === -d || (k !== d && V[ki - 1]! < V[ki + 1]!)) {\r\n x = V[ki + 1]!;\r\n } else {\r\n x = V[ki - 1]! + 1;\r\n }\r\n\r\n let y = x - k;\r\n\r\n // 沿着对角线延伸\r\n while (x < N && y < M && oldLines[x] === newLines[y]) {\r\n x++;\r\n y++;\r\n }\r\n\r\n V[ki] = x;\r\n snapshot.set(k, x);\r\n\r\n // 到达终点\r\n if (x >= N && y >= M) {\r\n trace.push(snapshot);\r\n // 回溯路径\r\n return backtrack(trace, N, M, oldLines, newLines, offset);\r\n }\r\n }\r\n\r\n trace.push(snapshot);\r\n }\r\n\r\n // 不应该到达这里(总有解)\r\n return oldLines.map((line) => ({ op: \"remove\" as Op, line }))\r\n .concat(newLines.map((line) => ({ op: \"add\" as Op, line })));\r\n}\r\n\r\n/**\r\n * 从 trace 中回溯最短编辑路径,生成 DiffLine 列表。\r\n *\r\n * trace 存储了每一层 (d) 的 V 快照。\r\n * 从终点 (N, M) 开始,逆着路径逐层回溯到 (0, 0)。\r\n */\r\nfunction backtrack(\r\n trace: Map<number, number>[],\r\n N: number,\r\n M: number,\r\n oldLines: string[],\r\n newLines: string[],\r\n _offset: number,\r\n): DiffLine[] {\r\n const result: DiffLine[] = [];\r\n\r\n let x = N;\r\n let y = M;\r\n\r\n // 从最后一层回溯到第 1 层(第 0 层只有初始蛇行,没有实际移动)\r\n for (let d = trace.length - 1; d >= 1; d--) {\r\n const k = x - y;\r\n\r\n // 从 trace[d-1] 中查找上一步的 x 位置\r\n // 注意:trace[d] 存的是本层结果,前一层的数据在 trace[d-1] 中\r\n const prevSnapshot = trace[d - 1]!;\r\n const prevKDown = prevSnapshot.get(k - 1);\r\n const prevKUp = prevSnapshot.get(k + 1);\r\n\r\n let prevK: number;\r\n if (k === -d) {\r\n // 只能在 k+1 方向\r\n prevK = k + 1;\r\n } else if (k === d) {\r\n // 只能在 k-1 方向\r\n prevK = k - 1;\r\n } else if ((prevKDown ?? -1) < (prevKUp ?? -1)) {\r\n // k+1 方向的 V 值更大,从那里过来(删除)\r\n prevK = k + 1;\r\n } else {\r\n // k-1 方向的 V 值更大,从那里过来(添加)\r\n prevK = k - 1;\r\n }\r\n\r\n const prevX = prevSnapshot.get(prevK) ?? 0;\r\n const prevY = prevX - prevK;\r\n\r\n // ===== 从 (prevX, prevY) 到 (x, y) 的回溯路径 =====\r\n\r\n if (prevK === k - 1) {\r\n // prevK = k-1 → V[k-1] 被使用 → x = V[k-1] + 1 → 垂直移动(删除)\r\n // 路径: (prevX, prevY) → (prevX+1, prevY) [删除] → 蛇行 [(prevX+2, prevY+1), ...]\r\n // 回溯: 先走蛇行(相等行), 再走删除行\r\n\r\n // 第一步:蛇行(相等行),从 (x,y) 回到 (prevX+1, prevY)\r\n while (x > prevX + 1 && y > prevY) {\r\n x--;\r\n y--;\r\n result.unshift({ op: \"equal\", line: oldLines[x]! });\r\n }\r\n\r\n // 第二步:删除行\r\n if (x > prevX) {\r\n x--;\r\n result.unshift({ op: \"remove\", line: oldLines[x]! });\r\n }\r\n } else {\r\n // prevK = k+1 → V[k+1] 被使用 → x = V[k+1] → 水平移动(添加)\r\n // 路径: (prevX, prevY) → (prevX, prevY+1) [添加] → 蛇行 [(prevX+1, prevY+2), ...]\r\n // 回溯: 先走蛇行(相等行), 再走添加行\r\n\r\n // 第一步:蛇行(相等行),从 (x,y) 回到 (prevX, prevY+1)\r\n while (x > prevX && y > prevY + 1) {\r\n x--;\r\n y--;\r\n result.unshift({ op: \"equal\", line: oldLines[x]! });\r\n }\r\n\r\n // 第二步:添加行\r\n if (y > prevY) {\r\n y--;\r\n result.unshift({ op: \"add\", line: newLines[y]! });\r\n }\r\n }\r\n }\r\n\r\n // 处理初始蛇行:从 (0, 0) 到当前 (x, y) 的相等行\r\n // 逆向(从 (x, y) 到 (1, 1) 到 (0, 0) 回溯)\r\n while (x > 0 && y > 0) {\r\n x--;\r\n y--;\r\n result.unshift({ op: \"equal\", line: oldLines[x]! });\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Hunk 分组\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 上下文行数(unified diff 标准 3 行) */\r\nconst CONTEXT_LINES = 3;\r\n\r\n/**\r\n * 将 diff 行列表分组为 hunks。\r\n */\r\nfunction groupIntoHunks(diffLines: DiffLine[]): DiffHunk[] {\r\n if (diffLines.length === 0) return [];\r\n\r\n // 找出所有变更行的索引\r\n const changeIndices: number[] = [];\r\n for (let i = 0; i < diffLines.length; i++) {\r\n const line = diffLines[i]!;\r\n if (line.op !== \"equal\") {\r\n changeIndices.push(i);\r\n }\r\n }\r\n\r\n if (changeIndices.length === 0) return [];\r\n\r\n // 将相近的变更行分组\r\n const groups: number[][] = [[changeIndices[0]!]];\r\n\r\n for (let idx = 1; idx < changeIndices.length; idx++) {\r\n const lastGroup = groups[groups.length - 1]!;\r\n const prevIdx = lastGroup[lastGroup.length - 1]!;\r\n const currIdx = changeIndices[idx]!;\r\n\r\n let gap = 0;\r\n for (let k = prevIdx + 1; k < currIdx; k++) {\r\n const line = diffLines[k]!;\r\n if (line.op === \"equal\") gap++;\r\n }\r\n\r\n if (gap <= 2 * CONTEXT_LINES) {\r\n lastGroup.push(currIdx);\r\n } else {\r\n groups.push([currIdx]);\r\n }\r\n }\r\n\r\n // 为每个分组生成 hunk\r\n const hunks: DiffHunk[] = [];\r\n\r\n for (const group of groups) {\r\n const firstChangeIdx = group[0]!;\r\n const lastChangeIdx = group[group.length - 1]!;\r\n\r\n const startIdx = Math.max(0, firstChangeIdx - CONTEXT_LINES);\r\n const endIdx = Math.min(diffLines.length - 1, lastChangeIdx + CONTEXT_LINES);\r\n\r\n const lines = diffLines.slice(startIdx, endIdx + 1);\r\n\r\n let oldLine = 0;\r\n let newLine = 0;\r\n for (let i = 0; i < startIdx; i++) {\r\n const line = diffLines[i]!;\r\n if (line.op === \"equal\" || line.op === \"remove\") oldLine++;\r\n if (line.op === \"equal\" || line.op === \"add\") newLine++;\r\n }\r\n\r\n let oldCount = 0;\r\n let newCount = 0;\r\n for (const diffLine of lines) {\r\n if (diffLine.op === \"equal\" || diffLine.op === \"remove\") oldCount++;\r\n if (diffLine.op === \"equal\" || diffLine.op === \"add\") newCount++;\r\n }\r\n\r\n hunks.push({\r\n oldStart: oldLine + 1,\r\n oldCount,\r\n newStart: newLine + 1,\r\n newCount,\r\n lines,\r\n });\r\n }\r\n\r\n return hunks;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Unified diff 格式化\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction formatUnifiedDiff(hunks: DiffHunk[], filePath: string): string {\r\n if (hunks.length === 0) return \"\";\r\n\r\n const parts: string[] = [];\r\n const fileName = extractFileName(filePath);\r\n\r\n parts.push(`--- a/${fileName}`);\r\n parts.push(`+++ b/${fileName}`);\r\n\r\n for (const hunk of hunks) {\r\n parts.push(\r\n `@@ -${String(hunk.oldStart)},${String(hunk.oldCount)} +${String(hunk.newStart)},${String(hunk.newCount)} @@`,\r\n );\r\n\r\n for (const diffLine of hunk.lines) {\r\n switch (diffLine.op) {\r\n case \"equal\":\r\n parts.push(` ${diffLine.line}`);\r\n break;\r\n case \"remove\":\r\n parts.push(`-${diffLine.line}`);\r\n break;\r\n case \"add\":\r\n parts.push(`+${diffLine.line}`);\r\n break;\r\n }\r\n }\r\n }\r\n\r\n return parts.join(\"\\n\");\r\n}\r\n\r\nfunction formatNewFileDiff(lines: string[], filePath: string): string {\r\n const fileName = extractFileName(filePath);\r\n const parts: string[] = [];\r\n\r\n parts.push(`--- /dev/null`);\r\n parts.push(`+++ b/${fileName}`);\r\n parts.push(`@@ -0,0 +1,${String(lines.length)} @@`);\r\n\r\n for (const line of lines) {\r\n parts.push(`+${line}`);\r\n }\r\n\r\n return parts.join(\"\\n\");\r\n}\r\n\r\nfunction formatDeletedFileDiff(lines: string[], filePath: string): string {\r\n const fileName = extractFileName(filePath);\r\n const parts: string[] = [];\r\n\r\n parts.push(`--- a/${fileName}`);\r\n parts.push(`+++ /dev/null`);\r\n parts.push(`@@ -1,${String(lines.length)} +0,0 @@`);\r\n\r\n for (const line of lines) {\r\n parts.push(`-${line}`);\r\n }\r\n\r\n return parts.join(\"\\n\");\r\n}\r\n\r\nfunction extractFileName(filePath: string): string {\r\n const normalized = filePath.replace(/\\\\/g, \"/\");\r\n return normalized.split(\"/\").pop() ?? filePath;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 公共 API\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 计算两个文本之间的 unified diff,返回 FileDiff 对象。\r\n *\r\n * 使用 Myers O(ND) 算法产生最短编辑脚本,与 Git 的 diff 引擎同款。\r\n * 自动检测并保留原始文件的行尾风格(LF / CRLF)。\r\n */\r\nexport function computeFileDiff(\r\n oldContent: string,\r\n newContent: string,\r\n filePath: string,\r\n): FileDiff {\r\n if (oldContent === newContent) {\r\n return {\r\n filePath,\r\n patch: \"\",\r\n existedBefore: oldContent.length > 0,\r\n additions: 0,\r\n deletions: 0,\r\n };\r\n }\r\n\r\n if (oldContent.length === 0) {\r\n const split = splitLines(newContent);\r\n const patch = formatNewFileDiff(split.lines, filePath);\r\n return {\r\n filePath,\r\n patch,\r\n existedBefore: false,\r\n additions: split.lines.length,\r\n deletions: 0,\r\n };\r\n }\r\n\r\n if (newContent.length === 0) {\r\n const split = splitLines(oldContent);\r\n const patch = formatDeletedFileDiff(split.lines, filePath);\r\n return {\r\n filePath,\r\n patch,\r\n existedBefore: true,\r\n additions: 0,\r\n deletions: split.lines.length,\r\n };\r\n }\r\n\r\n const oldSplit = splitLines(oldContent);\r\n const newSplit = splitLines(newContent);\r\n\r\n const diffLines = computeLineDiff(oldSplit.lines, newSplit.lines);\r\n\r\n let additions = 0;\r\n let deletions = 0;\r\n for (const line of diffLines) {\r\n if (line.op === \"add\") additions++;\r\n if (line.op === \"remove\") deletions++;\r\n }\r\n\r\n const hunks = groupIntoHunks(diffLines);\r\n const patch = formatUnifiedDiff(hunks, filePath);\r\n\r\n return {\r\n filePath,\r\n patch,\r\n existedBefore: true,\r\n additions,\r\n deletions,\r\n };\r\n}\r\n\r\n/**\r\n * 将文件变更应用于原内容,返回新的文件内容。\r\n */\r\nexport function applyChange(\r\n kind: \"edit\" | \"create\" | \"delete\",\r\n oldContent: string,\r\n newContent: string,\r\n): string {\r\n if (kind === \"delete\") return \"\";\r\n if (kind === \"create\") return newContent;\r\n return newContent;\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 行尾(EOL)风格检测与保留 — 写工具统一使用此模块\r\n// ---------------------------------------------------------------------------\r\n//\r\n// 解决问题:原文件为 CRLF(Windows 项目常态)时,写工具若直接 writeFile\r\n// 新内容(往往以 LF 编码),会导致整文件 EOL 翻转,git diff 全文件飘红,\r\n// AI 看到大量\"假改动\"后陷入修复循环。此模块提供公共的 EOL 检测与落盘封装。\r\n//\r\n// 设计要点:\r\n// - 检测策略与 diff.ts 的 splitLines 保持一致:首个 CRLF 早于首个 LF → CRLF\r\n// - 对原内容做 EOL 探测,按探测结果统一新内容的行尾\r\n// - 保留原文件是否以行尾符结尾(trailing newline)的特征\r\n\r\nimport { writeFile } from \"node:fs/promises\";\r\n\r\n/**\r\n * 检测文本的行尾风格。\r\n *\r\n * 与 diff.ts 的 splitLines 探测策略一致:\r\n * - 找到最早出现的换行符位置;若最早的是 \\r\\n 则判为 CRLF,否则 LF\r\n *\r\n * @returns 行尾字符串:\"\\r\\n\" 或 \"\\n\";空文本默认 \"\\n\"\r\n */\r\nexport function detectEol(text: string): string {\r\n if (text.length === 0) return \"\\n\";\r\n\r\n const crlfIdx = text.indexOf(\"\\r\\n\");\r\n const lfIdx = text.indexOf(\"\\n\");\r\n\r\n if (crlfIdx !== -1 && (lfIdx === -1 || crlfIdx <= lfIdx)) {\r\n return \"\\r\\n\";\r\n }\r\n return \"\\n\";\r\n}\r\n\r\n/**\r\n * 检测文本是否以行尾符结尾(trailing newline)。\r\n */\r\nexport function hasTrailingNewline(text: string): boolean {\r\n return text.endsWith(\"\\n\") || text.endsWith(\"\\r\\n\");\r\n}\r\n\r\n/**\r\n * 按给定 EOL 规整文本,并保留与原内容一致的\"是否以行尾结尾\"特征。\r\n *\r\n * 规整步骤:\r\n * 1. 先统一拆成行片段(兼容现有 \\r\\n 与 \\n 混合)\r\n * 2. 用目标 EOL 重新拼接\r\n * 3. 若原内容以行尾结尾但规整后没有,补一个;反之不补\r\n *\r\n * 用于写入前对 newContent 做归一化,保证不在 EOL 上产生噪声 diff。\r\n *\r\n * @param originalContent 原文件内容(用于探测 EOL 风格与尾行尾特征)\r\n * @param newContent 待写入的新内容\r\n * @returns 应实际落盘的内容\r\n */\r\nexport function normalizeEol(originalContent: string, newContent: string): string {\r\n const targetEol = detectEol(originalContent);\r\n\r\n // 拆行:兼容混合 \\r\\n / \\n;先把 \\r\\n 统一成 \\n 再按 \\n 拆\r\n const lines = newContent.replace(/\\r\\n/g, \"\\n\").split(\"\\n\");\r\n\r\n // 判断原内容是否以行尾符结尾\r\n const originalHasTrailing = hasTrailingNewline(originalContent);\r\n\r\n // newContent 经 replace+split 后,行尾结尾会在末尾多一个空行元素\r\n // 如 \"a\\nb\\n\" → [\"a\",\"b\",\"\"],\"a\\nb\" → [\"a\",\"b\"]\r\n const newHasTrailing = lines.length > 0 && lines[lines.length - 1] === \"\";\r\n\r\n // 若新内容原本就带尾行尾,split 出的末尾空字符串保留即可\r\n // 若新内容不带尾行尾,但原内容带,则补一个\r\n let result = lines.join(targetEol);\r\n if (originalHasTrailing && !newHasTrailing) {\r\n result += targetEol;\r\n }\r\n // 若新内容带尾行尾但原内容不带:去掉末尾 EOL\r\n if (!originalHasTrailing && newHasTrailing) {\r\n // result 此时末尾恰有一个 targetEol,截悼\r\n if (result.endsWith(targetEol)) {\r\n result = result.slice(0, -targetEol.length);\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * 写文件并保留原文件的 EOL 风格与尾行尾特征。\r\n *\r\n * - 原内容存在时:按原 EOL 归一化新内容后写入\r\n * - 原内容不存在(新建文件):LF + 保留新内容自带的尾行尾特征\r\n *\r\n * 所有写工具(write_file / edit_file / multi_edit / delete_range)应统一走此函数,\r\n * 避免 EOL 翻转造成的全文件 diff 风暴。\r\n *\r\n * @param filePath 目标文件绝对路径\r\n * @param originalContent 原文件内容;文件不存在时传空字符串\r\n * @param newContent 新内容\r\n */\r\nexport async function writeFileWithEol(\r\n filePath: string,\r\n originalContent: string,\r\n newContent: string,\r\n): Promise<void> {\r\n const content =\r\n originalContent.length > 0\r\n ? normalizeEol(originalContent, newContent)\r\n : newContent;\r\n await writeFile(filePath, content, \"utf-8\");\r\n}","// ---------------------------------------------------------------------------\r\n// read_file 工具 — 读取指定路径的文件内容\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readFile, stat } from \"node:fs/promises\";\r\nimport { open } from \"node:fs/promises\";\r\nimport { relative } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { resolvePath, truncateOutput } from \"../sandbox.js\";\r\n\r\n/** read_file 工具的参数格式 */\r\nexport interface ReadFileArgs {\r\n /** 文件路径(相对于 cwd 或绝对路径) */\r\n path: string;\r\n /** 起始行号(1-based),默认 1 */\r\n startLine?: number;\r\n /** 结束行号(1-based,包含),默认到文件末尾 */\r\n endLine?: number;\r\n}\r\n\r\n/** 读取前 N 字节检测是否为二进制文件 */\r\nasync function checkBinary(filePath: string): Promise<boolean> {\r\n const fileHandle = await open(filePath, \"r\");\r\n try {\r\n const buffer = Buffer.alloc(8192); // 8KB\r\n const { bytesRead } = await fileHandle.read(buffer, 0, 8192, 0);\r\n return buffer.subarray(0, bytesRead).includes(0); // NUL 字节 → 二进制\r\n } finally {\r\n await fileHandle.close();\r\n }\r\n}\r\n\r\n/**\r\n * read_file 工具 — 读取文件内容,支持行号范围选择。\r\n *\r\n * 功能:\r\n * - 按行号范围读取部分内容\r\n * - 自动添加行号前缀(→ 分隔)\r\n * - 文件大小限制(默认 10MB)\r\n * - 输出长度截断(默认 50K 字符)\r\n * - 二进制检测(阻止读取二进制文件)\r\n */\r\nexport const readFileTool: AgentTool<ReadFileArgs> = {\r\n name: \"read_file\",\r\n kind: ToolKind.Read,\r\n description:\r\n \"读取指定路径的文件内容。支持行号范围选择,输出带行号。适用于查看源代码、配置文件等文本文件。自动拒绝二进制文件。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n path: {\r\n type: \"string\",\r\n description: \"文件路径(相对于当前工作目录或绝对路径)\",\r\n },\r\n startLine: {\r\n type: \"number\",\r\n description: \"起始行号(从 1 开始),默认为 1\",\r\n },\r\n endLine: {\r\n type: \"number\",\r\n description: \"结束行号(包含),默认到文件末尾\",\r\n },\r\n },\r\n required: [\"path\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: ReadFileArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.path || typeof args.path !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 path\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const filePath = resolvePath(args.path, ctx.cwd);\r\n\r\n try {\r\n // 检查文件大小\r\n const fileStat = await stat(filePath);\r\n const maxSize = 10 * 1024 * 1024; // 10MB\r\n if (fileStat.size > maxSize) {\r\n return {\r\n success: false,\r\n data: `文件过大(${(fileStat.size / 1024 / 1024).toFixed(1)}MB),超过 10MB 限制`,\r\n error: \"FILE_TOO_LARGE\",\r\n };\r\n }\r\n\r\n // 检查是否为目录\r\n if (fileStat.isDirectory()) {\r\n return {\r\n success: false,\r\n data: `\"${filePath}\" 是一个目录,请使用 ls 工具查看目录内容`,\r\n error: \"IS_DIRECTORY\",\r\n };\r\n }\r\n\r\n // 二进制检测:扫描前 8KB 看是否包含 NUL 字节\r\n if (fileStat.size > 0) {\r\n const isBin = await checkBinary(filePath);\r\n if (isBin) {\r\n return {\r\n success: false,\r\n data: `\"${filePath}\" 看起来是二进制文件,不支持读取`,\r\n error: \"BINARY_FILE\",\r\n };\r\n }\r\n }\r\n\r\n const content = await readFile(filePath, \"utf-8\");\r\n const lines = content.split(\"\\n\");\r\n\r\n // 如果是空文件,最后一项为空行,pop 掉\r\n if (lines.length > 0 && lines[lines.length - 1] === \"\" && content.endsWith(\"\\n\")) {\r\n lines.pop();\r\n }\r\n\r\n // 行号范围处理(1-based → 0-based)\r\n const startLine = Math.max(1, args.startLine ?? 1) - 1;\r\n const endLine = args.endLine\r\n ? Math.min(args.endLine, lines.length)\r\n : lines.length;\r\n const selectedLines = lines.slice(startLine, endLine);\r\n\r\n // 添加行号前缀(右对齐 + →)\r\n const lineNumWidth = String(endLine).length;\r\n const result = selectedLines\r\n .map((line, i) => {\r\n const lineNum = String(startLine + i + 1).padStart(lineNumWidth, \" \");\r\n return `${lineNum}→${line}`;\r\n })\r\n .join(\"\\n\");\r\n\r\n // 尾部提示:告知剩余行数\r\n const remaining = lines.length - endLine;\r\n const tailHint = remaining > 0\r\n ? `\\n\\n[还有 ${remaining} 行;使用 startLine=${endLine + 1} 继续查看]`\r\n : \"\";\r\n\r\n // UI 摘要:仅显示路径与行数范围,避免把文件内容塞进 UI\r\n const relPath = relative(ctx.cwd, filePath).replace(/\\\\/g, \"/\");\r\n const rangeLabel = (startLine > 0 || endLine < lines.length)\r\n ? `第 ${startLine + 1}-${endLine} 行`\r\n : `${lines.length} 行`;\r\n\r\n return {\r\n success: true,\r\n data: truncateOutput(result) + tailHint,\r\n summary: `📖 ${relPath}(${rangeLabel})`,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `读取文件失败:${message}`,\r\n error: \"READ_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// write_file 工具 — 创建或覆盖文件\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { mkdir, readFile } from \"node:fs/promises\";\r\nimport { dirname, relative, basename } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { resolvePath, confine } from \"../sandbox.js\";\r\nimport { computeFileDiff } from \"../diff.js\";\r\nimport { writeFileWithEol } from \"../eol.js\";\r\n\r\n/** write_file 工具的参数格式 */\r\nexport interface WriteFileArgs {\r\n /** 文件路径(相对于 cwd 或绝对路径) */\r\n path: string;\r\n /** 要写入的文件内容 */\r\n content: string;\r\n}\r\n\r\n/**\r\n * write_file 工具 — 创建或覆盖文件。\r\n */\r\nexport const writeFileTool: AgentTool<WriteFileArgs> = {\r\n name: \"write_file\",\r\n kind: ToolKind.Edit,\r\n description:\r\n \"创建或覆盖文件。如果父目录不存在会自动创建。适用于创建新文件或完全替换文件内容。请勿用此工具创建 _temp_、_debug_ 等用于诊断/调试的临时文件——如需诊断请改用 bash 工具内联脚本(node -e)。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n path: {\r\n type: \"string\",\r\n description: \"文件路径(相对于当前工作目录或绝对路径)\",\r\n },\r\n content: {\r\n type: \"string\",\r\n description: \"要写入的文件内容\",\r\n },\r\n },\r\n required: [\"path\", \"content\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: WriteFileArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.path || typeof args.path !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 path\", error: \"INVALID_ARGS\" };\r\n }\r\n if (args.content === undefined || args.content === null) {\r\n return { success: false, data: \"缺少必要参数 content\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const filePath = resolvePath(args.path, ctx.cwd);\r\n\r\n try {\r\n if (ctx.writeRoots && ctx.writeRoots.length > 0) {\r\n const conf = await confine(ctx.writeRoots, filePath);\r\n if (!conf.ok) {\r\n return { success: false, data: conf.error, error: \"OUTSIDE_WRITE_ROOTS\" };\r\n }\r\n }\r\n\r\n let oldContent = \"\";\r\n let existedBefore = false;\r\n try {\r\n oldContent = await readFile(filePath, \"utf-8\");\r\n existedBefore = true;\r\n } catch {\r\n // 文件不存在,这是新建文件\r\n }\r\n\r\n await mkdir(dirname(filePath), { recursive: true });\r\n\r\n const content = args.content;\r\n await writeFileWithEol(filePath, oldContent, content);\r\n\r\n const diff = computeFileDiff(oldContent, content, filePath);\r\n diff.existedBefore = existedBefore;\r\n\r\n const lineCount = content.split(\"\\n\").length;\r\n const byteSize = Buffer.byteLength(content, \"utf-8\");\r\n\r\n const action = existedBefore ? \"已修改\" : \"已创建\";\r\n const diffSummary = existedBefore\r\n ? `,+${diff.additions} -${diff.deletions}`\r\n : `,+${diff.additions} 行(新建)`;\r\n\r\n const fileName = basename(filePath);\r\n const summary = existedBefore\r\n ? `📝 修改: ${fileName} (+${diff.additions} -${diff.deletions})`\r\n : `📝 新建: ${fileName} (+${diff.additions} 行)`;\r\n\r\n return {\r\n success: true,\r\n data: `文件${action}:${relative(ctx.cwd, filePath).replace(/\\\\/g, \"/\")}(${lineCount} 行,${byteSize} 字节${diffSummary})`,\r\n summary,\r\n diff,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `写入文件失败:${message}`,\r\n error: \"WRITE_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// edit_file 工具 — 精确字符串替换编辑文件\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { basename } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { resolvePath, confine } from \"../sandbox.js\";\r\nimport { computeFileDiff } from \"../diff.js\";\r\nimport { writeFileWithEol } from \"../eol.js\";\r\n\r\n/** edit_file 工具的参数格式 */\r\nexport interface EditFileArgs {\r\n /** 文件路径(相对于 cwd 或绝对路径) */\r\n path: string;\r\n /** 要查找的原始文本(精确匹配) */\r\n old_text: string;\r\n /** 替换后的新文本 */\r\n new_text: string;\r\n}\r\n\r\n/**\r\n * edit_file 工具 — 对文件进行精确字符串替换。\r\n */\r\nexport const editFileTool: AgentTool<EditFileArgs> = {\r\n name: \"edit_file\",\r\n kind: ToolKind.Edit,\r\n description:\r\n \"对文件进行精确字符串替换。查找文件中的 old_text 并替换为 new_text。如果 old_text 出现多次或未找到则报错。适用于小范围精确修改。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n path: {\r\n type: \"string\",\r\n description: \"文件路径(相对于当前工作目录或绝对路径)\",\r\n },\r\n old_text: {\r\n type: \"string\",\r\n description: \"要查找的原始文本(精确匹配)\",\r\n },\r\n new_text: {\r\n type: \"string\",\r\n description: \"替换后的新文本\",\r\n },\r\n },\r\n required: [\"path\", \"old_text\", \"new_text\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: EditFileArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.path || typeof args.path !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 path\", error: \"INVALID_ARGS\" };\r\n }\r\n if (typeof args.old_text !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 old_text\", error: \"INVALID_ARGS\" };\r\n }\r\n if (typeof args.new_text !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 new_text\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const filePath = resolvePath(args.path, ctx.cwd);\r\n\r\n if (ctx.writeRoots && ctx.writeRoots.length > 0) {\r\n const conf = await confine(ctx.writeRoots, filePath);\r\n if (!conf.ok) {\r\n return { success: false, data: conf.error, error: \"OUTSIDE_WRITE_ROOTS\" };\r\n }\r\n }\r\n\r\n try {\r\n const content = await readFile(filePath, \"utf-8\");\r\n\r\n const firstIndex = content.indexOf(args.old_text);\r\n if (firstIndex === -1) {\r\n return {\r\n success: false,\r\n data: `未找到要替换的文本。请确认 old_text 与文件内容完全一致(包括缩进和空格)。`,\r\n error: \"TEXT_NOT_FOUND\",\r\n };\r\n }\r\n\r\n const secondIndex = content.indexOf(args.old_text, firstIndex + 1);\r\n if (secondIndex !== -1) {\r\n return {\r\n success: false,\r\n data: `要替换的文本在文件中出现多次,请提供更多上下文以精确定位。`,\r\n error: \"TEXT_MULTIPLE_MATCHES\",\r\n };\r\n }\r\n\r\n const newContent = content.replace(args.old_text, args.new_text);\r\n await writeFileWithEol(filePath, content, newContent);\r\n\r\n const diff = computeFileDiff(content, newContent, filePath);\r\n diff.existedBefore = true;\r\n\r\n const beforeText = content.slice(0, firstIndex);\r\n const startLine = beforeText.split(\"\\n\").length;\r\n const oldLines = args.old_text.split(\"\\n\").length;\r\n const newLines = args.new_text.split(\"\\n\").length;\r\n\r\n const diffSummary = `+${diff.additions} -${diff.deletions}`;\r\n const summary = `📝 修改: ${basename(filePath)} (+${diff.additions} -${diff.deletions})`;\r\n\r\n return {\r\n success: true,\r\n data: `文件已编辑:${filePath}\\n替换位置:第 ${startLine} 行\\n${oldLines} 行 → ${newLines} 行\\n变更:${diffSummary}`,\r\n summary,\r\n diff,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `编辑文件失败:${message}`,\r\n error: \"EDIT_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// multi_edit 工具 — 精确字符串批量替换编辑文件(原子性)\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { basename } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { resolvePath, confine } from \"../sandbox.js\";\r\nimport { computeFileDiff } from \"../diff.js\";\r\nimport { writeFileWithEol } from \"../eol.js\";\r\n\r\n/** 单个编辑步骤 */\r\nexport interface EditStep {\r\n /** 要查找的原始文本(精确匹配) */\r\n oldText: string;\r\n /** 替换后的新文本 */\r\n newText: string;\r\n /** 是否替换文件中所有匹配项,默认 false */\r\n replaceAll?: boolean;\r\n}\r\n\r\n/** multi_edit 工具的参数格式 */\r\nexport interface MultiEditArgs {\r\n /** 文件路径(相对于 cwd 或绝对路径) */\r\n path: string;\r\n /** 有序编辑步骤数组 */\r\n edits: EditStep[];\r\n}\r\n\r\n/**\r\n * multi_edit 工具 — 对文件进行原子批量替换编辑。\r\n */\r\nexport const multiEditTool: AgentTool<MultiEditArgs> = {\r\n name: \"multi_edit\",\r\n kind: ToolKind.Edit,\r\n description:\r\n \"对文件进行原子批量替换编辑。在一个请求中执行多个精确替换,任一失败则全部回滚。支持 replaceAll 参数做全局替换。适用于需要同时修改文件多处的场景。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n path: {\r\n type: \"string\",\r\n description: \"文件路径(相对于当前工作目录或绝对路径)\",\r\n },\r\n edits: {\r\n type: \"array\",\r\n description: \"有序的编辑步骤列表。每个步骤包含 oldText(精确匹配)、newText(替换内容)和可选的 replaceAll(是否替换所有匹配项)\",\r\n items: {\r\n type: \"object\",\r\n properties: {\r\n oldText: { type: \"string\", description: \"要查找的原始文本(精确匹配)\" },\r\n newText: { type: \"string\", description: \"替换后的新文本\" },\r\n replaceAll: { type: \"boolean\", description: \"是否替换所有匹配项,默认 false\" },\r\n },\r\n required: [\"oldText\", \"newText\"],\r\n additionalProperties: false,\r\n },\r\n },\r\n },\r\n required: [\"path\", \"edits\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: MultiEditArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.path || typeof args.path !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 path\", error: \"INVALID_ARGS\" };\r\n }\r\n if (!Array.isArray(args.edits) || args.edits.length === 0) {\r\n return { success: false, data: \"缺少必要参数 edits(非空数组)\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const filePath = resolvePath(args.path, ctx.cwd);\r\n\r\n if (ctx.writeRoots && ctx.writeRoots.length > 0) {\r\n const conf = await confine(ctx.writeRoots, filePath);\r\n if (!conf.ok) {\r\n return { success: false, data: conf.error, error: \"OUTSIDE_WRITE_ROOTS\" };\r\n }\r\n }\r\n\r\n try {\r\n const originalContent = await readFile(filePath, \"utf-8\");\r\n let currentContent = originalContent;\r\n\r\n for (let idx = 0; idx < args.edits.length; idx++) {\r\n const step = args.edits[idx]!;\r\n if (typeof step.oldText !== \"string\") {\r\n return {\r\n success: false,\r\n data: `第 ${idx + 1} 步:缺少有效的 oldText`,\r\n error: \"INVALID_STEP_ARGS\",\r\n };\r\n }\r\n\r\n if (step.replaceAll) {\r\n let count = 0;\r\n let pos = -1;\r\n while ((pos = currentContent.indexOf(step.oldText, pos + 1)) !== -1) {\r\n count++;\r\n }\r\n\r\n if (count === 0) {\r\n return {\r\n success: false,\r\n data: `第 ${idx + 1} 步:未找到要替换的文本,请确认 oldText 与文件内容完全一致`,\r\n error: \"TEXT_NOT_FOUND\",\r\n };\r\n }\r\n\r\n currentContent = currentContent.split(step.oldText).join(step.newText);\r\n } else {\r\n const firstIdx = currentContent.indexOf(step.oldText);\r\n if (firstIdx === -1) {\r\n return {\r\n success: false,\r\n data: `第 ${idx + 1} 步:未找到要替换的文本。请确认 oldText 与文件内容完全一致(包括缩进和空格)。`,\r\n error: \"TEXT_NOT_FOUND\",\r\n };\r\n }\r\n\r\n const secondIdx = currentContent.indexOf(step.oldText, firstIdx + 1);\r\n if (secondIdx !== -1) {\r\n return {\r\n success: false,\r\n data: `第 ${idx + 1} 步:要替换的文本在文件中出现多次,请提供更多上下文以精确定位,或设置 replaceAll=true`,\r\n error: \"TEXT_MULTIPLE_MATCHES\",\r\n };\r\n }\r\n\r\n currentContent = currentContent.replace(step.oldText, step.newText);\r\n }\r\n }\r\n\r\n await writeFileWithEol(filePath, originalContent, currentContent);\r\n\r\n const diff = computeFileDiff(originalContent, currentContent, filePath);\r\n diff.existedBefore = true;\r\n\r\n return {\r\n success: true,\r\n data: `文件已编辑:${filePath}\\n共执行 ${args.edits.length} 步替换\\n变更:+${diff.additions} -${diff.deletions}`,\r\n summary: `📝 修改: ${basename(filePath)} (${args.edits.length} 步, +${diff.additions} -${diff.deletions})`,\r\n diff,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `批量编辑失败:${message}`,\r\n error: \"MULTI_EDIT_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// delete_range 工具 — 按行锚点删除文件中的行范围\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { basename } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { resolvePath, confine } from \"../sandbox.js\";\r\nimport { computeFileDiff } from \"../diff.js\";\r\nimport { writeFileWithEol } from \"../eol.js\";\r\n\r\n/** delete_range 工具的参数格式 */\r\nexport interface DeleteRangeArgs {\r\n /** 文件路径(相对于 cwd 或绝对路径) */\r\n path: string;\r\n /** 删除范围的起始标记行(必须唯一) */\r\n startAnchor: string;\r\n /** 删除范围的结束标记行(必须唯一,须在 start_anchor 之后) */\r\n endAnchor: string;\r\n /** 是否包含锚点行本身,默认 false(只删除中间内容) */\r\n inclusive?: boolean;\r\n}\r\n\r\n/**\r\n * 在行列表中查找唯一匹配的行号。\r\n */\r\nfunction findUniqueLine(lines: string[], anchor: string, label: string): { line: number } | { error: string } {\r\n const matches: number[] = [];\r\n for (let i = 0; i < lines.length; i++) {\r\n if (lines[i] === anchor) {\r\n matches.push(i);\r\n }\r\n }\r\n\r\n if (matches.length === 0) {\r\n return { error: `未找到 \"${label}\" 锚点行,请确认内容完全一致` };\r\n }\r\n if (matches.length > 1) {\r\n return { error: `\"${label}\" 锚点行在文件中出现 ${matches.length} 次,请使用更唯一的行内容` };\r\n }\r\n\r\n return { line: matches[0]! };\r\n}\r\n\r\n/**\r\n * delete_range 工具 — 删除文件中两个锚点行之间的内容。\r\n */\r\nexport const deleteRangeTool: AgentTool<DeleteRangeArgs> = {\r\n name: \"delete_range\",\r\n kind: ToolKind.Edit,\r\n description:\r\n \"删除文件中两个锚点行之间的内容。通过两个唯一的行内容精确定位范围。适用于删除方法、代码块、配置段等。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n path: {\r\n type: \"string\",\r\n description: \"文件路径(相对于当前工作目录或绝对路径)\",\r\n },\r\n startAnchor: {\r\n type: \"string\",\r\n description: \"删除范围的起始标记行内容(必须在文件中唯一,精确匹配整行)\",\r\n },\r\n endAnchor: {\r\n type: \"string\",\r\n description: \"删除范围的结束标记行内容(必须在文件中唯一,精确匹配整行,须在 start_anchor 之后)\",\r\n },\r\n inclusive: {\r\n type: \"boolean\",\r\n description: \"是否包含锚点行本身,默认 false(只删除两行之间的内容)\",\r\n },\r\n },\r\n required: [\"path\", \"startAnchor\", \"endAnchor\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: DeleteRangeArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.path || typeof args.path !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 path\", error: \"INVALID_ARGS\" };\r\n }\r\n if (typeof args.startAnchor !== \"string\") {\r\n return { success: false, data: \"缺少有效参数 startAnchor\", error: \"INVALID_ARGS\" };\r\n }\r\n if (typeof args.endAnchor !== \"string\") {\r\n return { success: false, data: \"缺少有效参数 endAnchor\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const filePath = resolvePath(args.path, ctx.cwd);\r\n const inclusive = args.inclusive ?? false;\r\n\r\n if (ctx.writeRoots && ctx.writeRoots.length > 0) {\r\n const conf = await confine(ctx.writeRoots, filePath);\r\n if (!conf.ok) {\r\n return { success: false, data: conf.error, error: \"OUTSIDE_WRITE_ROOTS\" };\r\n }\r\n }\r\n\r\n try {\r\n const content = await readFile(filePath, \"utf-8\");\r\n const lines = content.split(\"\\n\");\r\n\r\n const startResult = findUniqueLine(lines, args.startAnchor, \"start_anchor\");\r\n if (\"error\" in startResult) {\r\n return { success: false, data: startResult.error, error: \"ANCHOR_NOT_FOUND\" };\r\n }\r\n\r\n const endResult = findUniqueLine(lines, args.endAnchor, \"end_anchor\");\r\n if (\"error\" in endResult) {\r\n return { success: false, data: endResult.error, error: \"ANCHOR_NOT_FOUND\" };\r\n }\r\n\r\n const startLine = startResult.line;\r\n const endLine = endResult.line;\r\n\r\n if (startLine >= endLine) {\r\n return {\r\n success: false,\r\n data: \"end_anchor 必须在 start_anchor 之后\",\r\n error: \"ANCHOR_ORDER_ERROR\",\r\n };\r\n }\r\n\r\n const rangeStart = inclusive ? startLine : startLine + 1;\r\n const rangeEnd = inclusive ? endLine : endLine - 1;\r\n\r\n if (rangeStart > rangeEnd) {\r\n return {\r\n success: false,\r\n data: \"删除范围为空(两行锚点相邻且 inclusive=false)\",\r\n error: \"EMPTY_RANGE\",\r\n };\r\n }\r\n\r\n const newLines = [...lines.slice(0, rangeStart), ...lines.slice(rangeEnd + 1)];\r\n const newContent = newLines.join(\"\\n\");\r\n\r\n await writeFileWithEol(filePath, content, newContent);\r\n\r\n const diff = computeFileDiff(content, newContent, filePath);\r\n diff.existedBefore = true;\r\n\r\n const deletedLines = rangeEnd - rangeStart + 1;\r\n\r\n const summary = `📝 修改: ${basename(filePath)} (删 ${deletedLines} 行, +${diff.additions} -${diff.deletions})`;\r\n\r\n return {\r\n success: true,\r\n data: `文件已编辑:${filePath}\\n删除行范围:第 ${rangeStart + 1} 行 ~ 第 ${rangeEnd + 1} 行(共 ${deletedLines} 行)\\n变更:+${diff.additions} -${diff.deletions}`,\r\n summary,\r\n diff,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `删除范围失败:${message}`,\r\n error: \"DELETE_RANGE_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// bash 工具 — 执行 shell 命令\r\n// ---------------------------------------------------------------------------\r\n\r\nimport process from \"node:process\";\r\nimport { existsSync } from \"node:fs\";\r\nimport { join } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { execCommand, truncateOutput, getDefaultTimeout } from \"../sandbox.js\";\r\n\r\n/** 是否为 Windows 平台 */\r\nconst isWindows = process.platform === \"win32\";\r\n\r\ninterface DetectResult {\r\n /** shell 可执行文件路径,Windows 未找到 Git Bash 时为 null */\r\n bin: string | null;\r\n /** shell 调用参数前缀,bash 用 [\"-c\"],cmd 用 [\"/c\"] */\r\n args: string[];\r\n /** 给 LLM 看的 shell 名称 */\r\n label: string;\r\n /** 给 LLM 看的语法指引 */\r\n hint: string;\r\n}\r\n\r\n/**\r\n * 探测本机可用的 bash。\r\n *\r\n * Windows 上优先 Git Bash(命令语法与 Unix 一致,跨平台命令可复用),\r\n * 找不到才回退 cmd.exe。这样 LLM 生成的 `pwd && ls` 在装有 Git Bash 的\r\n * Windows 机器上能直接跑通,不再像旧的“硬走 cmd”实现那样在中文\r\n * GBK 报错里循环重试(见曾经的日志\r\n * e3cb5aff-34c0-499a-a3be-dee13c18d14f 中 round 2-6 的 EXIT_CODE_1 风暴)。\r\n */\r\nfunction detectShell(): DetectResult {\r\n if (!isWindows) {\r\n return {\r\n bin: \"sh\",\r\n args: [\"-c\"],\r\n label: \"sh\",\r\n hint: \"当前 shell 是 bash 兼容语法,支持 pwd/ls/&&/管道等标准 Unix 工具。\",\r\n };\r\n }\r\n\r\n // 1. 用户显式指定的 bash 路径(优先级最高)\r\n const envBash = process.env.DSKCODE_BASH;\r\n if (envBash && existsSync(envBash)) {\r\n return { bin: envBash, args: [\"-c\"], label: \"Git Bash\", hint: SHELL_HINT_BASH };\r\n }\r\n\r\n // 2. 常见 Git for Windows 安装位置(C/D 盘 × Program Files × LocalAppData)\r\n // 同时覆盖 64 位默认目录与 32 位兼容目录,以及单用户安装。\r\n const roots = [\r\n process.env.ProgramFiles,\r\n process.env[\"ProgramFiles(x86)\"],\r\n process.env.LOCALAPPDATA ? join(process.env.LOCALAPPDATA, \"Programs\") : null,\r\n // 非 C 盘安装(你机器就是 D 盘),枚举几个常见盘符根\r\n \"D:\\\\Program Files\",\r\n \"E:\\\\Program Files\",\r\n ].filter((v): v is string => Boolean(v));\r\n\r\n for (const root of roots) {\r\n for (const sub of [\"Git\\\\bin\\\\bash.exe\", \"Git\\\\usr\\\\bin\\\\bash.exe\"]) {\r\n const candidate = join(root, sub);\r\n if (existsSync(candidate)) {\r\n return { bin: candidate, args: [\"-c\"], label: \"Git Bash\", hint: SHELL_HINT_BASH };\r\n }\r\n }\r\n }\r\n\r\n // 3. 回退 cmd.exe —— 命令语法不同,要在 hint 里明确告知 LLM\r\n return {\r\n bin: \"cmd\",\r\n args: [\"/c\"],\r\n label: \"cmd.exe\",\r\n hint:\r\n \"当前 shell 是 Windows cmd.exe。请使用 cmd 语法:路径用反斜杠或正斜杠均可、命令链用 & 或换行,避免使用 pwd/ls/&& 等 Unix 命令。\",\r\n };\r\n}\r\n\r\n/** Git Bash 检出后给 LLM 的语法提示 */\r\nconst SHELL_HINT_BASH =\r\n \"当前 shell 是 bash 兼容语法,支持 pwd/ls/&&/管道等标准 Unix 工具。\";\r\n\r\nconst SHELL_INFO = detectShell();\r\n\r\n/** bash 工具的参数格式 */\r\nexport interface BashArgs {\r\n /** 要执行的命令 */\r\n command: string;\r\n /** 执行超时时间(毫秒),默认 30000 */\r\n timeout?: number;\r\n}\r\n\r\n/** 仅供测试导出当前探测到的 shell 信息(不参与运行期逻辑) */\r\nexport const __shellInfo = SHELL_INFO;\r\n\r\n/**\r\n * bash 工具 — 在 shell 中执行命令。\r\n */\r\nexport const bashTool: AgentTool<BashArgs> = {\r\n name: \"bash\",\r\n kind: ToolKind.Other,\r\n description: [\r\n \"在 shell 中执行命令,返回 stdout / stderr / 退出码。支持超时控制和信号中止。\",\r\n `当前 shell:${SHELL_INFO.label}。${SHELL_INFO.hint}`,\r\n \"读取文件请优先使用 read_file 工具,不要用 cat/type/Get-Content;\",\r\n \"搜索代码用 grep / glob,列目录用 ls——这些专用工具在所有平台都可用且无需关心 shell 语法。\",\r\n ].join(\"\"),\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n command: {\r\n type: \"string\",\r\n description: `要执行的 shell 命令(当前 shell:${SHELL_INFO.label})`,\r\n },\r\n timeout: {\r\n type: \"number\",\r\n description: \"执行超时时间(毫秒),默认 30000\",\r\n },\r\n },\r\n required: [\"command\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: BashArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.command || typeof args.command !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 command\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const timeout = args.timeout ?? ctx.timeout ?? getDefaultTimeout();\r\n\r\n try {\r\n // shell 已在启动期按平台探测好:Windows 走 Git Bash,没有才回退 cmd\r\n const shellCommand = SHELL_INFO.bin ?? \"cmd\";\r\n const shellArgs = [...SHELL_INFO.args, args.command];\r\n\r\n const result = await execCommand(\r\n shellCommand,\r\n shellArgs,\r\n ctx.cwd,\r\n timeout,\r\n ctx.signal,\r\n true,\r\n );\r\n\r\n const parts: string[] = [];\r\n if (result.stdout) {\r\n parts.push(truncateOutput(result.stdout));\r\n }\r\n if (result.stderr) {\r\n parts.push(`[stderr]\\n${truncateOutput(result.stderr)}`);\r\n }\r\n\r\n const success = result.exitCode === 0;\r\n const output = parts.length > 0 ? parts.join(\"\\n\") : \"(无输出)\";\r\n\r\n const cmdPreview = args.command.length > 60\r\n ? args.command.slice(0, 57) + \"...\"\r\n : args.command;\r\n const summary = `🔧 $ ${cmdPreview}(exit ${result.exitCode ?? \"未知\"})`;\r\n\r\n return {\r\n success,\r\n data: `${output}\\n[退出码: ${result.exitCode ?? \"未知\"}]`,\r\n summary,\r\n error: success ? undefined : `EXIT_CODE_${result.exitCode ?? \"UNKNOWN\"}`,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `命令执行失败:${message}`,\r\n error: \"EXECUTION_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// glob 工具 — 文件搜索(模式匹配)\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readdir, stat } from \"node:fs/promises\";\r\nimport { join, relative, isAbsolute } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { truncateOutput } from \"../sandbox.js\";\r\n\r\n/** glob 工具的参数格式 */\r\nexport interface GlobArgs {\r\n /** 搜索模式(支持 * 和 ** 通配符) */\r\n pattern: string;\r\n /** 搜索的起始目录,默认为 cwd */\r\n directory?: string;\r\n}\r\n\r\n/**\r\n * 将 glob 模式转换为正则表达式。\r\n */\r\nfunction globToRegex(pattern: string): RegExp {\r\n let regexStr = pattern;\r\n regexStr = regexStr.replace(/\\*\\*\\//g, \"<<GLOBSTAR_SLASH>>\");\r\n regexStr = regexStr.replace(/\\*\\*/g, \"<<GLOBSTAR>>\");\r\n regexStr = regexStr.replace(/\\?/g, \"<<QUESTION>>\");\r\n regexStr = regexStr.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\");\r\n regexStr = regexStr.replace(/\\*/g, \"[^/]*\");\r\n regexStr = regexStr.replace(/<<GLOBSTAR_SLASH>>/g, \"(.*/)?\");\r\n regexStr = regexStr.replace(/<<GLOBSTAR>>/g, \".*\");\r\n regexStr = regexStr.replace(/<<QUESTION>>/g, \"[^/]\");\r\n return new RegExp(`^${regexStr}$`, \"i\");\r\n}\r\n\r\n/**\r\n * 递归遍历目录收集文件路径。\r\n */\r\nasync function walkDir(dir: string, baseDir: string): Promise<string[]> {\r\n const results: string[] = [];\r\n let entries;\r\n\r\n try {\r\n entries = await readdir(dir, { withFileTypes: true });\r\n } catch {\r\n return results;\r\n }\r\n\r\n for (const entry of entries) {\r\n if (entry.isDirectory() && (entry.name === \"node_modules\" || entry.name === \".git\")) {\r\n continue;\r\n }\r\n\r\n const fullPath = join(dir, entry.name);\r\n const relPath = relative(baseDir, fullPath);\r\n\r\n if (entry.isDirectory()) {\r\n results.push(...await walkDir(fullPath, baseDir));\r\n } else {\r\n results.push(relPath);\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\n/**\r\n * glob 工具 — 按模式搜索文件路径。\r\n *\r\n * 功能:\r\n * - 支持 * 和 ** 通配符\r\n * - 自动跳过 node_modules 和 .git 目录\r\n * - 返回相对于搜索目录的路径列表\r\n */\r\nexport const globTool: AgentTool<GlobArgs> = {\r\n name: \"glob\",\r\n kind: ToolKind.Read,\r\n description:\r\n \"按模式搜索文件路径。支持 *(匹配文件名部分)和 **(匹配多层目录)通配符。自动跳过 node_modules 和 .git 目录。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n pattern: {\r\n type: \"string\",\r\n description: \"搜索模式(支持 * 和 ** 通配符,如 **/*.ts、src/**/*.test.ts)\",\r\n },\r\n directory: {\r\n type: \"string\",\r\n description: \"搜索的起始目录,默认为当前工作目录\",\r\n },\r\n },\r\n required: [\"pattern\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: GlobArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.pattern || typeof args.pattern !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 pattern\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const searchDir = args.directory\r\n ? (isAbsolute(args.directory) ? args.directory : join(ctx.cwd, args.directory))\r\n : ctx.cwd;\r\n const regex = globToRegex(args.pattern);\r\n\r\n try {\r\n const dirStat = await stat(searchDir);\r\n if (!dirStat.isDirectory()) {\r\n return { success: false, data: `路径不是目录:${searchDir}`, error: \"NOT_DIRECTORY\" };\r\n }\r\n\r\n const allFiles = await walkDir(searchDir, searchDir);\r\n const matched = allFiles.filter((f) => regex.test(f));\r\n\r\n if (matched.length === 0) {\r\n return {\r\n success: true,\r\n data: `未找到匹配 \"${args.pattern}\" 的文件`,\r\n summary: `${args.pattern} → 0 个文件`,\r\n };\r\n }\r\n\r\n const limit = 200;\r\n const limited = matched.slice(0, limit);\r\n const output = limited.join(\"\\n\");\r\n const suffix = matched.length > limit ? `\\n\\n... 共 ${matched.length} 个文件,只显示前 ${limit} 个` : \"\";\r\n\r\n return {\r\n success: true,\r\n data: truncateOutput(output + suffix),\r\n summary: `${args.pattern} → ${matched.length} 个文件`,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `文件搜索失败:${message}`,\r\n error: \"GLOB_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// grep 工具 — 文件内容正则搜索\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readdir, readFile, stat } from \"node:fs/promises\";\r\nimport { join, relative, isAbsolute } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { truncateOutput } from \"../sandbox.js\";\r\n\r\n/** grep 工具的参数格式 */\r\nexport interface GrepArgs {\r\n /** 正则表达式模式 */\r\n pattern: string;\r\n /** 搜索的目录路径,默认为 cwd */\r\n directory?: string;\r\n /** 文件扩展名过滤(如 \"ts\"、\"json\"),不含点号 */\r\n include?: string;\r\n /** 是否大小写敏感,默认 false */\r\n case_sensitive?: boolean;\r\n /** 最大搜索文件数,默认 200 */\r\n max_files?: number;\r\n}\r\n\r\n/** 单个匹配结果 */\r\ninterface GrepMatch {\r\n file: string;\r\n line: number;\r\n content: string;\r\n}\r\n\r\n/**\r\n * 递归遍历目录收集文件路径。\r\n */\r\nasync function collectFiles(\r\n dir: string,\r\n extension?: string,\r\n maxFiles = 200,\r\n): Promise<string[]> {\r\n const results: string[] = [];\r\n let entries;\r\n\r\n try {\r\n entries = await readdir(dir, { withFileTypes: true });\r\n } catch {\r\n return results;\r\n }\r\n\r\n for (const entry of entries) {\r\n if (results.length >= maxFiles) break;\r\n\r\n if (entry.isDirectory() && (entry.name === \"node_modules\" || entry.name === \".git\" || entry.name === \"dist\")) {\r\n continue;\r\n }\r\n\r\n const fullPath = join(dir, entry.name);\r\n\r\n if (entry.isDirectory()) {\r\n results.push(...await collectFiles(fullPath, extension, maxFiles - results.length));\r\n } else {\r\n if (extension && !entry.name.endsWith(`.${extension}`)) {\r\n continue;\r\n }\r\n results.push(fullPath);\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\n/**\r\n * grep 工具 — 在文件内容中搜索正则表达式匹配。\r\n */\r\nexport const grepTool: AgentTool<GrepArgs> = {\r\n name: \"grep\",\r\n kind: ToolKind.Read,\r\n description:\r\n \"在文件内容中搜索正则表达式。返回匹配行的文件路径、行号和内容。支持大小写敏感、文件扩展名过滤。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n pattern: {\r\n type: \"string\",\r\n description: \"正则表达式搜索模式\",\r\n },\r\n directory: {\r\n type: \"string\",\r\n description: \"搜索的目录路径,默认为当前工作目录\",\r\n },\r\n include: {\r\n type: \"string\",\r\n description: \"文件扩展名过滤(如 ts、json),不含点号\",\r\n },\r\n case_sensitive: {\r\n type: \"boolean\",\r\n description: \"是否大小写敏感,默认 false\",\r\n },\r\n max_files: {\r\n type: \"number\",\r\n description: \"最大搜索文件数,默认 200\",\r\n },\r\n },\r\n required: [\"pattern\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: GrepArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.pattern || typeof args.pattern !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 pattern\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const searchDir = args.directory\r\n ? (isAbsolute(args.directory) ? args.directory : join(ctx.cwd, args.directory))\r\n : ctx.cwd;\r\n const maxFiles = args.max_files ?? 200;\r\n\r\n try {\r\n const flags = args.case_sensitive ? \"g\" : \"gi\";\r\n const regex = new RegExp(args.pattern, flags);\r\n\r\n const dirStat = await stat(searchDir);\r\n if (!dirStat.isDirectory()) {\r\n return { success: false, data: `路径不是目录:${searchDir}`, error: \"NOT_DIRECTORY\" };\r\n }\r\n\r\n const files = await collectFiles(searchDir, args.include, maxFiles);\r\n const matches: GrepMatch[] = [];\r\n\r\n for (const filePath of files) {\r\n try {\r\n const content = await readFile(filePath, \"utf-8\");\r\n const lines = content.split(\"\\n\");\r\n const relPath = relative(searchDir, filePath);\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n if (regex.test(lines[i] ?? \"\")) {\r\n regex.lastIndex = 0;\r\n matches.push({\r\n file: relPath,\r\n line: i + 1,\r\n content: lines[i]!,\r\n });\r\n if (matches.length >= 500) break;\r\n }\r\n }\r\n\r\n if (matches.length >= 500) break;\r\n } catch {\r\n continue;\r\n }\r\n }\r\n\r\n if (matches.length === 0) {\r\n return {\r\n success: true,\r\n data: `未找到匹配 \"${args.pattern}\" 的内容`,\r\n summary: `🔍 \"${args.pattern}\" → 0 条命中`,\r\n };\r\n }\r\n\r\n const output = matches\r\n .map((m) => `${m.file}:${m.line}: ${m.content}`)\r\n .join(\"\\n\");\r\n\r\n const fileSet = new Set(matches.map((m) => m.file));\r\n const summary = `🔍 \"${args.pattern}\" → ${matches.length} 条命中 / ${fileSet.size} 个文件`;\r\n\r\n return {\r\n success: true,\r\n data: truncateOutput(output),\r\n summary,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `内容搜索失败:${message}`,\r\n error: \"GREP_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// ls 工具 — 列出目录内容\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readdir, stat } from \"node:fs/promises\";\r\nimport { join, relative } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { resolvePath, truncateOutput } from \"../sandbox.js\";\r\n\r\n/** ls 工具的参数格式 */\r\nexport interface LsArgs {\r\n /** 目录路径,默认为 cwd */\r\n path?: string;\r\n /** 是否显示隐藏文件,默认 false */\r\n all?: boolean;\r\n}\r\n\r\n/** 目录项类型标记 */\r\ntype EntryType = \"FILE\" | \"DIR\" | \"LINK\";\r\n\r\n/**\r\n * ls 工具 — 列出目录内容。\r\n *\r\n * 功能:\r\n * - 显示文件/目录类型标记\r\n * - 显示文件大小\r\n * - 可选显示隐藏文件\r\n * - 自动跳过无权访问的目录\r\n */\r\nexport const lsTool: AgentTool<LsArgs> = {\r\n name: \"ls\",\r\n kind: ToolKind.Read,\r\n description:\r\n \"列出目录内容。显示条目类型(文件/目录/链接)和大小。可选择是否显示隐藏文件。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n path: {\r\n type: \"string\",\r\n description: \"目录路径(相对于当前工作目录或绝对路径),默认为当前目录\",\r\n },\r\n all: {\r\n type: \"boolean\",\r\n description: \"是否显示隐藏文件(以 . 开头的文件),默认 false\",\r\n },\r\n },\r\n required: [],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: LsArgs, ctx: ToolContext): Promise<ToolResult> {\r\n const dirPath = args.path ? resolvePath(args.path, ctx.cwd) : ctx.cwd;\r\n const showAll = args.all ?? false;\r\n\r\n try {\r\n const entries = await readdir(dirPath, { withFileTypes: true });\r\n const lines: string[] = [];\r\n\r\n // 按名称排序:目录在前,文件在后\r\n const sorted = [...entries].toSorted((a, b) => {\r\n if (a.isDirectory() !== b.isDirectory()) {\r\n return a.isDirectory() ? -1 : 1;\r\n }\r\n return a.name.localeCompare(b.name);\r\n });\r\n\r\n for (const entry of sorted) {\r\n // 过滤隐藏文件\r\n if (!showAll && entry.name.startsWith(\".\")) continue;\r\n\r\n const typeMark: EntryType = entry.isDirectory()\r\n ? \"DIR\"\r\n : entry.isSymbolicLink()\r\n ? \"LINK\"\r\n : \"FILE\";\r\n\r\n let sizeStr = \"\";\r\n if (typeMark === \"FILE\") {\r\n try {\r\n const fileStat = await stat(join(dirPath, entry.name));\r\n if (fileStat.size < 1024) {\r\n sizeStr = `${fileStat.size}B`;\r\n } else if (fileStat.size < 1024 * 1024) {\r\n sizeStr = `${(fileStat.size / 1024).toFixed(1)}KB`;\r\n } else {\r\n sizeStr = `${(fileStat.size / 1024 / 1024).toFixed(1)}MB`;\r\n }\r\n } catch {\r\n sizeStr = \"?\";\r\n }\r\n }\r\n\r\n const typeLabel = typeMark === \"DIR\" ? \"📁\" : typeMark === \"LINK\" ? \"🔗\" : \"📄\";\r\n lines.push(`${typeLabel} ${entry.name}${sizeStr ? ` (${sizeStr})` : \"\"}`);\r\n }\r\n\r\n if (lines.length === 0) {\r\n return { success: true, data: \"目录为空\", summary: `📂 ${relative(ctx.cwd, dirPath).replace(/\\\\/g, \"/\")}(空)` };\r\n }\r\n\r\n const relPath = relative(ctx.cwd, dirPath).replace(/\\\\/g, \"/\");\r\n return {\r\n success: true,\r\n data: truncateOutput(`目录:${dirPath}\\n${lines.join(\"\\n\")}`),\r\n summary: `📂 ${relPath}(${lines.length} 项)`,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `列目录失败:${message}`,\r\n error: \"LS_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// fetch 工具 — HTTP 请求\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { truncateOutput } from \"../sandbox.js\";\r\n\r\n/** fetch 工具的参数格式 */\r\nexport interface FetchArgs {\r\n /** 请求 URL */\r\n url: string;\r\n /** 请求方法,默认 GET */\r\n method?: string;\r\n /** 请求头 */\r\n headers?: Record<string, string>;\r\n /** 请求体(POST/PUT 时使用) */\r\n body?: string;\r\n /** 响应最大长度(字符),默认 50000 */\r\n max_length?: number;\r\n}\r\n\r\n/**\r\n * fetch 工具 — 发起 HTTP 请求并返回响应内容。\r\n */\r\nexport const fetchTool: AgentTool<FetchArgs> = {\r\n name: \"fetch\",\r\n kind: ToolKind.Read,\r\n description:\r\n \"发起 HTTP 请求并返回响应内容。支持自定义方法和请求头。适用于获取网页内容、API 调用等场景。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n url: {\r\n type: \"string\",\r\n description: \"请求的 URL\",\r\n },\r\n method: {\r\n type: \"string\",\r\n description: \"HTTP 方法(GET、POST、PUT、DELETE 等),默认 GET\",\r\n },\r\n headers: {\r\n type: \"object\",\r\n description: \"请求头键值对\",\r\n additionalProperties: { type: \"string\" },\r\n },\r\n body: {\r\n type: \"string\",\r\n description: \"请求体内容(POST/PUT 时使用)\",\r\n },\r\n max_length: {\r\n type: \"number\",\r\n description: \"响应内容最大长度(字符),默认 50000\",\r\n },\r\n },\r\n required: [\"url\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: FetchArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.url || typeof args.url !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 url\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const method = (args.method ?? \"GET\").toUpperCase();\r\n const timeout = 30_000;\r\n const maxLength = args.max_length ?? 50_000;\r\n\r\n try {\r\n const fetchOptions: RequestInit = {\r\n method,\r\n headers: args.headers ?? {},\r\n signal: ctx.signal ?? AbortSignal.timeout(timeout),\r\n };\r\n\r\n if (args.body && (method === \"POST\" || method === \"PUT\" || method === \"PATCH\")) {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n (fetchOptions.headers as Record<string, string>)[\"Content-Type\"] ??= \"application/json\";\r\n fetchOptions.body = args.body;\r\n }\r\n\r\n const response = await fetch(args.url, fetchOptions);\r\n\r\n const contentType = response.headers.get(\"content-type\") ?? \"unknown\";\r\n const statusText = `${response.status} ${response.statusText}`;\r\n\r\n let body: string;\r\n try {\r\n body = await response.text();\r\n } catch {\r\n body = \"(无法读取响应体)\";\r\n }\r\n\r\n const truncatedBody = truncateOutput(body, maxLength);\r\n\r\n const header = `状态: ${statusText}\\n内容类型: ${contentType}`;\r\n const separator = body.length > 0 ? \"\\n---\\n\" : \"\";\r\n\r\n const urlPreview = args.url.length > 60\r\n ? args.url.slice(0, 57) + \"...\"\r\n : args.url;\r\n const summary = `🌐 ${method} ${urlPreview} → ${response.status}`;\r\n\r\n return {\r\n success: response.ok,\r\n data: `${header}${separator}${truncatedBody}`,\r\n summary,\r\n error: response.ok ? undefined : `HTTP_${response.status}`,\r\n };\r\n } catch (err: unknown) {\r\n if (err instanceof DOMException && err.name === \"AbortError\") {\r\n return { success: false, data: \"请求被中止\", error: \"ABORTED\" };\r\n }\r\n if (err instanceof TypeError && err.message.includes(\"fetch\")) {\r\n return { success: false, data: `网络错误:${err.message}`, error: \"NETWORK_ERROR\" };\r\n }\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `HTTP 请求失败:${message}`,\r\n error: \"FETCH_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// 内置工具 — 注册所有内置工具的工厂函数\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { type AnyAgentTool, eraseTool } from \"../types.js\";\r\nimport { readFileTool } from \"./read-file.js\";\r\nimport { writeFileTool } from \"./write-file.js\";\r\nimport { editFileTool } from \"./edit-file.js\";\r\nimport { multiEditTool } from \"./multi-edit.js\";\r\nimport { deleteRangeTool } from \"./delete-range.js\";\r\nimport { bashTool } from \"./bash.js\";\r\nimport { globTool } from \"./glob.js\";\r\nimport { grepTool } from \"./grep.js\";\r\nimport { lsTool } from \"./ls.js\";\r\nimport { fetchTool } from \"./fetch.js\";\r\n\r\n/** 所有内置工具列表(类型已擦除,可直接用于注册) */\r\nexport const builtinTools: AnyAgentTool[] = [\r\n eraseTool(readFileTool),\r\n eraseTool(writeFileTool),\r\n eraseTool(editFileTool),\r\n eraseTool(multiEditTool),\r\n eraseTool(deleteRangeTool),\r\n eraseTool(bashTool),\r\n eraseTool(globTool),\r\n eraseTool(grepTool),\r\n eraseTool(lsTool),\r\n eraseTool(fetchTool),\r\n];\r\n\r\n/**\r\n * 获取所有内置工具映射表(按名称索引)。\r\n */\r\nexport function getBuiltinToolMap(): Map<string, AnyAgentTool> {\r\n return new Map(builtinTools.map((t) => [t.name, t]));\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 渐变色工具 — 用于占位符从左到右的渐变动画\r\n// ---------------------------------------------------------------------------\r\n\r\n/** RGB 色标元组 [R, G, B],每个值 0~255 */\r\nexport type ColorStop = [number, number, number];\r\n\r\n// ---- 预设渐变方案 ----\r\n\r\n/** 空闲占位符渐变:赛博朋克绿 → 青 → 紫 */\r\nexport const IDLE_GRADIENT_STOPS: ColorStop[] = [\r\n [0, 255, 65], // #00ff41\r\n [0, 255, 255], // #00ffff\r\n [189, 147, 249], // #bd93f9\r\n];\r\n\r\n/** 命令提示条渐变:浅柠黄 → 暖黄 → 橙色(从左到右流动) */\r\nexport const CMD_TIP_GRADIENT_STOPS: ColorStop[] = [\r\n [255, 245, 180], // #fff5b4 浅柠黄\r\n [255, 210, 80], // #ffd250 暖黄\r\n [255, 150, 50], // #ff9632 橙色\r\n];\r\n\r\n/** 流式占位符渐变:琥珀 → 焦糖 → 棕红 */\r\nexport const STREAMING_GRADIENT_STOPS: ColorStop[] = [\r\n [255, 191, 0], // #ffbf00 琥珀金\r\n [210, 140, 60], // #d28c3c 焦糖\r\n [175, 70, 40], // #af4628 棕红\r\n];\r\n\r\n// ---- 渐变动画配置 ----\r\n\r\n/** 动画参数:每帧相位增量 & 帧间隔(ms) */\r\nexport const GRADIENT_ANIMATION = {\r\n /** 空闲占位符动画:每帧相位步进(值越大流速越快) */\r\n idlePhaseStep: 0.06,\r\n /** 空闲占位符动画:帧间隔(ms) */\r\n idleInterval: 40,\r\n /** 流式占位符动画:每帧相位步进 */\r\n streamingPhaseStep: 0.05,\r\n /** 流式占位符动画:帧间隔(ms) */\r\n streamingInterval: 40,\r\n /** 命令提示条动画:每帧相位步进 */\r\n cmdTipPhaseStep: 0.05,\r\n /** 命令提示条动画:帧间隔(ms) */\r\n cmdTipInterval: 40,\r\n} as const;\r\n\r\n// ---- 渲染时跳过着色的字符 ----\r\n\r\n/** 不参与渐变着色的字符(空格、标点、emoji 等) */\r\nexport const SKIP_CHARS = new Set([\" \", \"~\", \".\", \"。\", \"!\", \"👇\"]);\r\n\r\n// ---- 核心算法 ----\r\n\r\n/** 根据 t(0~1) 在色标间线性插值,返回 hex 色值 */\r\nexport function lerpStopsToHex(t: number, stops: readonly ColorStop[]): string {\r\n const len = stops.length;\r\n const segment = t * (len - 1);\r\n const idx = Math.min(Math.floor(segment), len - 2);\r\n const frac = segment - idx;\r\n const stop = stops[idx];\r\n const nextStop = stops[idx + 1];\r\n if (!stop || !nextStop) return \"#808080\";\r\n const r = Math.round(stop[0] + (nextStop[0] - stop[0]) * frac);\r\n const g = Math.round(stop[1] + (nextStop[1] - stop[1]) * frac);\r\n const b = Math.round(stop[2] + (nextStop[2] - stop[2]) * frac);\r\n return `#${r.toString(16).padStart(2, \"0\")}${g.toString(16).padStart(2, \"0\")}${b.toString(16).padStart(2, \"0\")}`;\r\n}\r\n\r\n/** 对文本计算逐字渐变色数组,phase 控制色彩偏移位置(0~1) */\r\nexport function getGradientColors(\r\n text: string,\r\n phase: number,\r\n stops: readonly ColorStop[],\r\n): string[] {\r\n const len = text.length;\r\n if (len === 0) return [];\r\n return text.split(\"\").map((ch, i) => {\r\n if (SKIP_CHARS.has(ch)) return \"\";\r\n const t = ((i / (len - 1)) + phase) % 1;\r\n return lerpStopsToHex(t, stops);\r\n });\r\n}","import { Box, Text, useInput } from \"ink\";\r\nimport { useState, useCallback } from \"react\";\r\nimport type { Game } from \"../game/index.js\";\r\nimport { useDoubleCtrlC } from \"../ui/useDoubleCtrlC.js\";\r\n\r\ninterface GamePickerProps {\r\n games: Game[];\r\n onSelect: (game: Game) => void;\r\n onExit?: () => void;\r\n onBackToChat?: () => void;\r\n}\r\n\r\nexport function GamePicker({ games, onSelect, onExit, onBackToChat }: GamePickerProps) {\r\n const [selectedIndex, setSelectedIndex] = useState(0);\r\n\r\n const exitAction = onBackToChat ?? onExit ?? (() => process.exit(0));\r\n const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(exitAction);\r\n\r\n useInput(\r\n useCallback(\r\n (input, key) => {\r\n // Ctrl+C:双击退出\r\n if (input === \"c\" && key.ctrl) {\r\n handleCtrlC();\r\n return;\r\n }\r\n\r\n if (games.length === 0) return;\r\n if (key.upArrow || input === \"k\") {\r\n setSelectedIndex((prev) => (prev > 0 ? prev - 1 : games.length - 1));\r\n } else if (key.downArrow || input === \"j\") {\r\n setSelectedIndex((prev) => (prev < games.length - 1 ? prev + 1 : 0));\r\n } else if (key.return) {\r\n const game = games[selectedIndex];\r\n if (game) onSelect(game);\r\n } else if (key.escape || input === \"q\") {\r\n if (onBackToChat) onBackToChat();\r\n else onExit?.();\r\n }\r\n },\r\n [games, selectedIndex, onSelect, onExit, onBackToChat, handleCtrlC],\r\n ),\r\n );\r\n\r\n return (\r\n <Box flexDirection=\"column\">\r\n <Box marginBottom={1}>\r\n <Text bold color=\"#00ffff\">\r\n 🎮 游戏列表\r\n </Text>\r\n </Box>\r\n\r\n <Box flexDirection=\"column\">\r\n {games.map((game, index) => {\r\n const isSelected = index === selectedIndex;\r\n return (\r\n <Box key={game.id} flexDirection=\"row\">\r\n <Box width={3} flexShrink={0}>\r\n {isSelected ? (\r\n <Text bold color=\"#00ff41\">\r\n {\"▸ \"}\r\n </Text>\r\n ) : (\r\n <Text>{\" \"}</Text>\r\n )}\r\n </Box>\r\n <Box width={20} flexShrink={0}>\r\n <Text bold color={isSelected ? \"#00ff41\" : \"#ffffff\"}>\r\n {game.name}\r\n </Text>\r\n </Box>\r\n <Box>\r\n <Text color=\"#888888\">{game.description}</Text>\r\n </Box>\r\n </Box>\r\n );\r\n })}\r\n </Box>\r\n\r\n <Box marginTop={1}>\r\n <Text dimColor>\r\n {\" ↑/↓ 选择 Enter 启动 q 返回\"}\r\n </Text>\r\n </Box>\r\n\r\n {/* 双击 Ctrl+C 退出提示 */}\r\n {doubleCtrlC && (\r\n <Box marginTop={1}>\r\n <Text color=\"#ff1493\" bold>\r\n {\" ⚠ 再按一次 Ctrl+C 退出 dskcode\"}\r\n </Text>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}\r\n","export interface Game {\r\n /** 游戏唯一标识 */\r\n id: string;\r\n /** 游戏名称 */\r\n name: string;\r\n /** 简短描述 */\r\n description: string;\r\n /** 启动游戏 */\r\n play: () => Promise<void>;\r\n}\r\n\r\nconst registry = new Map<string, Game>();\r\n\r\nexport function registerGame(game: Game): void {\r\n registry.set(game.id, game);\r\n}\r\n\r\nexport function getGame(id: string): Game | undefined {\r\n return registry.get(id);\r\n}\r\n\r\nexport function listGames(): Game[] {\r\n return Array.from(registry.values());\r\n}\r\n","import { Box, Text, useInput, render } from \"ink\";\r\nimport { useState, useEffect, useRef, useCallback } from \"react\";\r\n\r\nconst GAME_WIDTH = 40;\r\nconst GAME_HEIGHT = 18;\r\nconst PADDLE_WIDTH = 9;\r\nconst BRICK_COLORS = [166, 214, 76, 69];\r\n\r\ninterface Vec2 {\r\n x: number;\r\n y: number;\r\n}\r\n\r\ninterface Brick {\r\n x: number;\r\n y: number;\r\n w: number;\r\n alive: boolean;\r\n}\r\n\r\n// ─── 10 个关卡定义 ──────────────────────────────────────────────\r\n\r\ninterface LevelDef {\r\n rows: number;\r\n cols: number;\r\n bw: number; // 砖块宽度(格数)\r\n desc: string;\r\n // 返回 true 表示该位置有砖块\r\n pattern: (col: number, row: number, rows: number, cols: number) => boolean;\r\n}\r\n\r\nconst LEVELS: LevelDef[] = [\r\n { rows: 4, cols: 8, bw: 3, desc: \"经典 4×8\", pattern: () => true },\r\n { rows: 3, cols: 8, bw: 3, desc: \"轻松 3 层\", pattern: () => true },\r\n { rows: 6, cols: 8, bw: 3, desc: \"厚墙 6 层\", pattern: () => true },\r\n { rows: 4, cols: 8, bw: 3, desc: \"棋盘格\", pattern: (c, r) => (c + r) % 2 === 0 },\r\n { rows: 4, cols: 8, bw: 3, desc: \"金字塔\", pattern: (c, r, _, tc) => c >= r && c < tc - r },\r\n { rows: 4, cols: 8, bw: 3, desc: \"交错排列\", pattern: (c, r) => r % 2 === 0 || (c >= 1 && c <= 6) },\r\n { rows: 4, cols: 8, bw: 3, desc: \"中空边框\", pattern: (c, r, tr, tc) => r === 0 || r === tr - 1 || c === 0 || c === tc - 1 },\r\n { rows: 4, cols: 10, bw: 2, desc: \"密集 10 列\", pattern: () => true },\r\n { rows: 7, cols: 8, bw: 3, desc: \"高墙 7 层\", pattern: () => true },\r\n { rows: 8, cols: 8, bw: 3, desc: \"满屏 8 层\", pattern: () => true },\r\n];\r\n\r\nfunction getLevel(level: number): LevelDef {\r\n return LEVELS[(level - 1) % LEVELS.length]!;\r\n}\r\n\r\nfunction createBricks(level: number): Brick[] {\r\n const def = getLevel(level);\r\n const bricks: Brick[] = [];\r\n const gap = 2;\r\n const totalW = def.cols * def.bw + (def.cols - 1) * gap;\r\n const startX = Math.floor((GAME_WIDTH - totalW) / 2);\r\n const step = def.bw + gap;\r\n\r\n for (let row = 0; row < def.rows; row++) {\r\n for (let col = 0; col < def.cols; col++) {\r\n if (def.pattern(col, row, def.rows, def.cols)) {\r\n bricks.push({\r\n x: startX + col * step,\r\n y: 2 + row * 2,\r\n w: def.bw,\r\n alive: true,\r\n });\r\n }\r\n }\r\n }\r\n return bricks;\r\n}\r\n\r\n// ─── 游戏状态 ──────────────────────────────────────────────────\r\n\r\ninterface GameState {\r\n level: number;\r\n bricks: Brick[];\r\n paddleX: number;\r\n ball: Vec2;\r\n ballDir: Vec2;\r\n score: number;\r\n lives: number;\r\n gameOver: boolean;\r\n win: boolean;\r\n paused: boolean;\r\n}\r\n\r\nfunction createInitialState(level: number): GameState {\r\n const def = getLevel(level);\r\n const totalW = def.cols * def.bw + (def.cols - 1) * 2;\r\n const _startX = Math.floor((GAME_WIDTH - totalW) / 2);\r\n return {\r\n level,\r\n bricks: createBricks(level),\r\n paddleX: Math.floor(GAME_WIDTH / 2) - Math.floor(PADDLE_WIDTH / 2),\r\n ball: { x: GAME_WIDTH / 2, y: GAME_HEIGHT - 3 },\r\n ballDir: { x: 1, y: -1 },\r\n score: 0,\r\n lives: 3,\r\n gameOver: false,\r\n win: false,\r\n paused: false,\r\n };\r\n}\r\n\r\n// ─── 物理更新 ──────────────────────────────────────────────────\r\n\r\nfunction update(state: GameState): void {\r\n if (state.paused || state.gameOver || state.win) return;\r\n\r\n state.ball.x += state.ballDir.x;\r\n state.ball.y += state.ballDir.y;\r\n\r\n if (state.ball.x <= 0) { state.ball.x = 0; state.ballDir.x = 1; }\r\n if (state.ball.x >= GAME_WIDTH - 1) { state.ball.x = GAME_WIDTH - 1; state.ballDir.x = -1; }\r\n if (state.ball.y <= 0) { state.ball.y = 0; state.ballDir.y = 1; }\r\n\r\n // 挡板碰撞\r\n if (\r\n state.ball.y === GAME_HEIGHT - 1 &&\r\n state.ball.x >= state.paddleX &&\r\n state.ball.x <= state.paddleX + PADDLE_WIDTH\r\n ) {\r\n state.ballDir.y = -1;\r\n const hitPos = (state.ball.x - state.paddleX) / PADDLE_WIDTH;\r\n state.ballDir.x = hitPos < 0.5 ? -1 : 1;\r\n }\r\n\r\n // 出界\r\n if (state.ball.y > GAME_HEIGHT) {\r\n state.lives--;\r\n if (state.lives <= 0) {\r\n state.gameOver = true;\r\n } else {\r\n state.ball = { x: GAME_WIDTH / 2, y: GAME_HEIGHT - 3 };\r\n state.ballDir = { x: 1, y: -1 };\r\n state.paddleX = Math.floor(GAME_WIDTH / 2) - Math.floor(PADDLE_WIDTH / 2);\r\n }\r\n }\r\n\r\n // 砖块碰撞\r\n const hitBrick = state.bricks.find((b) => {\r\n if (!b.alive) return false;\r\n return state.ball.x >= b.x && state.ball.x < b.x + b.w && state.ball.y >= b.y && state.ball.y < b.y + 1;\r\n });\r\n\r\n if (hitBrick) {\r\n hitBrick.alive = false;\r\n state.score += 10;\r\n state.ballDir.y = -state.ballDir.y;\r\n }\r\n\r\n if (state.bricks.every((b) => !b.alive)) state.win = true;\r\n}\r\n\r\n// ─── 画面渲染(含 ANSI 颜色) ─────────────────────────────────\r\n\r\nfunction buildBoard(state: GameState): string {\r\n const lines: string[] = [];\r\n\r\n function brickColorIndex(x: number, y: number): number | undefined {\r\n const row = Math.floor((y - 2) / 2);\r\n if (row >= 0) {\r\n const b = state.bricks.find((br) => br.y === y && br.alive && x >= br.x && x < br.x + br.w);\r\n if (b) return row % BRICK_COLORS.length;\r\n }\r\n return undefined;\r\n }\r\n\r\n for (let y = 0; y < GAME_HEIGHT; y++) {\r\n let line = \"\";\r\n for (let x = 0; x < GAME_WIDTH; x++) {\r\n const isBall = state.ball.x === x && state.ball.y === y;\r\n const isPaddle = y === GAME_HEIGHT - 1 && x >= state.paddleX && x < state.paddleX + PADDLE_WIDTH;\r\n const brickRow = brickColorIndex(x, y);\r\n\r\n if (isBall) {\r\n line += \"\\x1b[97m●\\x1b[0m\";\r\n } else if (isPaddle) {\r\n line += \"\\x1b[94m▄\\x1b[0m\";\r\n } else if (brickRow !== undefined) {\r\n line += `\\x1b[38;5;${BRICK_COLORS[brickRow]}m▀\\x1b[0m`;\r\n } else {\r\n line += \" \";\r\n }\r\n }\r\n lines.push(line);\r\n }\r\n\r\n return lines.map((l) => `│${l}│`).join(\"\\n\");\r\n}\r\n\r\n// ─── Ink 组件 ──────────────────────────────────────────────────\r\n\r\ninterface BrickBreakerGameProps {\r\n onExit: () => void;\r\n}\r\n\r\nfunction BrickBreakerGame({ onExit: _onExit }: BrickBreakerGameProps) {\r\n const [initialLevel, setInitialLevel] = useState(1);\r\n const [selectingLevel, setSelectingLevel] = useState(true);\r\n const stateRef = useRef<GameState>(createInitialState(initialLevel));\r\n const [tick, setTick] = useState(0);\r\n const onExitRef = useRef(_onExit);\r\n onExitRef.current = _onExit;\r\n\r\n useEffect(() => {\r\n if (selectingLevel) return;\r\n const interval = setInterval(() => {\r\n update(stateRef.current);\r\n setTick((t) => t + 1);\r\n }, 80);\r\n return () => clearInterval(interval);\r\n }, [selectingLevel]);\r\n\r\n // 重新开始(保留当前关卡)\r\n const restart = useCallback((level?: number) => {\r\n const lv = level ?? stateRef.current.level;\r\n stateRef.current = createInitialState(lv);\r\n setInitialLevel(lv);\r\n setSelectingLevel(false);\r\n setTick(0);\r\n }, []);\r\n\r\n // 选关\r\n const startLevelSelect = useCallback(() => {\r\n setSelectingLevel(true);\r\n }, []);\r\n\r\n useInput(\r\n useCallback((input, key) => {\r\n const s = stateRef.current;\r\n\r\n // 选关模式\r\n if (selectingLevel) {\r\n if (input >= \"1\" && input <= \"9\") {\r\n restart(Number(input));\r\n } else if (input === \"0\") {\r\n restart(10);\r\n } else if (key.escape || input === \"q\") {\r\n onExitRef.current();\r\n }\r\n return;\r\n }\r\n\r\n if (key.leftArrow) {\r\n s.paddleX = Math.max(0, s.paddleX - 1);\r\n setTick((t) => t + 1);\r\n } else if (key.rightArrow) {\r\n s.paddleX = Math.min(GAME_WIDTH - PADDLE_WIDTH, s.paddleX + 1);\r\n setTick((t) => t + 1);\r\n } else if (input === \"p\" || input === \" \") {\r\n s.paused = !s.paused;\r\n } else if (input === \"r\") {\r\n if (s.gameOver || s.win) restart();\r\n } else if (input === \"l\") {\r\n if (s.gameOver || s.win) startLevelSelect();\r\n } else if (input === \"q\" || key.escape) {\r\n onExitRef.current();\r\n }\r\n }, [selectingLevel, restart, startLevelSelect]),\r\n );\r\n\r\n const s = stateRef.current;\r\n const aliveCount = s.bricks.filter((b) => b.alive).length;\r\n const board = buildBoard(s);\r\n const def = getLevel(s.level);\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n void tick;\r\n\r\n return (\r\n <Box flexDirection=\"column\">\r\n {/* 状态栏 */}\r\n <Box flexDirection=\"row\">\r\n <Box width={20}>\r\n <Text>\r\n 关卡 {s.level}: <Text color=\"cyan\">{def.desc}</Text>\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text>\r\n 分数: <Text color=\"yellow\">{String(s.score).padStart(3, \"0\")}</Text>\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text>\r\n 生命: <Text color=\"red\">{\"♥\".repeat(Math.max(0, s.lives))}</Text>\r\n </Text>\r\n </Box>\r\n <Box width={10}>\r\n <Text>\r\n 砖块: <Text color=\"cyan\">{aliveCount}</Text>\r\n </Text>\r\n </Box>\r\n <Box>\r\n <Text color={s.paused ? \"gray\" : \"green\"}>\r\n [{s.paused ? \"暂停\" : \"运行中\"}]\r\n </Text>\r\n </Box>\r\n </Box>\r\n\r\n {/* 游戏画面 */}\r\n <Box flexDirection=\"column\">\r\n <Text>┌{\"─\".repeat(GAME_WIDTH)}┐</Text>\r\n <Text>{selectingLevel ? \" \\x1b[90m选择关卡后开始...\\x1b[0m\" : board}</Text>\r\n <Text>└{\"─\".repeat(GAME_WIDTH)}┘</Text>\r\n </Box>\r\n\r\n {/* 选关界面 */}\r\n {selectingLevel && (\r\n <Box marginTop={1} flexDirection=\"column\">\r\n <Text bold color=\"yellow\">选择关卡</Text>\r\n <Box flexDirection=\"row\" flexWrap=\"wrap\">\r\n {LEVELS.map((lv, i) => (\r\n <Box key={i} width={22}>\r\n <Text>\r\n <Text color={initialLevel === i + 1 ? \"green\" : \"white\"}>\r\n {i + 1 === 10 ? \"0\" : String(i + 1)}\r\n </Text>\r\n . {lv.desc} ({lv.rows}×{lv.cols})\r\n </Text>\r\n </Box>\r\n ))}\r\n </Box>\r\n <Box marginTop={1}>\r\n <Text dimColor>按数字选关 q 退出</Text>\r\n </Box>\r\n </Box>\r\n )}\r\n\r\n {/* 结束/通关信息 */}\r\n {!selectingLevel && (s.gameOver || s.win) && (\r\n <Box marginTop={1}>\r\n <Text bold color={s.gameOver ? \"red\" : \"green\"}>\r\n {s.gameOver ? \"游戏结束!\" : \"恭喜通关!\"}\r\n </Text>\r\n <Text>\r\n {\" 分数: \"}<Text color=\"yellow\">{s.score}</Text>\r\n </Text>\r\n </Box>\r\n )}\r\n\r\n {/* 操作提示 */}\r\n {!selectingLevel && (\r\n <Box marginTop={1}>\r\n {s.gameOver || s.win ? (\r\n <Text dimColor>\r\n {\"← → 移动 r 重开 l 选关 q 退出\"}\r\n </Text>\r\n ) : (\r\n <Text dimColor>\r\n {\"← → 移动 p 暂停 q 退出\"}\r\n </Text>\r\n )}\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}\r\n\r\n// ─── 导出 ──────────────────────────────────────────────────────\r\n\r\nexport default {\r\n id: \"brick-breaker\",\r\n name: \"Brick Breaker\",\r\n description: \"经典打砖块游戏,10 个关卡可选!\",\r\n play: async () => {\r\n await new Promise<void>((resolve) => {\r\n const { unmount } = render(\r\n <BrickBreakerGame onExit={() => { unmount(); resolve(); }} />,\r\n );\r\n });\r\n },\r\n};\r\n","import { Box, Text, useInput, render } from \"ink\";\r\nimport { useState, useEffect, useRef, useCallback } from \"react\";\r\n\r\nconst GAME_W = 66;\r\nconst GAME_H = 20; // 6 行分数 + 14 行掉落单词\r\nconst SCORE_H = 6;\r\n// const WORD_H = 14;\r\nconst MAX_WORDS = 10;\r\nconst CYBER_PALETTE = [\"#00ffff\", \"#ff00ff\", \"#00ff41\", \"#ff1493\", \"#8b00ff\"];\r\n\r\n// 3×5 像素大号数字(每个像素 = ██,每数字 6 列)\r\nconst DIGIT_ART: Record<string, string[]> = {\r\n \"0\": [\" ██████ \", \"██ ██\", \"██ ██\", \"██ ██\", \" ██████ \"],\r\n \"1\": [\" ██ \", \" ████ \", \" ██ \", \" ██ \", \" ██████ \"],\r\n \"2\": [\" ██████ \", \" ██ \", \" ██████ \", \" ██ \", \" ██████ \"],\r\n \"3\": [\" ██████ \", \" ██ \", \" ██████ \", \" ██ \", \" ██████ \"],\r\n \"4\": [\"██ ██\", \"██ ██\", \" ██████ \", \" ██ \", \" ██ \"],\r\n \"5\": [\" ██████ \", \" ██ \", \" ██████ \", \" ██ \", \" ██████ \"],\r\n \"6\": [\" ██████ \", \" ██ \", \" ██████ \", \" ██ ██\", \" ██████ \"],\r\n \"7\": [\" ██████ \", \" ██ \", \" ██ \", \" ██ \", \" ██ \"],\r\n \"8\": [\" ██████ \", \"██ ██\", \" ██████ \", \"██ ██\", \" ██████ \"],\r\n \"9\": [\" ██████ \", \"██ ██\", \" ██████ \", \" ██ \", \" ██████ \"],\r\n};\r\n\r\nfunction buildScoreLines(scoreStr: string): string[] {\r\n const lines = [\"\", \"\", \"\", \"\", \"\"];\r\n for (const ch of scoreStr) {\r\n const art = DIGIT_ART[ch] ?? DIGIT_ART[\"0\"]!;\r\n for (let r = 0; r < 5; r++) {\r\n lines[r] += \" \" + (art[r] ?? \"\");\r\n }\r\n }\r\n // 左右各留 6 格间距\r\n const pad = 6;\r\n return lines.map((l) => \" \".repeat(pad) + l);\r\n}\r\n\r\nfunction hexToRgb(hex: string): string {\r\n const r = parseInt(hex.slice(1, 3), 16);\r\n const g = parseInt(hex.slice(3, 5), 16);\r\n const b = parseInt(hex.slice(5, 7), 16);\r\n return `${r};${g};${b}`;\r\n}\r\n\r\n// ─── 200 个常见单词 ────────────────────────────────────────────\r\n\r\nconst WORDS_BANK = [\r\n // ── 框架 & 库 ──\r\n \"react\", \"vue\", \"next\", \"node\", \"axios\", \"express\", \"lodash\", \"jquery\",\r\n \"webpack\", \"vite\", \"babel\", \"eslint\", \"prettier\", \"tailwind\", \"bootstrap\",\r\n \"sass\", \"less\", \"postcss\", \"redux\", \"pinia\", \"vuex\", \"router\",\r\n \"nestjs\", \"socket\", \"graphql\", \"rest\", \"grpc\", \"prisma\", \"typeorm\",\r\n\r\n // ── 前端 ──\r\n \"html\", \"css\", \"jsx\", \"tsx\", \"dom\", \"spa\", \"ssr\", \"csr\", \"pwa\",\r\n \"component\", \"prop\", \"hook\", \"composable\", \"directive\", \"filter\", \"mixin\",\r\n \"template\", \"render\", \"virtual\", \"diff\", \"patch\", \"hydration\",\r\n \"responsive\", \"flexbox\", \"grid\", \"animation\", \"transition\",\r\n\r\n // ── 后端 ──\r\n \"api\", \"route\", \"middleware\", \"controller\", \"service\", \"module\",\r\n \"dto\", \"entity\", \"schema\", \"migration\", \"seeder\", \"factory\",\r\n \"auth\", \"jwt\", \"oauth\", \"session\", \"cookie\", \"token\", \"cors\",\r\n \"cache\", \"redis\", \"mq\", \"rabbit\", \"kafka\", \"nats\",\r\n\r\n // ── 数据库 ──\r\n \"sql\", \"mysql\", \"pg\", \"sqlite\", \"mongo\", \"redis\", \"orm\",\r\n \"table\", \"index\", \"query\", \"join\", \"union\", \"group\", \"order\",\r\n \"where\", \"having\", \"limit\", \"offset\", \"insert\", \"update\", \"delete\",\r\n\r\n // ── DevOps ──\r\n \"docker\", \"nginx\", \"linux\", \"bash\", \"shell\", \"yaml\", \"toml\",\r\n \"ci\", \"cd\", \"deploy\", \"rollback\", \"release\", \"build\", \"test\",\r\n \"lint\", \"format\", \"stage\", \"commit\", \"branch\", \"merge\", \"rebase\",\r\n\r\n // ── 数据结构 & 算法 ──\r\n \"array\", \"stack\", \"queue\", \"tree\", \"graph\", \"list\", \"map\", \"set\",\r\n \"sort\", \"search\", \"filter\", \"reduce\", \"map\", \"async\", \"await\",\r\n \"promise\", \"callback\", \"closure\", \"proxy\", \"reflect\", \"decorator\",\r\n\r\n // ── 常用操作 ──\r\n \"create\", \"read\", \"update\", \"delete\", \"crud\", \"parse\", \"stringify\",\r\n \"encode\", \"decode\", \"transform\", \"validate\", \"format\", \"parse\",\r\n \"upload\", \"download\", \"export\", \"import\", \"backup\", \"restore\",\r\n\r\n // ── 类型 & 变量 ──\r\n \"string\", \"number\", \"boolean\", \"object\", \"array\", \"tuple\", \"enum\",\r\n \"interface\", \"type\", \"class\", \"function\", \"method\", \"property\",\r\n \"public\", \"private\", \"static\", \"readonly\", \"optional\", \"abstract\",\r\n \"const\", \"let\", \"var\", \"void\", \"null\", \"undef\", \"never\", \"any\",\r\n\r\n // ── 补充 ──\r\n \"config\", \"logger\", \"monitor\", \"metric\", \"alert\", \"webhook\",\r\n \"endpoint\", \"payload\", \"header\", \"status\", \"timeout\", \"retry\",\r\n \"fallback\", \"circuit\", \"breaker\", \"throttle\", \"debounce\",\r\n \"scroll\", \"resize\", \"click\", \"hover\", \"focus\", \"blur\",\r\n];\r\n\r\nfunction randomWord(used: Set<string>): string {\r\n let w: string;\r\n do { w = WORDS_BANK[Math.floor(Math.random() * WORDS_BANK.length)]!; }\r\n while (used.has(w));\r\n return w;\r\n}\r\n\r\n// ─── 游戏状态 ──────────────────────────────────────────────────\r\n\r\ninterface DropWord {\r\n text: string;\r\n row: number;\r\n col: number;\r\n}\r\n\r\ninterface GameState {\r\n words: DropWord[];\r\n score: number;\r\n lives: number;\r\n speed: number;\r\n spawnTimer: number;\r\n gameOver: boolean;\r\n paused: boolean;\r\n typed: string;\r\n target: string | null;\r\n combo: number;\r\n message: string;\r\n messageTimer: number;\r\n usedWords: Set<string>;\r\n}\r\n\r\nfunction createInitialState(): GameState {\r\n return {\r\n words: [],\r\n score: 0,\r\n lives: 3,\r\n speed: 0.3,\r\n spawnTimer: 0,\r\n gameOver: false,\r\n paused: false,\r\n typed: \"\",\r\n target: null,\r\n combo: 0,\r\n message: \"\",\r\n messageTimer: 0,\r\n usedWords: new Set(),\r\n };\r\n}\r\n\r\nfunction pickTarget(s: GameState): string | null {\r\n // 选取最靠左的存活单词作为目标\r\n let best: DropWord | null = null;\r\n for (const w of s.words) {\r\n if (!best || w.col < best.col) best = w;\r\n }\r\n return best?.text ?? null;\r\n}\r\n\r\n// ─── 物理更新 ──────────────────────────────────────────────────\r\n\r\nfunction update(s: GameState): void {\r\n if (s.paused || s.gameOver) return;\r\n\r\n // 生成新单词\r\n s.spawnTimer++;\r\n if (s.spawnTimer >= Math.max(20, 50 - Math.floor(s.speed * 15))) {\r\n s.spawnTimer = 0;\r\n if (s.words.length < MAX_WORDS) {\r\n const used = new Set(s.usedWords);\r\n for (const w of s.words) used.add(w.text);\r\n const text = randomWord(used);\r\n // 找空白行\r\n const usedRows = new Set(s.words.map((w) => w.row));\r\n let row = -1;\r\n for (let r = SCORE_H; r < GAME_H; r++) {\r\n if (!usedRows.has(r)) { row = r; break; }\r\n }\r\n if (row >= 0) {\r\n s.words.push({ text, row, col: GAME_W - 1 });\r\n s.usedWords.add(text);\r\n }\r\n }\r\n }\r\n\r\n // 移动单词\r\n for (const w of s.words) {\r\n w.col -= s.speed;\r\n }\r\n\r\n // 移除超出左边的 + 更新目标\r\n const before = s.words.length;\r\n s.words = s.words.filter((w) => w.col > -w.text.length);\r\n const removed = before - s.words.length;\r\n\r\n // 单词漏掉了 → 扣命\r\n if (removed > 0) {\r\n s.lives -= removed;\r\n s.typed = \"\";\r\n s.target = pickTarget(s);\r\n if (s.lives <= 0) {\r\n s.gameOver = true;\r\n s.words = [];\r\n }\r\n }\r\n\r\n // 更新目标\r\n s.target = pickTarget(s);\r\n\r\n // 消息计时\r\n if (s.messageTimer > 0) {\r\n s.messageTimer--;\r\n if (s.messageTimer <= 0) s.message = \"\";\r\n }\r\n}\r\n\r\n// ─── 画面渲染 ──────────────────────────────────────────────────\r\n\r\nfunction buildGameView(s: GameState, scoreLines: string[], scoreColor: string, message: string): string[] {\r\n const rows: string[] = [];\r\n\r\n for (let y = 0; y < GAME_H; y++) {\r\n let line = \"\";\r\n\r\n // 分数区域(前 6 行)\r\n if (y < SCORE_H) {\r\n if (y < 5) {\r\n // 数字行 — 先补足宽度再套赛博朋克色\r\n const raw = (scoreLines[y] ?? \"\").padEnd(GAME_W);\r\n line = `\\x1b[38;2;${hexToRgb(scoreColor)}m${raw}\\x1b[0m`;\r\n } else if (y === 5) {\r\n // 第 6 行:连击 / 暂停\r\n if (s.combo >= 3) {\r\n const comboText = `${s.combo}连击!`;\r\n const pad = Math.floor((GAME_W - comboText.length) / 2);\r\n const comboColor = CYBER_PALETTE[s.combo % CYBER_PALETTE.length]!;\r\n const raw = \" \".repeat(pad) + comboText + \" \".repeat(GAME_W - pad - comboText.length);\r\n line = `\\x1b[38;2;${hexToRgb(comboColor)}m${raw}\\x1b[0m`;\r\n } else if (s.paused) {\r\n const pauseText = \"暂停\";\r\n const pad = Math.floor((GAME_W - pauseText.length) / 2);\r\n line = \" \".repeat(pad) + pauseText;\r\n line = line.padEnd(GAME_W);\r\n }\r\n }\r\n // 补全到 GAME_W 宽度\r\n line = line.padEnd(GAME_W);\r\n } else {\r\n // 单词区域\r\n // 第 1 行显示消息(如果有)\r\n if (y === SCORE_H && message) {\r\n const pad = Math.floor((GAME_W - message.length) / 2);\r\n const raw = \" \".repeat(pad) + message + \" \".repeat(GAME_W - pad - message.length);\r\n const msgColor = CYBER_PALETTE[Math.floor(Math.random() * CYBER_PALETTE.length)]!;\r\n line = `\\x1b[38;2;${hexToRgb(msgColor)}m${raw}\\x1b[0m`;\r\n } else {\r\n for (let x = 0; x < GAME_W; x++) {\r\n const word = s.words.find((w) => {\r\n const charIdx = x - Math.floor(w.col);\r\n return charIdx >= 0 && charIdx < w.text.length && w.row === y;\r\n });\r\n if (word) {\r\n const charIdx = x - Math.floor(word.col);\r\n const ch = word.text[charIdx]!;\r\n const isTarget = word.text === s.target;\r\n const typedIdx = s.target === word.text ? s.typed.length : 0;\r\n const isTyped = isTarget && charIdx < typedIdx;\r\n if (isTarget) {\r\n line += isTyped ? `\\x1b[92m${ch}\\x1b[0m` : `\\x1b[97m${ch}\\x1b[0m`;\r\n } else {\r\n line += `\\x1b[90m${ch}\\x1b[0m`;\r\n }\r\n } else {\r\n line += \" \";\r\n }\r\n }\r\n }\r\n }\r\n\r\n rows.push(line);\r\n }\r\n return rows;\r\n}\r\n\r\n// ─── Ink 组件 ──────────────────────────────────────────────────\r\n\r\ninterface CoderCheckProps {\r\n onExit: () => void;\r\n}\r\n\r\nfunction CoderCheck({ onExit: _onExit }: CoderCheckProps) {\r\n const stateRef = useRef<GameState>(createInitialState());\r\n const [tick, setTick] = useState(0);\r\n const [colorOffset, setColorOffset] = useState(0);\r\n const onExitRef = useRef(_onExit);\r\n onExitRef.current = _onExit;\r\n\r\n // 赛博朋克颜色动画\r\n useEffect(() => {\r\n const timer = setInterval(() => {\r\n setColorOffset((prev) => (prev + 1) % CYBER_PALETTE.length);\r\n }, 400);\r\n return () => clearInterval(timer);\r\n }, []);\r\n\r\n // 游戏循环\r\n useEffect(() => {\r\n const interval = setInterval(() => {\r\n update(stateRef.current);\r\n setTick((t) => t + 1);\r\n }, 60);\r\n return () => clearInterval(interval);\r\n }, []);\r\n\r\n useInput(\r\n useCallback((input, key) => {\r\n const s = stateRef.current;\r\n if (s.gameOver) {\r\n if (input === \"r\") {\r\n stateRef.current = createInitialState();\r\n setTick(0);\r\n } else if (input === \"q\" || key.escape) {\r\n onExitRef.current();\r\n }\r\n return;\r\n }\r\n\r\n if ((input === \"p\" && key.ctrl) || input === \" \") {\r\n s.paused = !s.paused;\r\n return;\r\n }\r\n if ((input === \"q\" && key.ctrl) || key.escape) {\r\n onExitRef.current();\r\n return;\r\n }\r\n\r\n if (s.paused) return;\r\n\r\n // 打字输入\r\n if (input.length === 1 && input >= \"a\" && input <= \"z\") {\r\n // 选取目标\r\n if (!s.target) {\r\n s.target = pickTarget(s);\r\n }\r\n if (s.target) {\r\n const nextChar = s.target[s.typed.length];\r\n if (nextChar === input) {\r\n s.typed += input;\r\n // 检查是否完成\r\n if (s.typed === s.target) {\r\n // 移除该单词\r\n s.words = s.words.filter((w) => w.text !== s.target);\r\n s.combo++;\r\n // 连击 >= 3 时以当前分数翻倍奖励\r\n const prevScore = s.score;\r\n const basePts = s.target.length;\r\n const comboBonus = s.combo >= 3 ? s.score : 0;\r\n s.score += basePts + comboBonus;\r\n s.speed = s.speed + 0.02;\r\n\r\n // 每 500 分提速\r\n const prevMilestone = Math.floor(prevScore / 500);\r\n const newMilestone = Math.floor(s.score / 500);\r\n if (newMilestone > prevMilestone) {\r\n s.speed += 0.15;\r\n }\r\n\r\n // 超过 99999 分提示通关\r\n if (s.score >= 100000 && prevScore < 100000) {\r\n s.message = \"恭喜通关! 难度最大化!\";\r\n s.messageTimer = 100;\r\n }\r\n s.typed = \"\";\r\n s.target = pickTarget(s);\r\n }\r\n } else {\r\n // 打错字符,连击中断\r\n s.combo = 0;\r\n }\r\n }\r\n }\r\n }, []),\r\n );\r\n\r\n const s = stateRef.current;\r\n const scoreColor = CYBER_PALETTE[colorOffset]!;\r\n const scoreStr = String(s.score).padStart(5, \"0\");\r\n const scoreLines = buildScoreLines(scoreStr);\r\n const view = buildGameView(s, scoreLines, scoreColor, s.message);\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n void tick;\r\n\r\n return (\r\n <Box flexDirection=\"column\" paddingX={1}>\r\n {/* 顶栏:生命 + 速度 */}\r\n <Box flexDirection=\"row\">\r\n <Text>\r\n 生命 <Text color=\"red\">{\"♥\".repeat(Math.max(0, s.lives))}</Text>\r\n {\" \"}速度 <Text color=\"cyan\">Lv.{Math.floor(s.speed * 10)}</Text>\r\n </Text>\r\n </Box>\r\n\r\n {/* 目标单词提示 */}\r\n {!s.gameOver && s.target && (\r\n <Box marginTop={1}>\r\n <Text>\r\n 打字: <Text color=\"green\">{s.typed}</Text>\r\n <Text color=\"white\">{s.target.slice(s.typed.length)}</Text>\r\n </Text>\r\n </Box>\r\n )}\r\n\r\n {/* 游戏画面(含分数) */}\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n <Text>┌{\"─\".repeat(GAME_W)}┐</Text>\r\n {view.map((row, i) => (\r\n <Text key={i}>{`│${row}│`}</Text>\r\n ))}\r\n <Text>└{\"─\".repeat(GAME_W)}┘</Text>\r\n </Box>\r\n\r\n {/* 游戏结束 */}\r\n {s.gameOver && (\r\n <Box marginTop={1}>\r\n <Text bold color=\"red\">\r\n 游戏结束!\r\n </Text>\r\n <Text>\r\n {\" 得分: \"}<Text color=\"yellow\">{s.score}</Text>\r\n {\" r 重开 q 退出\"}\r\n </Text>\r\n </Box>\r\n )}\r\n\r\n {/* 操作提示 */}\r\n <Box marginTop={1}>\r\n <Text dimColor>\r\n {\"打字消除单词 空格/Ctrl+P暂停 Ctrl+Q退出\"}\r\n </Text>\r\n </Box>\r\n </Box>\r\n );\r\n}\r\n\r\n// ─── 导出 ──────────────────────────────────────────────────────\r\n\r\nexport default {\r\n id: \"coder-check\",\r\n name: \"Coder Check\",\r\n description: \"极速打字游戏,输入单词消除它们!\",\r\n play: async () => {\r\n await new Promise<void>((resolve) => {\r\n const { unmount } = render(\r\n <CoderCheck onExit={() => { unmount(); resolve(); }} />,\r\n );\r\n });\r\n },\r\n};\r\n","import type { Direction, GameState, GameConfig, Food, Point } from './types.js';\r\n\r\n// ─── 辅助函数 ───────────────────────────────────────────\r\n\r\nfunction isOpposite(a: Direction, b: Direction): boolean {\r\n return (\r\n (a === 'UP' && b === 'DOWN') ||\r\n (a === 'DOWN' && b === 'UP') ||\r\n (a === 'LEFT' && b === 'RIGHT') ||\r\n (a === 'RIGHT' && b === 'LEFT')\r\n );\r\n}\r\n\r\nfunction samePoint(a: Point, b: Point): boolean {\r\n return a.x === b.x && a.y === b.y;\r\n}\r\n\r\nfunction movePoint(p: Point, dir: Direction): Point {\r\n switch (dir) {\r\n case 'UP': return { x: p.x, y: p.y - 1 };\r\n case 'DOWN': return { x: p.x, y: p.y + 1 };\r\n case 'LEFT': return { x: p.x - 1, y: p.y };\r\n case 'RIGHT': return { x: p.x + 1, y: p.y };\r\n }\r\n}\r\n\r\nfunction wrapPoint(p: Point, config: GameConfig): Point {\r\n return {\r\n x: ((p.x % config.width) + config.width) % config.width,\r\n y: ((p.y % config.height) + config.height) % config.height,\r\n };\r\n}\r\n\r\n// ─── 食物生成 ───────────────────────────────────────────\r\n\r\n/**\r\n * 在蛇身 + 指定排除位置之外的随机空位生成食物。\r\n * 排除位置用于防止普通食物和特殊食物重叠。\r\n */\r\nfunction generateFood(\r\n snake: Point[],\r\n config: GameConfig,\r\n type: 'normal' | 'special',\r\n excludePositions: Point[] = [],\r\n): Food | null {\r\n const occupied = new Set(snake.map(p => `${p.x},${p.y}`));\r\n // 排除位置也加入已占用集合(防止与另一种食物重叠)\r\n for (const p of excludePositions) {\r\n occupied.add(`${p.x},${p.y}`);\r\n }\r\n\r\n const totalCells = config.width * config.height;\r\n if (occupied.size >= totalCells) return null;\r\n\r\n let pos: Point;\r\n do {\r\n pos = {\r\n x: Math.floor(Math.random() * config.width),\r\n y: Math.floor(Math.random() * config.height),\r\n };\r\n } while (occupied.has(`${pos.x},${pos.y}`));\r\n\r\n return {\r\n position: pos,\r\n type,\r\n ...(type === 'special' ? { spawnedAt: Date.now() } : {}),\r\n };\r\n}\r\n\r\n// ─── 状态工厂 ───────────────────────────────────────────\r\n\r\n/** 创建初始游戏状态 */\r\nexport function createInitialState(config: GameConfig): GameState {\r\n const startX = Math.floor(config.width / 2);\r\n const startY = Math.floor(config.height / 2);\r\n\r\n const snake: Point[] = [\r\n { x: startX, y: startY },\r\n { x: startX - 1, y: startY },\r\n { x: startX - 2, y: startY },\r\n ];\r\n\r\n const food = generateFood(snake, config, 'normal')!;\r\n\r\n return {\r\n snake,\r\n direction: 'RIGHT',\r\n nextDirection: 'RIGHT',\r\n food,\r\n specialFood: null,\r\n score: 0,\r\n speedLevel: 1,\r\n isGameOver: false,\r\n isPaused: false,\r\n wallWrap: Math.random() < 0.5,\r\n tickInterval: config.initialSpeed,\r\n };\r\n}\r\n\r\n// ─── 核心逻辑:每帧推进 ─────────────────────────────────\r\n\r\nexport function tick(state: GameState, config: GameConfig): void {\r\n // 1. 应用缓冲方向(防止同帧内 180° 掉头)\r\n state.direction = state.nextDirection;\r\n\r\n // 2. 计算新蛇头位置\r\n const head = state.snake[0]!;\r\n let newHead = movePoint(head, state.direction);\r\n\r\n // 3. 边界处理\r\n const outOfBounds =\r\n newHead.x < 0 ||\r\n newHead.x >= config.width ||\r\n newHead.y < 0 ||\r\n newHead.y >= config.height;\r\n\r\n if (outOfBounds) {\r\n if (state.wallWrap) {\r\n newHead = wrapPoint(newHead, config);\r\n } else {\r\n state.isGameOver = true;\r\n return;\r\n }\r\n }\r\n\r\n // 4. 判断本帧是否会吃到食物(影响自碰撞检测的判定范围)\r\n const ateNormal = samePoint(newHead, state.food.position);\r\n const ateSpecial = state.specialFood !== null && samePoint(newHead, state.specialFood.position);\r\n const willEat = ateNormal || ateSpecial;\r\n\r\n // 自碰撞检测:不吃食物时排除蛇尾(因为蛇尾会移除)\r\n const bodyToCheck = willEat ? state.snake : state.snake.slice(0, -1);\r\n for (const segment of bodyToCheck) {\r\n if (samePoint(newHead, segment)) {\r\n state.isGameOver = true;\r\n return;\r\n }\r\n }\r\n\r\n // 5. 移动蛇\r\n state.snake.unshift(newHead);\r\n\r\n // 6. 处理进食\r\n if (ateNormal) {\r\n state.score += 1;\r\n updateSpeed(state, config);\r\n\r\n // 生成新普通食物时排除特殊食物的位置\r\n const exclude: Point[] = state.specialFood ? [state.specialFood.position] : [];\r\n const newFood = generateFood(state.snake, config, 'normal', exclude);\r\n if (newFood) {\r\n state.food = newFood;\r\n } else {\r\n // 没有空位了(蛇填满了棋盘),宣告胜利\r\n state.isGameOver = true;\r\n return;\r\n }\r\n\r\n // 25% 概率生成特殊食物,排除普通食物的位置\r\n if (state.specialFood === null && Math.random() < 0.25) {\r\n state.specialFood = generateFood(state.snake, config, 'special', [state.food.position]);\r\n }\r\n } else if (ateSpecial) {\r\n state.score += 3;\r\n updateSpeed(state, config);\r\n state.specialFood = null;\r\n } else {\r\n // 没吃到食物,移除蛇尾\r\n state.snake.pop();\r\n }\r\n\r\n // 7. 特殊食物倒计时(5 秒后消失)\r\n if (state.specialFood?.spawnedAt !== undefined) {\r\n if (Date.now() - state.specialFood.spawnedAt > 5000) {\r\n state.specialFood = null;\r\n }\r\n }\r\n}\r\n\r\n// ─── 速度更新 ───────────────────────────────────────────\r\n\r\nfunction updateSpeed(state: GameState, config: GameConfig): void {\r\n const newLevel = Math.floor(state.score / config.scorePerLevel) + 1;\r\n state.speedLevel = newLevel;\r\n state.tickInterval = Math.max(\r\n config.minSpeed,\r\n config.initialSpeed - (newLevel - 1) * config.speedDecrement,\r\n );\r\n}\r\n\r\n// ─── 方向变更(外部调用) ──────────────────────────────\r\n\r\n/**\r\n * 尝试改变方向。\r\n * 检查新方向是否与当前移动方向相反,防止 180° 掉头。\r\n */\r\nexport function changeDirection(state: GameState, newDir: Direction): void {\r\n if (!isOpposite(newDir, state.direction)) {\r\n state.nextDirection = newDir;\r\n }\r\n}\r\n","import chalk from 'chalk';\r\nimport type { GameState, GameConfig } from './types.js';\r\n\r\n// 每个格子在终端中占 2 个字符宽度,让网格看起来更接近正方形\r\nconst CELL_W = 2;\r\nconst EMPTY = ' ';\r\nconst BODY_CHAR = '▓';\r\nconst HEAD_CHAR = '▓';\r\nconst NORMAL_FOOD_CHAR = '◆';\r\nconst SPECIAL_FOOD_CHAR = '★';\r\n\r\n// ─── ANSI 控制序列 ──────────────────────────────────────\r\n\r\nconst CSI = '\\x1b[';\r\nconst CURSOR_HIDE = `${CSI}?25l`;\r\nconst CURSOR_SHOW = `${CSI}?25h`;\r\nconst CURSOR_HOME = `${CSI}H`;\r\n\r\n/** 隐藏终端光标 */\r\nexport function hideCursor(): void {\r\n process.stdout.write(CURSOR_HIDE);\r\n}\r\n\r\n/** 显示终端光标 */\r\nexport function showCursor(): void {\r\n process.stdout.write(CURSOR_SHOW);\r\n}\r\n\r\n/** 清屏并复位光标 */\r\nexport function clearScreen(): void {\r\n process.stdout.write(`${CSI}2J${CURSOR_HOME}`);\r\n}\r\n\r\n// ─── 着色辅助 ───────────────────────────────────────────\r\n\r\nfunction cellColor(char: string, type: 'snake-head' | 'snake-body' | 'normal-food' | 'special-food' | 'empty'): string {\r\n switch (type) {\r\n case 'snake-head': return chalk.greenBright(char);\r\n case 'snake-body': return chalk.green(char);\r\n case 'normal-food': return chalk.red(char);\r\n case 'special-food': return chalk.yellow(char);\r\n case 'empty': return char;\r\n }\r\n}\r\n\r\n// ─── 主渲染函数 ─────────────────────────────────────────\r\n\r\n/**\r\n * 将当前游戏状态渲染到终端。\r\n * 先移动光标到左上角再逐行覆盖,避免闪烁。\r\n */\r\nexport function renderToTerminal(state: GameState, config: GameConfig): void {\r\n const lines = buildRenderLines(state, config);\r\n // 先清屏再回左上角绘制,避免状态切换/终端滚动时旧行残留导致重叠\r\n process.stdout.write(`${CSI}2J${CURSOR_HOME}` + lines.join('\\n'));\r\n}\r\n\r\nfunction buildRenderLines(state: GameState, config: GameConfig): string[] {\r\n const { width: W, height: H } = config;\r\n const lines: string[] = [];\r\n\r\n // ── 构建网格数据 ──\r\n // grid[y][x] 存储该位置要显示的内容类型\r\n const grid: string[][] = Array.from({ length: H }, () => Array(W).fill('empty'));\r\n\r\n // 蛇身(除蛇头外)\r\n for (let i = 1; i < state.snake.length; i++) {\r\n const seg = state.snake[i]!;\r\n if (seg.y >= 0 && seg.y < H && seg.x >= 0 && seg.x < W) {\r\n grid[seg.y]![seg.x] = 'snake-body';\r\n }\r\n }\r\n\r\n // 蛇头(覆盖蛇身标记,确保蛇头颜色优先)\r\n const head = state.snake[0]!;\r\n if (head.y >= 0 && head.y < H && head.x >= 0 && head.x < W) {\r\n grid[head.y]![head.x] = 'snake-head';\r\n }\r\n\r\n // 普通食物\r\n const fp = state.food.position;\r\n if (grid[fp.y]![fp.x] === 'empty') {\r\n grid[fp.y]![fp.x] = 'normal-food';\r\n }\r\n\r\n // 特殊食物\r\n if (state.specialFood) {\r\n const sp = state.specialFood.position;\r\n if (grid[sp.y]![sp.x] === 'empty') {\r\n grid[sp.y]![sp.x] = 'special-food';\r\n }\r\n }\r\n\r\n // ── 顶部边框 ──\r\n lines.push(' ' + chalk.gray('╔' + '══'.repeat(W) + '╗'));\r\n\r\n // ── 每行渲染 ──\r\n for (let y = 0; y < H; y++) {\r\n let row = ' ' + chalk.gray('║');\r\n for (let x = 0; x < W; x++) {\r\n const cellType = grid[y]![x]!;\r\n let char: string;\r\n switch (cellType) {\r\n case 'snake-head':\r\n char = HEAD_CHAR.repeat(CELL_W);\r\n break;\r\n case 'snake-body':\r\n char = BODY_CHAR.repeat(CELL_W);\r\n break;\r\n case 'normal-food':\r\n char = NORMAL_FOOD_CHAR + ' ';\r\n break;\r\n case 'special-food':\r\n char = SPECIAL_FOOD_CHAR + ' ';\r\n break;\r\n default:\r\n char = EMPTY;\r\n }\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n row += cellColor(char, cellType as unknown as 'snake-head' | 'snake-body' | 'normal-food' | 'special-food' | 'empty');\r\n }\r\n row += chalk.gray('║');\r\n lines.push(row);\r\n }\r\n\r\n // ── 底部边框 ──\r\n lines.push(' ' + chalk.gray('╚' + '══'.repeat(W) + '╝'));\r\n\r\n // ── HUD 信息 ──\r\n lines.push('');\r\n lines.push(\r\n ` ${chalk.cyan('分数')}: ${state.score} ` +\r\n `${chalk.cyan('长度')}: ${state.snake.length} ` +\r\n `${chalk.cyan('速度')}: Lv.${state.speedLevel} ` +\r\n `${chalk.cyan('边界')}: ${state.wallWrap ? '穿墙' : '撞墙'}`,\r\n );\r\n\r\n // 特殊食物倒计时提示\r\n if (state.specialFood?.spawnedAt !== undefined) {\r\n const remaining = Math.ceil((5000 - (Date.now() - state.specialFood.spawnedAt)) / 1000);\r\n if (remaining > 0) {\r\n lines.push(` ${chalk.yellow(`⭐ 特殊食物 ${remaining} 秒后消失`)}`);\r\n }\r\n }\r\n\r\n // 操作提示\r\n lines.push(` ${chalk.gray('W/A/S/D 或 方向键移动 | P 暂停 | M 切换模式 | Q 退出')}`);\r\n\r\n // 暂停 / 游戏结束提示\r\n if (state.isPaused) {\r\n lines.push('');\r\n lines.push(` ${chalk.yellow('⏸ 已暂停 — 按 P 继续')}`);\r\n }\r\n\r\n if (state.isGameOver) {\r\n lines.push('');\r\n lines.push(` ${chalk.red.bold('💀 游戏结束!')} ${chalk.cyan('最终分数: ' + state.score)}`);\r\n lines.push(` ${chalk.gray('按 R 重新开始 | 按 Q 退出')}`);\r\n }\r\n\r\n return lines;\r\n}\r\n","import * as readline from 'node:readline';\r\nimport type { Direction, GameState } from './types.js';\r\nimport { changeDirection } from './engine.js';\r\n\r\n// ─── 按键 → 方向映射 ───────────────────────────────────\r\n\r\nconst KEY_TO_DIR: Record<string, Direction> = {\r\n up: 'UP',\r\n w: 'UP',\r\n W: 'UP',\r\n down: 'DOWN',\r\n s: 'DOWN',\r\n S: 'DOWN',\r\n left: 'LEFT',\r\n a: 'LEFT',\r\n A: 'LEFT',\r\n right: 'RIGHT',\r\n d: 'RIGHT',\r\n D: 'RIGHT',\r\n};\r\n\r\n// ─── 输入设置 ───────────────────────────────────────────\r\n\r\nexport interface InputContext {\r\n state: GameState;\r\n}\r\n\r\nexport type InputCleanup = () => void;\r\n\r\n/**\r\n * 设置终端键盘输入监听。\r\n * - WASD / 方向键 → 控制方向\r\n * - P → 暂停/继续\r\n * - R → 游戏结束后重新开始\r\n * - Q / Ctrl+C → 退出游戏\r\n *\r\n * @param ctx 可变状态上下文(重启时替换 state 引用)\r\n * @param onQuit 退出回调\r\n * @param onRestart 重新开始回调(应返回新的 GameState)\r\n */\r\nexport function setupInput(\r\n ctx: InputContext,\r\n onQuit: () => void,\r\n onRestart: () => GameState,\r\n): InputCleanup {\r\n readline.emitKeypressEvents(process.stdin);\r\n\r\n if (process.stdin.isTTY) {\r\n process.stdin.setRawMode(true);\r\n }\r\n\r\n const handler = (_: unknown, key: readline.Key) => {\r\n if (!key || !key.name) return;\r\n\r\n const { name, ctrl } = key;\r\n\r\n // Ctrl+C / Ctrl+D → 退出\r\n if (ctrl && (name === 'c' || name === 'd')) {\r\n onQuit();\r\n return;\r\n }\r\n\r\n // Q → 退出\r\n if (name === 'q') {\r\n onQuit();\r\n return;\r\n }\r\n\r\n const state = ctx.state;\r\n\r\n // 游戏结束后 R → 重新开始\r\n if (state.isGameOver) {\r\n if (name === 'r') {\r\n ctx.state = onRestart();\r\n }\r\n return;\r\n }\r\n\r\n // P → 暂停/继续\r\n if (name === 'p') {\r\n state.isPaused = !state.isPaused;\r\n return;\r\n }\r\n\r\n // M → 切换边界模式(穿墙 / 撞墙)\r\n if (name === 'm') {\r\n state.wallWrap = !state.wallWrap;\r\n return;\r\n }\r\n\r\n // 方向控制\r\n const dir = KEY_TO_DIR[name];\r\n if (dir) {\r\n changeDirection(state, dir);\r\n }\r\n };\r\n\r\n process.stdin.on('keypress', handler);\r\n\r\n return () => {\r\n process.stdin.removeListener('keypress', handler);\r\n if (process.stdin.isTTY) {\r\n process.stdin.setRawMode(false);\r\n }\r\n };\r\n}\r\n","import type { Game } from '../index.js';\r\nimport type { GameConfig } from './types.js';\r\nimport { createInitialState, tick } from './engine.js';\r\nimport { renderToTerminal, clearScreen, hideCursor, showCursor } from './renderer.js';\r\nimport { setupInput } from './input.js';\r\nimport type { InputContext } from './input.js';\r\n\r\nconst CONFIG: GameConfig = {\r\n width: 20,\r\n height: 16,\r\n initialSpeed: 180,\r\n minSpeed: 60,\r\n speedDecrement: 10,\r\n scorePerLevel: 5,\r\n};\r\n\r\n/** 游戏结束后或暂停时的循环间隔(不需要高帧率) */\r\nconst IDLE_INTERVAL = 200;\r\n\r\nconst snakeGame: Game = {\r\n id: 'snake',\r\n name: 'Big Snake',\r\n description: '经典贪吃蛇,双食物系统、难度递增、穿墙/撞墙模式',\r\n play: async () => {\r\n const ctx: InputContext = { state: createInitialState(CONFIG) };\r\n\r\n return new Promise<void>((resolve) => {\r\n const cleanupInput = setupInput(\r\n ctx,\r\n () => {\r\n // ── 退出 ──\r\n stop = true;\r\n clearTimeout(timerId);\r\n showCursor();\r\n cleanupInput();\r\n resolve();\r\n },\r\n () => {\r\n // ── 重新开始 ──\r\n return createInitialState(CONFIG);\r\n },\r\n );\r\n\r\n // 初始化终端\r\n clearScreen();\r\n hideCursor();\r\n\r\n let stop = false;\r\n let timerId: ReturnType<typeof setTimeout>;\r\n\r\n function loop(): void {\r\n if (stop) return;\r\n\r\n const s = ctx.state;\r\n\r\n if (!s.isGameOver && !s.isPaused) {\r\n tick(s, CONFIG);\r\n }\r\n\r\n renderToTerminal(s, CONFIG);\r\n\r\n // 游戏进行中 → 用 tick 速度;暂停/结束时用恒定低速,节省 CPU\r\n const interval = s.isGameOver || s.isPaused ? IDLE_INTERVAL : s.tickInterval;\r\n timerId = setTimeout(loop, interval);\r\n }\r\n\r\n timerId = setTimeout(loop, 0);\r\n });\r\n },\r\n};\r\n\r\nexport default snakeGame;\r\n","import { registerGame, listGames } from \"./index.js\";\r\nimport type { Game } from \"./index.js\";\r\nimport brickBreaker from \"./brick-breaker/index.js\";\r\nimport coderCheck from \"./coder-check/index.js\";\r\nimport snake from \"./snake/index.js\";\r\n\r\n/** 在此注册所有游戏 */\r\nexport function initGames(): Game[] {\r\n registerGame(brickBreaker);\r\n registerGame(coderCheck);\r\n registerGame(snake);\r\n return listGames();\r\n}\r\n","import { Box, Text, useInput } from \"ink\";\r\nimport { useState, useCallback, useEffect, useMemo } from \"react\";\r\nimport asciichart from \"asciichart\";\r\nimport os from \"node:os\";\r\nimport { join } from \"node:path\";\r\nimport type { StockRow } from \"./types.js\";\r\nimport { useDoubleCtrlC } from \"../ui/useDoubleCtrlC.js\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 自选股配置文件路径(取用户全局 ~/.dskcode/settings.json 的真实绝对路径)\r\n// ---------------------------------------------------------------------------\r\n\r\nconst SETTINGS_PATH = join(os.homedir(), \".dskcode\", \"settings.json\");\r\n\r\n/**\r\n * 将本地绝对路径转换为 file:// URL,兼容 Windows 与 POSIX。\r\n * Windows: C:\\Users\\me\\.dskcode\\settings.json → file:///C:/Users/me/.dskcode/settings.json\r\n */\r\nfunction toFileUrl(p: string): string {\r\n const norm = p.replace(/\\\\/g, \"/\");\r\n if (process.platform === \"win32\") {\r\n return \"file://\" + norm.replace(/^([a-zA-Z]:)/, \"/$1\");\r\n }\r\n return \"file://\" + norm;\r\n}\r\n\r\n/**\r\n * 用 OSC 8 转义序列把 text 包装成可点击的终端超链接。\r\n * 现代终端(Windows Terminal / iTerm2 / kitty / WezTerm 等)点击后会以系统默认程序打开该 URI。\r\n * 不可见宽度的转义对 string-width 透明(strip-ansi 会剥除),不影响布局。\r\n */\r\nfunction osc8Link(uri: string, text: string): string {\r\n return `\\x1b]8;;${uri}\\x1b\\\\${text}\\x1b]8;;\\x1b\\\\`;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 分时数据接口\r\n// ---------------------------------------------------------------------------\r\n\r\n/** API 接口地址模板 */\r\nconst MINUTE_API = \"https://web.ifzq.gtimg.cn/appstock/app/minute/query?code={code}&r=0.1\";\r\n\r\ninterface MinuteResponse {\r\n code: number;\r\n data?: Record<string, {\r\n data?: { data?: string[]; date?: string };\r\n qt?: Record<string, (string | string[])[]>;\r\n }>;\r\n}\r\n\r\n/** 确保股票代码带市场前缀(如 513090 → sh513090) */\r\nfunction normalizeApiCode(code: string): string {\r\n if (code.startsWith(\"sh\") || code.startsWith(\"sz\")) return code;\r\n if (/^60|^68|^51/.test(code)) return \"sh\" + code;\r\n if (/^00|^30|^39/.test(code)) return \"sz\" + code;\r\n return \"sh\" + code;\r\n}\r\n\r\n/**\r\n * 调用分时接口获取某只股票的全天分钟数据。\r\n * 返回 { prices: 每分钟价格数组, quote: 实时行情快照, date: 日期 }\r\n */\r\nasync function fetchStockMinute(code: string): Promise<{\r\n prices: number[];\r\n quote: StockRow | null;\r\n date: string;\r\n} | null> {\r\n const url = MINUTE_API.replace(\"{code}\", normalizeApiCode(code));\r\n try {\r\n const resp = await fetch(url);\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const json = (await resp.json()) as unknown as MinuteResponse;\r\n if (json.code !== 0) return null;\r\n\r\n const stockKey = normalizeApiCode(code);\r\n const stockData = json.data?.[stockKey];\r\n if (!stockData) return null;\r\n\r\n // 1. 分钟行情 — 用于折线图\r\n const rawMinutes = stockData.data?.data ?? [];\r\n const prices: number[] = [];\r\n for (const line of rawMinutes) {\r\n // 格式: \"HHMM price volume amount\"\r\n const parts = (line).split(\" \");\r\n if (parts.length >= 2) {\r\n const p = parseFloat(parts[1]!);\r\n if (!isNaN(p)) prices.push(p);\r\n }\r\n }\r\n\r\n // 2. 实时行情快照 — 用于列表\r\n const qtKey = stockKey;\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const qt = stockData.qt?.[qtKey] as unknown as string[] | undefined;\r\n let quote: StockRow | null = null;\r\n if (qt && qt.length >= 35) {\r\n quote = {\r\n code,\r\n name: qt[1] ?? \"\",\r\n price: parseFloat(qt[3] ?? \"0\"),\r\n changePercent: parseFloat(qt[32] ?? \"0\"),\r\n changeAmount: parseFloat(qt[31] ?? \"0\"),\r\n high: parseFloat(qt[33] ?? \"0\"),\r\n low: parseFloat(qt[34] ?? \"0\"),\r\n volume: parseInt(qt[6] ?? \"0\", 10),\r\n // 成交额(单位:万元)→ 转为元\r\n amount: parseFloat(qt[37] ?? \"0\") * 10_000,\r\n };\r\n }\r\n\r\n return {\r\n prices,\r\n quote,\r\n date: stockData.data?.date ?? \"\",\r\n };\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 缓存\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 缓存各股票的分钟价格数据,key = 股票代码 */\r\nconst minuteCache = new Map<string, number[]>();\r\n\r\nfunction cacheMinute(code: string, prices: number[]): void {\r\n minuteCache.set(code, prices);\r\n}\r\n\r\n/** 仅用于测试环境重置缓存 */\r\nexport function _clearMinuteCache(): void {\r\n minuteCache.clear();\r\n}\r\n\r\nfunction _getCachedMinutes(_code: string): number[] | undefined {\r\n return minuteCache.get(_code);\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 演示兜底数据\r\n// ---------------------------------------------------------------------------\r\n\r\nconst FALLBACK_STOCKS: StockRow[] = [\r\n // 上证指数成交额约 1.56 万亿\r\n { code: \"000001\", name: \"上证指数\", price: 3150.00, changePercent: 0.35, changeAmount: 11.02, high: 3160.00, low: 3140.00, volume: 285430000, amount: 1_560_000_000_000 },\r\n // 紫金矿业成交额约 108.5 亿\r\n { code: \"601899\", name: \"紫金矿业\", price: 18.20, changePercent: 1.85, changeAmount: 0.33, high: 18.45, low: 17.90, volume: 59615384, amount: 10_850_000_000 },\r\n { code: \"399006\", name: \"创业板指\", price: 1820.00, changePercent: -0.52, changeAmount: -9.50, high: 1835.00, low: 1815.00, volume: 98650000, amount: 18_240_000_000 },\r\n];\r\n\r\n// ---------------------------------------------------------------------------\r\n// fetchStocks — 加载列表数据\r\n// ---------------------------------------------------------------------------\r\n\r\nasync function fetchStocks(codes: string[]): Promise<StockRow[]> {\r\n const results = await Promise.all(\r\n codes.map(async (code) => {\r\n const data = await fetchStockMinute(code);\r\n if (data?.quote) {\r\n // 同时缓存分钟数据,进详情就不用再请求了\r\n if (data.prices.length > 0) cacheMinute(code, data.prices);\r\n return data.quote;\r\n }\r\n return null;\r\n }),\r\n );\r\n\r\n // 用真实数据替换,缺失的用兜底\r\n const real: StockRow[] = results.filter((r): r is StockRow => r !== null);\r\n if (real.length > 0) return real;\r\n\r\n // 全都没取到?用兜底数据\r\n return FALLBACK_STOCKS;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 格式化工具\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction formatPrice(p: number): string {\r\n return p >= 100 ? p.toFixed(2) : p.toFixed(3);\r\n}\r\n\r\nfunction _formatVolume(_v: number): string {\r\n if (_v >= 10000) return (_v / 10000).toFixed(1) + \"万\";\r\n return _v.toLocaleString();\r\n}\r\n\r\n/**\r\n * 成交额格式化(元 → 万/亿/万亿)\r\n * 1 万亿 = 1_0000_0000_0000,1 亿 = 1_0000_0000,1 万 = 1_0000\r\n */\r\nfunction formatAmount(yuan: number): string {\r\n if (yuan >= 1_000_000_000_000) return (yuan / 1_000_000_000_000).toFixed(2) + \"万亿\";\r\n if (yuan >= 100_000_000) return (yuan / 100_000_000).toFixed(2) + \"亿\";\r\n if (yuan >= 10_000) return (yuan / 10_000).toFixed(1) + \"万\";\r\n return yuan.toLocaleString();\r\n}\r\n\r\n/**\r\n * 取最新 maxPoints 个点,用于绘制折线图。\r\n * 分时数据从 09:30 累积到当前时间,取尾部最新的 60 点\r\n * 能展示最近的行情走势,同时适配终端宽度。\r\n */\r\nfunction latestPoints(data: number[], maxPoints = 60): number[] {\r\n if (data.length <= maxPoints) return data;\r\n return data.slice(data.length - maxPoints);\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// StockList 组件\r\n// ---------------------------------------------------------------------------\r\n\r\ninterface StockListProps {\r\n /** 自选股代码列表 */\r\n codes?: string[];\r\n /** 按 q 时退出 */\r\n onExit: () => void;\r\n /** 按 q 时返回上级(从 chat 内跳转时使用) */\r\n onBackToChat?: () => void;\r\n}\r\n\r\nexport function StockList({ codes, onExit, onBackToChat }: StockListProps) {\r\n const [stocks, setStocks] = useState<StockRow[]>([]);\r\n const [selectedIndex, setSelectedIndex] = useState(0);\r\n const [loading, setLoading] = useState(true);\r\n const [lastUpdate, setLastUpdate] = useState<string>(\"\");\r\n const [detailView, setDetailView] = useState<StockRow | null>(null);\r\n const [detailPrices, setDetailPrices] = useState<number[] | null>(null);\r\n const [detailLoading, setDetailLoading] = useState(false);\r\n const [detailCountdown, setDetailCountdown] = useState(10);\r\n const [countdown, setCountdown] = useState(5);\r\n const [currentTime, setCurrentTime] = useState<string>(() =>\r\n new Date().toLocaleTimeString(\"zh-CN\", { hour12: false }),\r\n );\r\n // h 键一键置灰:所有高亮色替换为 dimColor\r\n const [dimMode, setDimMode] = useState(false);\r\n\r\n // o 键排序:默认 → 降序 → 升序 → 默认\r\n type SortOrder = \"default\" | \"asc\" | \"desc\";\r\n const [sortOrder, setSortOrder] = useState<SortOrder>(\"default\");\r\n\r\n const sortedStocks = useMemo(() => {\r\n if (sortOrder === \"default\") return stocks;\r\n return [...stocks].toSorted((a, b) =>\r\n sortOrder === \"desc\"\r\n ? b.changePercent - a.changePercent\r\n : a.changePercent - b.changePercent,\r\n );\r\n }, [stocks, sortOrder]);\r\n\r\n const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(onExit);\r\n\r\n // 实时时钟(每秒更新)\r\n useEffect(() => {\r\n const timer = setInterval(() => {\r\n setCurrentTime(new Date().toLocaleTimeString(\"zh-CN\", { hour12: false }));\r\n }, 1000);\r\n return () => clearInterval(timer);\r\n }, []);\r\n\r\n // ---------- 数据加载 ----------\r\n const loadData = useCallback(async () => {\r\n setLoading(true);\r\n try {\r\n const data = await fetchStocks(codes ?? []);\r\n setStocks(data);\r\n setLastUpdate(new Date().toLocaleTimeString());\r\n } catch {\r\n // 保持旧数据\r\n }\r\n setLoading(false);\r\n }, [codes]);\r\n\r\n // 首次加载\r\n useEffect(() => {\r\n void loadData();\r\n }, [loadData]);\r\n\r\n // 5 秒自动刷新 + 倒计时\r\n useEffect(() => {\r\n const interval = setInterval(() => {\r\n setCountdown((prev) => {\r\n if (prev <= 1) {\r\n void loadData();\r\n return 5;\r\n }\r\n return prev - 1;\r\n });\r\n }, 1000);\r\n return () => clearInterval(interval);\r\n }, [loadData]);\r\n\r\n // ---------- 进入详情时加载分钟数据,每 30s 自动刷新 ----------\r\n useEffect(() => {\r\n if (detailView) {\r\n const loadDetail = () => {\r\n // 清除缓存,强制走网络\r\n minuteCache.delete(detailView.code);\r\n void fetchStockMinute(detailView.code).then((data) => {\r\n if (data && data.prices.length > 0) {\r\n cacheMinute(detailView.code, data.prices);\r\n setDetailPrices(data.prices);\r\n }\r\n });\r\n };\r\n\r\n // 立即加载\r\n loadDetail();\r\n\r\n // 重置倒计时\r\n setDetailCountdown(10);\r\n\r\n // 每 10 秒刷新\r\n const timer = setInterval(loadDetail, 10000);\r\n return () => clearInterval(timer);\r\n }\r\n\r\n setDetailPrices(null);\r\n setDetailLoading(false);\r\n }, [detailView]);\r\n\r\n // 详情页倒计时\r\n useEffect(() => {\r\n if (!detailView) return;\r\n const timer = setInterval(() => {\r\n setDetailCountdown((prev) => (prev > 0 ? prev - 1 : 10));\r\n }, 1000);\r\n return () => clearInterval(timer);\r\n }, [detailView]);\r\n\r\n // ---------- 键盘控制 ----------\r\n useInput(\r\n useCallback(\r\n (input, key) => {\r\n // Ctrl+C:双击退出\r\n if (input === \"c\" && key.ctrl) {\r\n handleCtrlC();\r\n return;\r\n }\r\n\r\n if (detailView) {\r\n // 详情模式下按 Esc/q 返回列表\r\n if (key.escape || input === \"q\" || input === \" \") {\r\n setDetailView(null);\r\n }\r\n return;\r\n }\r\n\r\n if (stocks.length === 0) return;\r\n\r\n if (key.upArrow || input === \"k\") {\r\n setSelectedIndex((prev) => (prev > 0 ? prev - 1 : stocks.length - 1));\r\n } else if (key.downArrow || input === \"j\") {\r\n setSelectedIndex((prev) => (prev < stocks.length - 1 ? prev + 1 : 0));\r\n } else if (key.return) {\r\n const stock = stocks[selectedIndex];\r\n if (stock) setDetailView(stock);\r\n } else if (key.escape || input === \"q\") {\r\n if (onBackToChat) onBackToChat();\r\n else onExit();\r\n } else if (input === \"r\") {\r\n setCountdown(5);\r\n void loadData();\r\n } else if (input === \"o\") {\r\n setSortOrder((prev) => (prev === \"default\" ? \"desc\" : prev === \"desc\" ? \"asc\" : \"default\"));\r\n } else if (input === \"h\") {\r\n setDimMode((v) => !v);\r\n }\r\n },\r\n [stocks, selectedIndex, detailView, onExit, onBackToChat, loadData, handleCtrlC, setDimMode],\r\n ),\r\n );\r\n\r\n // ---------- 详情视图 ----------\r\n if (detailView) {\r\n if (detailLoading) {\r\n return (\r\n <Box paddingLeft={1}>\r\n <Text dimColor>{\" ⟳ 加载分时数据...\"}</Text>\r\n </Box>\r\n );\r\n }\r\n return renderDetail(detailView, () => setDetailView(null), detailPrices ?? undefined, detailCountdown, currentTime);\r\n }\r\n\r\n // ---------- 列表视图 ----------\r\n // 置灰模式下,所有高亮色统一替换为 dimColor\r\n const cp = (c: string) => dimMode ? { dimColor: true } : { color: c };\r\n return (\r\n <Box flexDirection=\"column\">\r\n {/* 顶部状态栏 */}\r\n <Box marginBottom={1} justifyContent=\"space-between\">\r\n <Text bold {...cp(\"#00ffff\")}>\r\n {\" 📈 自选股监控\"}\r\n </Text>\r\n <Text dimColor>\r\n {\" 🕐 \"}{currentTime}\r\n </Text>\r\n <Text dimColor>\r\n {loading ? \" ⟳ 刷新中...\" : ` ${countdown}s 后自动刷新`}\r\n </Text>\r\n </Box>\r\n\r\n {/* 表头 */}\r\n <Box>\r\n <Box width={3} />\r\n <Box width={9}>\r\n <Text dimColor>代码</Text>\r\n </Box>\r\n <Box width={16}>\r\n <Text dimColor>名称</Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text dimColor>最新价</Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text dimColor>\r\n 涨跌幅{sortOrder === \"desc\" ? \" ▼\" : sortOrder === \"asc\" ? \" ▲\" : \"\"}\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text dimColor>涨跌额</Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text dimColor>最高</Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text dimColor>最低</Text>\r\n </Box>\r\n <Box>\r\n <Text dimColor>成交额</Text>\r\n </Box>\r\n </Box>\r\n\r\n {/* 分隔线 */}\r\n <Box>\r\n <Text dimColor>{\" \" + \"─\".repeat(100)}</Text>\r\n </Box>\r\n\r\n {/* 股票列表 */}\r\n <Box flexDirection=\"column\">\r\n {sortedStocks.map((stock, index) => {\r\n const isSelected = index === selectedIndex;\r\n const isUp = stock.changePercent >= 0;\r\n const color = isUp ? \"#ff1493\" : \"#00ff41\";\r\n\r\n return (\r\n <Box key={stock.code}>\r\n <Box width={3} flexShrink={0}>\r\n {isSelected ? (\r\n <Text bold {...cp(\"#00ffff\")}>\r\n {\"▸ \"}\r\n </Text>\r\n ) : (\r\n <Text>{\" \"}</Text>\r\n )}\r\n </Box>\r\n <Box width={9}>\r\n <Text bold {...cp(isSelected ? \"#00ffff\" : \"#ffffff\")}>\r\n {stock.code}\r\n </Text>\r\n </Box>\r\n <Box width={16}>\r\n <Text {...cp(isSelected ? \"#ffffff\" : \"#cccccc\")}>\r\n {stock.name}\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text bold {...cp(color)}>\r\n {formatPrice(stock.price)}\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text {...cp(color)}>\r\n {isUp ? \"+\" : \"\"}{stock.changePercent.toFixed(2)}%\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text {...cp(color)}>\r\n {isUp ? \"+\" : \"\"}{stock.changeAmount.toFixed(3)}\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text {...cp(\"#cccccc\")}>\r\n {formatPrice(stock.high)}\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text {...cp(\"#888888\")}>\r\n {formatPrice(stock.low)}\r\n </Text>\r\n </Box>\r\n <Box>\r\n <Text {...cp(\"#888888\")}>\r\n {formatAmount(stock.amount)}\r\n </Text>\r\n </Box>\r\n </Box>\r\n );\r\n })}\r\n </Box>\r\n\r\n {/* 底栏 */}\r\n <Box marginTop={1}>\r\n <Text dimColor>\r\n {` ↑/↓ 选择 Enter 详情 r 手动刷新 o 排序 h 置灰/恢复 q 返回`}\r\n </Text>\r\n </Box>\r\n <Box>\r\n <Text dimColor>{` 最后更新: ${lastUpdate} 编辑自选股: `}</Text>\r\n <Text {...cp(\"#c792ea\")}>\r\n {osc8Link(toFileUrl(SETTINGS_PATH), SETTINGS_PATH)}\r\n </Text>\r\n </Box>\r\n\r\n {/* 双击 Ctrl+C 退出提示 */}\r\n {doubleCtrlC && (\r\n <Box marginTop={1}>\r\n <Text bold {...cp(\"#ff1493\")}>\r\n {\" ⚠ 再按一次 Ctrl+C 退出 dskcode\"}\r\n </Text>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 详情视图(独立的渲染函数)\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction renderDetail(\r\n stock: StockRow,\r\n _onBack: () => void,\r\n prices?: number[],\r\n countdown = 10,\r\n currentTime?: string,\r\n) {\r\n const isUp = stock.changePercent >= 0;\r\n const colorCode = isUp ? \"#ff1493\" : \"#00ff41\";\r\n const arrow = isUp ? \"▲\" : \"▼\";\r\n\r\n // 折线图\r\n let chartLines: string[] = [];\r\n if (prices && prices.length > 0) {\r\n const chartColor = isUp ? asciichart.red : asciichart.green;\r\n // 取最新 60 条分时数据展示近期走势\r\n const latest = latestPoints(prices, 60);\r\n let raw = asciichart.plot(latest, {\r\n height: 10,\r\n colors: [chartColor],\r\n });\r\n // 弯角 → 直角,折线更硬朗\r\n raw = raw\r\n .replaceAll(\"╭\", \"┌\")\r\n .replaceAll(\"╮\", \"┐\")\r\n .replaceAll(\"╰\", \"└\")\r\n .replaceAll(\"╯\", \"┘\");\r\n chartLines = raw.split(\"\\n\");\r\n }\r\n\r\n return (\r\n <Box flexDirection=\"column\" paddingLeft={1}>\r\n {/* 标题行 — 左:名称代码 + 实时时间,右:刷新倒计时 */}\r\n <Box marginBottom={1} justifyContent=\"space-between\">\r\n <Box>\r\n <Text bold color=\"#00ffff\">\r\n {\" 📊 \"}{stock.name}{\" \"}\r\n </Text>\r\n <Text dimColor>\r\n {stock.code}\r\n </Text>\r\n {currentTime && (\r\n <Text dimColor>\r\n {\" 🕐 \"}{currentTime}\r\n </Text>\r\n )}\r\n </Box>\r\n <Text dimColor>\r\n {`${countdown}s 后刷新`}\r\n </Text>\r\n </Box>\r\n\r\n {/* 价格摘要 */}\r\n <Box>\r\n <Box width={16}>\r\n <Text bold color=\"#888888\">当前价</Text>\r\n </Box>\r\n <Box>\r\n <Text bold color={colorCode}>\r\n {arrow} {formatPrice(stock.price)}\r\n </Text>\r\n </Box>\r\n </Box>\r\n <Box>\r\n <Box width={16}>\r\n <Text color=\"#888888\">涨跌幅</Text>\r\n </Box>\r\n <Box>\r\n <Text color={colorCode}>\r\n {isUp ? \"+\" : \"\"}{stock.changePercent.toFixed(2)}%\r\n {\" \"}\r\n {isUp ? \"+\" : \"\"}{stock.changeAmount.toFixed(3)}\r\n </Text>\r\n </Box>\r\n </Box>\r\n\r\n {/* 折线图 */}\r\n {chartLines.length > 0 && (\r\n <Box marginTop={1} flexDirection=\"column\">\r\n {chartLines.map((line, i) => (\r\n <Box key={i}>\r\n <Text color={colorCode}>\r\n {line || \" \"}\r\n </Text>\r\n </Box>\r\n ))}\r\n </Box>\r\n )}\r\n\r\n {/* 底部 */}\r\n <Box marginTop={1}>\r\n <Text dimColor>\r\n {\" Space/q 返回列表\"}\r\n </Text>\r\n </Box>\r\n </Box>\r\n );\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 递归扫描项目源码文件(排除 node_modules 等非源码目录)\r\n// 启动时调用一次,结果缓存为扁平路径数组传给 UI\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readdir } from \"node:fs/promises\";\r\nimport { join, relative } from \"node:path\";\r\n\r\n/** 需要跳过的目录名(任何层级) */\r\nconst IGNORE_DIRS = new Set([\r\n \"node_modules\",\r\n \".git\",\r\n \".svn\",\r\n \".hg\",\r\n \".dskcode\",\r\n \".claude\",\r\n \"dist\",\r\n \"build\",\r\n \".next\",\r\n \".turbo\",\r\n \".nx\",\r\n \"coverage\",\r\n \".cache\",\r\n \".nyc_output\",\r\n \".vscode\",\r\n \".idea\",\r\n \"__pycache__\",\r\n \".venv\",\r\n \"venv\",\r\n \".tox\",\r\n \"target\",\r\n \"vendor\",\r\n \"bower_components\",\r\n \"jspm_packages\",\r\n]);\r\n\r\n/** 只索引这些源码扩展名 */\r\nconst SOURCE_EXTS = new Set([\r\n \".ts\", \".tsx\", \".js\", \".jsx\", \".mjs\", \".cjs\",\r\n \".vue\", \".svelte\", \".astro\",\r\n \".css\", \".scss\", \".less\",\r\n \".html\",\r\n \".json\",\r\n \".md\",\r\n \".yaml\", \".yml\",\r\n \".toml\",\r\n]);\r\n\r\n/**\r\n * 递归扫描 dir 下的所有文件,返回相对于 baseDir 的路径数组。\r\n *\r\n * 优化手段:\r\n * - readdir withFileTypes 省掉 stat 调用(减少一半 syscall)\r\n * - 同层目录的 readdir 用 Promise.all 并行\r\n * - 跳过以 . 开头的隐藏目录和已知非源码目录\r\n *\r\n * 性能预期(SSD):\r\n * 1,000 文件 → ~15ms\r\n * 10,000 文件 → ~150ms\r\n * 50,000 文件 → ~800ms\r\n */\r\nexport async function scanProjectFiles(\r\n baseDir: string,\r\n dir?: string,\r\n): Promise<string[]> {\r\n const currentDir = dir ?? baseDir;\r\n\r\n let entries;\r\n try {\r\n entries = await readdir(currentDir, { withFileTypes: true });\r\n } catch {\r\n return [];\r\n }\r\n\r\n const files: string[] = [];\r\n const dirs: string[] = [];\r\n\r\n for (const entry of entries) {\r\n const name = entry.name;\r\n if (IGNORE_DIRS.has(name) || name.startsWith(\".\")) continue;\r\n\r\n if (entry.isDirectory()) {\r\n dirs.push(name);\r\n } else if (entry.isFile()) {\r\n const ext = name.slice(name.lastIndexOf(\".\")).toLowerCase();\r\n if (SOURCE_EXTS.has(ext)) {\r\n files.push(relative(baseDir, join(currentDir, name)));\r\n }\r\n }\r\n }\r\n\r\n // 同层子目录并行扫描\r\n const nested = await Promise.all(\r\n dirs.map((d) => scanProjectFiles(baseDir, join(currentDir, d))),\r\n );\r\n\r\n for (const n of nested) {\r\n files.push(...n);\r\n }\r\n\r\n return files;\r\n}\r\n","/** dskcode 退出码规范 */\r\nexport const ExitCode = {\r\n /** 正常执行完成 */\r\n SUCCESS: 0,\r\n /** 通用错误 */\r\n GENERAL_ERROR: 1,\r\n /** 配置错误 */\r\n CONFIG_ERROR: 2,\r\n /** 用户通过 Ctrl+C 中断 */\r\n SIGINT: 130,\r\n} as const;\r\n","#!/usr/bin/env node\r\n\r\nimport { createCli } from \"./cli/index.js\";\r\nimport { ExitCode } from \"./cli/exit-codes.js\";\r\nimport { CostTracker } from \"./provider/cost-tracker.js\";\r\nimport { ConversationLogger } from \"./logger/index.js\";\r\n\r\n/**\r\n * 双击 Ctrl+C 退出:\r\n * 第一次 SIGINT → 提示用户再按一次\r\n * 第二次 SIGINT(1.5 秒内)→ 立即退出\r\n *\r\n * 在 ink 交互模式下,Ctrl+C 由 useInput 捕获处理,\r\n * 此处只处理 ink 未运行时的 SIGINT(如启动阶段)。\r\n */\r\nlet sigintCount = 0;\r\n// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents\r\nlet sigintTimer: NodeJS.Timeout | null = null;\r\n\r\n/**\r\n * 全局兜底 SIGINT 处理:双击退出。\r\n * ink 交互模式下 Ctrl+C 由 useDoubleCtrlC hook 处理,\r\n * 此处仅处理 ink 未运行时的场景(如启动阶段、异常退出后)。\r\n *\r\n * 第二次 SIGINT / 任意 SIGTERM / 正常退出前都会统一 flush 所有\r\n * CostTracker 实例,确保今日用量数据落盘。\r\n */\r\nasync function gracefulExit(code: number): Promise<never> {\r\n try {\r\n await Promise.all([\r\n CostTracker.flushAll(),\r\n ConversationLogger.flushAll(),\r\n ]);\r\n } catch {\r\n // 兜底路径,flush 失败不影响退出码\r\n }\r\n process.exit(code);\r\n}\r\n\r\nprocess.on(\"SIGINT\", () => {\r\n sigintCount++;\r\n if (sigintCount >= 2) {\r\n void gracefulExit(ExitCode.SIGINT);\r\n return;\r\n }\r\n process.stdout.write(\"\\n ⚠ 再按一次 Ctrl+C 退出 dskcode\\n\");\r\n if (sigintTimer) clearTimeout(sigintTimer);\r\n sigintTimer = setTimeout(() => {\r\n sigintCount = 0;\r\n }, 1500);\r\n});\r\n\r\nprocess.on(\"SIGTERM\", () => {\r\n void gracefulExit(ExitCode.SIGINT);\r\n});\r\n\r\nconst program = createCli();\r\n\r\ntry {\r\n await program.parseAsync(process.argv);\r\n // 正常执行完毕后也兜底 flush(一般 record() 内部已 fire-and-forget,\r\n // 这里保证若本次没有任何 record 调用也不漏盘)\r\n await Promise.all([CostTracker.flushAll(), ConversationLogger.flushAll()]);\r\n} catch (err: unknown) {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const error = err as { exitCode?: number; code?: string };\r\n\r\n if (error.code === \"commander.helpDisplayed\" || error.code === \"commander.version\") {\r\n await Promise.all([CostTracker.flushAll(), ConversationLogger.flushAll()]);\r\n process.exit(error.exitCode ?? ExitCode.SUCCESS);\r\n }\r\n\r\n if (typeof error.exitCode === \"number\") {\r\n await Promise.all([CostTracker.flushAll(), ConversationLogger.flushAll()]);\r\n process.exit(error.exitCode);\r\n }\r\n\r\n console.error(String(err));\r\n await Promise.all([CostTracker.flushAll(), ConversationLogger.flushAll()]);\r\n process.exit(ExitCode.GENERAL_ERROR);\r\n}\r\n"],"mappings":"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","names":["SUPPORTED_MODELS","program","chalk","resolve","createInterface","existsSync","mkdir","readFile","join","chalk","stat","createInterface","resolve","mkdir","join","existsSync","stat","chalk","readFile","readFile","join","Text","jsxs","Box","Text","jsx","Box","Text","useEffect","useState","useCallback","useRef","useState","useEffect","InkSpinner","Box","Text","useEffect","useRef","useState","mkdir","readFile","writeFile","join","join","readFile","mkdir","writeFile","Box","Text","jsx","jsxs","Box","Text","cp","lines","jsx","jsxs","Box","Text","jsx","jsxs","useState","useRef","useEffect","cost","Text","Box","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","HIGHLIGHT_COLOR","Box","Text","InkSpinner","jsx","jsxs","registry","mkdir","readFile","writeFile","readdir","join","mkdir","join","cp","relative","realpath","process","process","patch","writeFile","readFile","relative","readFile","relative","mkdir","readFile","relative","basename","readFile","mkdir","basename","relative","readFile","basename","readFile","basename","readFile","basename","readFile","basename","readFile","basename","readFile","basename","process","existsSync","join","isWindows","process","existsSync","join","readdir","stat","join","relative","isAbsolute","readdir","join","relative","isAbsolute","stat","readdir","readFile","stat","join","relative","isAbsolute","readdir","join","isAbsolute","stat","readFile","relative","readdir","stat","join","relative","readdir","stat","join","relative","Fragment","jsx","jsxs","useState","useRef","useEffect","useCallback","DeepSeekProvider","Box","Text","cp","InkSpinner","Box","Text","useInput","useState","useCallback","jsx","jsxs","useState","useInput","useCallback","Box","Text","Box","Text","useInput","render","useState","useEffect","useRef","useCallback","jsx","jsxs","tick","s","resolve","Box","Text","useInput","render","useState","useEffect","useRef","useCallback","jsx","jsxs","CYBER_PALETTE","createInitialState","update","tick","s","resolve","createInitialState","chalk","createInitialState","resolve","render","chalk","Box","Text","useInput","useState","useCallback","useEffect","useMemo","join","jsx","jsxs","join","useState","useMemo","useEffect","useCallback","useInput","jsx","Box","Text","cp","jsxs","readdir","join","relative","jsx","program","chalk","join","readFile","resolve","render"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli/index.tsx","../src/config/loader.ts","../src/cli/middleware.ts","../src/cli/help.ts","../src/cli/api-key-setup.ts","../src/cli/skill-import.ts","../src/ui/RenderScope.tsx","../src/ui/Spinner.tsx","../src/ui/StatusMessage.tsx","../src/ui/DskcodeSplash.tsx","../src/ui/ChatSession.tsx","../src/ui/useDoubleCtrlC.ts","../src/ui/AssistantMessage.tsx","../src/provider/cost-tracker.ts","../src/ui/ToolCallBlock.tsx","../src/agent/message-builder.ts","../src/ui/HighlightedText.tsx","../src/ui/table-layout.ts","../src/ui/reasoning-utils.ts","../src/ui/DiffPreview.tsx","../src/ui/SkillSelector.tsx","../src/ui/FileSelector.tsx","../src/ui/TodoListPanel.tsx","../src/provider/registry.ts","../src/tool/types.ts","../src/agent/system-prompt.ts","../src/agent/prompts/system-prompt.hbs","../src/agent/prompts/plan-prompt.hbs","../src/tool/registry.ts","../src/agent/tool-executor.ts","../src/agent/storm-detector.ts","../src/agent/reflector.ts","../src/agent/tool-definitions.ts","../src/harness/todo-list.ts","../src/harness/tools.ts","../src/checkpoint/git-checkpoint.ts","../src/session-store/session-store.ts","../src/logger/logger.ts","../src/agent/index.ts","../src/tool/sandbox.ts","../src/tool/diff.ts","../src/tool/eol.ts","../src/tool/builtins/read-file.ts","../src/tool/builtins/write-file.ts","../src/tool/builtins/edit-file.ts","../src/tool/builtins/multi-edit.ts","../src/tool/builtins/delete-range.ts","../src/tool/builtins/bash.ts","../src/tool/builtins/glob.ts","../src/tool/builtins/grep.ts","../src/tool/builtins/ls.ts","../src/tool/builtins/fetch.ts","../src/tool/builtins/index.ts","../src/utils/gradient.ts","../src/ui/GamePicker.tsx","../src/game/index.ts","../src/game/brick-breaker/index.tsx","../src/game/coder-check/index.tsx","../src/game/snake/engine.ts","../src/game/snake/renderer.ts","../src/game/snake/input.ts","../src/game/snake/index.ts","../src/game/registry.ts","../src/stock/StockList.tsx","../src/utils/scan-files.ts","../src/cli/exit-codes.ts","../src/index.ts"],"sourcesContent":["import { Command } from \"commander\";\r\nimport { loadConfigMiddleware } from \"./middleware.js\";\r\nimport type { DskcodeContext } from \"./middleware.js\";\r\nimport { customHelp } from \"./help.js\";\r\nimport { hasApiKey, promptForApiKey } from \"./api-key-setup.js\";\r\nimport { promptImportClaudeSkills, countDskcodeSkills, countProjectLocalSkills, getAllSkills } from \"./skill-import.js\";\r\nimport { saveApiKey, loadAndValidate, saveStockConfig } from \"../config/index.js\";\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { join } from \"node:path\";\r\nimport { renderApp, ChatSession } from \"../ui/index.js\";\r\nimport { initGames } from \"../game/registry.js\";\r\nimport { listGames, getGame } from \"../game/index.js\";\r\nimport type { Game } from \"../game/index.js\";\r\nimport { GamePicker } from \"../ui/GamePicker.js\";\r\nimport { render } from \"ink\";\r\nimport chalk from \"chalk\";\r\nimport { StockList } from \"../stock/index.js\";\r\nimport { CostTracker } from \"../provider/cost-tracker.js\";\r\nimport { scanProjectFiles } from \"../utils/scan-files.js\";\r\n\r\nconst SUBCOMMANDS = [\"chat\", \"run\", \"setup\", \"init\", \"completion\", \"game\", \"stock\"];\r\n\r\nexport function createCli(): Command {\r\n const program = new Command();\r\n program.exitOverride();\r\n\r\n program\r\n .name(\"dskcode\")\r\n .description(\"基于 DeepSeek 的 AI 编程助手终端工具\")\r\n .version(\"0.0.0\", \"-V, --version\", \"显示版本号\")\r\n .option(\"--verbose\", \"开启详细日志输出\")\r\n .option(\"--config <path>\", \"指定配置文件路径\");\r\n\r\n program.helpInformation = () => customHelp(program);\r\n\r\n program.hook(\"preAction\", async (thisCommand, actionCommand) => {\r\n const ctx = await loadConfigMiddleware.call(thisCommand);\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n (actionCommand as unknown as Record<string, unknown>).dskcodeCtx = ctx;\r\n });\r\n\r\n // chat — 交互式对话\r\n program\r\n .command(\"chat\")\r\n .description(\"启动交互式对话会话\")\r\n .action(async function () {\r\n if (!process.stdin.isTTY) {\r\n console.error(\"dskcode chat 需要交互式终端。如需执行一次性任务,请使用 dskcode run。\");\r\n process.exit(1);\r\n }\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n let ctx = (this as unknown as Record<string, unknown>).dskcodeCtx as DskcodeContext | undefined;\r\n\r\n // 检查 API Key,如果没有则交互式输入\r\n if (ctx && !hasApiKey(ctx.config.providers)) {\r\n const key = await promptForApiKey();\r\n if (!key) process.exit(1);\r\n\r\n // 保存到全局配置\r\n const savedPath = await saveApiKey(key);\r\n console.log(` ${chalk.green(\"✔\")} API Key 已保存到 ${chalk.dim(savedPath)}\\n`);\r\n\r\n // 重新加载配置,使新 Key 生效\r\n const result = await loadAndValidate();\r\n ctx = { ...ctx, config: result.config };\r\n }\r\n\r\n // API Key 检查之后:检测项目本地 skill 和 Claude Code skill\r\n // 如果 .dskcode/skill 下有 skill,则跳过导入 Claude Code skill\r\n await promptImportClaudeSkills(process.cwd());\r\n\r\n // 从配置创建 CostTracker\r\n const costTracker = new CostTracker({\r\n budgetLimit: ctx?.config.budgetLimit ?? 0,\r\n tokenBudgetLimit: ctx?.config.tokenBudgetLimit ?? 0,\r\n });\r\n\r\n void startChat(ctx, costTracker);\r\n });\r\n\r\n // run\r\n program\r\n .command(\"run\")\r\n .description(\"执行一次性任务\")\r\n .argument(\"[prompt...]\", \"任务描述\")\r\n .option(\"--model <name>\", \"指定使用的模型\")\r\n .action(async function (_prompt: string[]) {\r\n console.log(\"dskcode run — 待实现(第07章)\");\r\n });\r\n\r\n // setup\r\n program\r\n .command(\"setup\")\r\n .description(\"运行配置向导\")\r\n .option(\"--export\", \"以 JSON 格式导出配置\")\r\n .option(\"--test\", \"测试 API Key 连通性\")\r\n .action(async function () {\r\n console.log(\"dskcode setup — 待实现(第14章)\");\r\n });\r\n\r\n // init\r\n program\r\n .command(\"init\")\r\n .description(\"在当前项目下生成项目记忆文件(AGENTS.md)\")\r\n .action(async function () {\r\n console.log(\"dskcode init — 待实现(第11章)\");\r\n });\r\n\r\n // completion\r\n program\r\n .command(\"completion\")\r\n .description(\"输出 shell 自动补全配置说明(bash/zsh)\")\r\n .argument(\"[shell]\", \"shell 类型\", /^(bash|zsh)$/i)\r\n .action(async function (shell?: string) {\r\n if (!shell) {\r\n console.log(\"请指定 shell 类型:dskcode completion bash 或 dskcode completion zsh\");\r\n return;\r\n }\r\n if (shell === \"bash\") {\r\n console.log(`# dskcode bash 自动补全\r\n_dskcode_completion() {\r\n local cur=\\${COMP_WORDS[COMP_CWORD]}\r\n if [[ \\${COMP_CWORD} -eq 1 ]]; then\r\n COMPREPLY=( $(compgen -W \"${SUBCOMMANDS.join(\" \")}\" -- \"\\${cur}\") )\r\n return 0\r\n fi\r\n COMPREPLY=( $(compgen -W \"--verbose --config --model\" -- \"\\${cur}\") )\r\n}\r\ncomplete -F _dskcode_completion dskcode`);\r\n } else {\r\n console.log(`# dskcode zsh 自动补全\r\n_dskcode_completion() {\r\n local -a commands\r\n commands=(\r\n \"chat:启动交互式对话会话\"\r\n \"run:执行一次性任务\"\r\n \"setup:运行配置向导\"\r\n \"init:生成项目记忆文件\"\r\n \"completion:输出 shell 自动补全说明\"\r\n \"game:内置小游戏\"\r\n \"stock:查看自选股实时行情\"\r\n )\r\n _describe 'dskcode commands' commands\r\n}\r\ncompdef _dskcode_completion dskcode`);\r\n }\r\n });\r\n\r\n // stock — 股票行情\r\n program\r\n .command(\"stock\")\r\n .description(\"查看自选股实时行情\")\r\n .argument(\"[codes...]\", \"股票代码(空格分隔),如 513090 600519\")\r\n .action(async function (codes: string[]) {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const _ctx = (this as unknown as Record<string, unknown>).dskcodeCtx as DskcodeContext | undefined;\r\n\r\n // 检查用户全局配置文件是否已有自选股配置;没有则自动创建\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n const globalConfigPath = join(home, \".dskcode\", \"settings.json\");\r\n let globalConfigHasStock = false;\r\n try {\r\n const raw = await readFile(globalConfigPath, \"utf-8\");\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const parsed = JSON.parse(raw) as Record<string, unknown>;\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const stock = parsed.stock as Record<string, unknown> | undefined;\r\n globalConfigHasStock = Array.isArray(stock?.symbols) && (stock.symbols as unknown[]).length > 0;\r\n } catch {\r\n // 文件不存在 — 需要创建\r\n }\r\n\r\n if (!globalConfigHasStock) {\r\n const defaultSymbols = [\r\n { code: \"sh000001\" },\r\n { code: \"sz399300\" },\r\n { code: \"sh601899\" },\r\n ];\r\n const savedPath = await saveStockConfig(defaultSymbols);\r\n console.log(`${chalk.green(\"✔\")} 已生成自选股配置: ${chalk.dim(savedPath)}`);\r\n console.log(`${chalk.dim(\" 提示: 可编辑上述文件自定义自选股列表\")}\\n`);\r\n }\r\n\r\n // 加载配置(包含可能刚写入的自选股)\r\n const freshResult = await loadAndValidate();\r\n const codeList = codes && codes.length > 0\r\n ? codes\r\n : freshResult.config.stock?.symbols?.map((s) => s.code)\r\n ?? [\"sh000001\", \"sz399006\", \"sh601688\"];\r\n\r\n const app = renderApp(\r\n <StockList\r\n codes={codeList}\r\n onExit={() => process.exit(0)}\r\n />,\r\n );\r\n\r\n await app.waitUntilExit;\r\n });\r\n\r\n // game — 游戏模式\r\n initGames();\r\n\r\n program\r\n .command(\"game\")\r\n .description(\"启动内置小游戏\")\r\n .argument(\"[name]\", \"游戏名称,不指定则显示交互式游戏列表\")\r\n .action(async function (name?: string) {\r\n if (name) {\r\n const game = getGame(name);\r\n if (!game) {\r\n console.error(`未找到游戏 \"${name}\"。使用 dskcode game 查看可用游戏列表。`);\r\n process.exit(1);\r\n }\r\n console.log(`正在启动: ${game.name} — ${game.description}\\n`);\r\n await game.play();\r\n } else {\r\n const games = listGames();\r\n if (games.length === 0) {\r\n console.log(\"暂无可用游戏。\");\r\n return;\r\n }\r\n\r\n const selectedGame = await new Promise<Game | null>((resolve) => {\r\n const { unmount } = render(\r\n <GamePicker\r\n games={games}\r\n onSelect={(game) => {\r\n unmount();\r\n resolve(game);\r\n }}\r\n onExit={() => {\r\n unmount();\r\n resolve(null);\r\n }}\r\n />,\r\n { exitOnCtrlC: false },\r\n );\r\n });\r\n\r\n if (selectedGame) {\r\n console.log(`\\n 启动游戏: ${chalk.green(selectedGame.name)}\\n`);\r\n await selectedGame.play();\r\n }\r\n }\r\n });\r\n\r\n return program;\r\n}\r\n\r\nasync function startChat(\r\n ctx: DskcodeContext | undefined,\r\n costTracker: CostTracker,\r\n) {\r\n // 统计 skill 数量并获取详情列表,以及扫描项目文件\r\n const [globalSkillCount, localSkillCount, skills, files] = await Promise.all([\r\n countDskcodeSkills(),\r\n countProjectLocalSkills(process.cwd()),\r\n getAllSkills(process.cwd()),\r\n scanProjectFiles(process.cwd()),\r\n ]);\r\n const skillCount = globalSkillCount + localSkillCount;\r\n // 从配置中提取默认 Provider 的 apiKey 和 baseUrl\r\n const defaultProvider = ctx?.config.providers.find(\r\n (p) => p.name === (ctx?.config.defaultProvider ?? \"deepseek\"),\r\n );\r\n const model = defaultProvider?.model ?? \"deepseek-v4-flash\";\r\n const chatApp = renderApp(\r\n <ChatSession\r\n skillCount={skillCount}\r\n skills={skills}\r\n files={files}\r\n toolCount={ctx?.config.tools.length ?? 0}\r\n verbose={ctx?.verbose ?? false}\r\n apiKey={defaultProvider?.apiKey}\r\n baseUrl={defaultProvider?.baseUrl ?? \"https://api.deepseek.com\"}\r\n costTracker={costTracker}\r\n model={model}\r\n onLaunchGame={() => {\r\n chatApp.unmount();\r\n setImmediate(() => {\r\n initGames();\r\n const games = listGames();\r\n const { unmount } = render(\r\n <GamePicker\r\n games={games}\r\n onSelect={async (game: Game) => {\r\n unmount();\r\n await game.play();\r\n // 游戏结束后返回对话\r\n void startChat(ctx, costTracker);\r\n }}\r\n onBackToChat={() => {\r\n unmount();\r\n setImmediate(() => startChat(ctx, costTracker));\r\n }}\r\n />,\r\n { exitOnCtrlC: false },\r\n );\r\n });\r\n }}\r\n onLaunchStock={() => {\r\n chatApp.unmount();\r\n setImmediate(() => {\r\n // 使用配置中的自选股列表或兜底默认值\r\n const defaultStockCodes = ctx?.config.stock?.symbols?.map((s) => s.code)\r\n ?? [\"sh000001\", \"sz399006\", \"sh601688\"];\r\n const stockApp = renderApp(\r\n <StockList\r\n codes={defaultStockCodes}\r\n onBackToChat={() => {\r\n stockApp.unmount();\r\n setImmediate(() => startChat(ctx, costTracker));\r\n }}\r\n onExit={() => process.exit(0)}\r\n />,\r\n );\r\n });\r\n }}\r\n />,\r\n );\r\n}\r\n","import { existsSync, watch } from \"node:fs\";\r\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\r\nimport { join } from \"node:path\";\r\nimport type { Config, StockConfig } from \"./types.js\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 出厂默认配置\r\n// ---------------------------------------------------------------------------\r\n\r\nexport const defaultConfig: Config = {\r\n defaultProvider: \"deepseek\",\r\n maxTokens: 8192,\r\n temperature: 0.7,\r\n maxToolRounds: 20,\r\n providers: [\r\n {\r\n name: \"deepseek\",\r\n baseUrl: \"https://api.deepseek.com\",\r\n model: \"deepseek-v4-flash\",\r\n },\r\n ],\r\n tools: [\r\n { name: \"read_file\", enabled: true },\r\n { name: \"write_file\", enabled: true },\r\n { name: \"edit_file\", enabled: true },\r\n { name: \"bash\", enabled: true },\r\n { name: \"glob\", enabled: true },\r\n { name: \"grep\", enabled: true },\r\n { name: \"ls\", enabled: true },\r\n { name: \"fetch\", enabled: true },\r\n ],\r\n plugins: [],\r\n stock: {\r\n symbols: [\r\n { code: \"sh000001\" },\r\n { code: \"sz399300\" },\r\n { code: \"sh601899\" },\r\n ],\r\n },\r\n};\r\n\r\n// ---------------------------------------------------------------------------\r\n// 配置文件路径解析\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 判断一个字符串是否为合法的 URL(用于区分 local path 和 url)。\r\n * 目前暂未使用,保留以备 plugin 配置 URL 校验时使用。\r\n */\r\nfunction _isUrl(s: string): boolean {\r\n try {\r\n void new URL(s);\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * 返回候选配置文件路径列表。\r\n * 若传入了 --config 路径,则只使用该路径;\r\n * 否则依次检查用户全局目录和项目本地目录。\r\n */\r\nfunction resolveConfigFiles(configPath?: string): string[] {\r\n if (configPath) {\r\n return [configPath];\r\n }\r\n\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n return [\r\n join(home, \".dskcode\", \"settings.json\"),\r\n join(process.cwd(), \".dskcode\", \"settings.json\"),\r\n ];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 深度合并\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 将较高优先级的配置 overlay 合并到 base 之上。\r\n *\r\n * 合并规则:\r\n * - 标量字段(string / number / boolean):覆盖\r\n * - 数组字段(providers / tools / plugins):直接替换,不合并\r\n */\r\nfunction mergeConfig(base: Config, overlay: Partial<Config>): Config {\r\n const result: Config = { ...base };\r\n\r\n if (overlay.defaultProvider !== undefined) {\r\n result.defaultProvider = overlay.defaultProvider;\r\n }\r\n if (overlay.verbose !== undefined) {\r\n result.verbose = overlay.verbose;\r\n }\r\n if (overlay.maxTokens !== undefined) {\r\n result.maxTokens = overlay.maxTokens;\r\n }\r\n if (overlay.temperature !== undefined) {\r\n result.temperature = overlay.temperature;\r\n }\r\n if (overlay.maxToolRounds !== undefined) {\r\n result.maxToolRounds = overlay.maxToolRounds;\r\n }\r\n if (overlay.budgetLimit !== undefined) {\r\n result.budgetLimit = overlay.budgetLimit;\r\n }\r\n if (overlay.tokenBudgetLimit !== undefined) {\r\n result.tokenBudgetLimit = overlay.tokenBudgetLimit;\r\n }\r\n if (overlay.providers !== undefined) {\r\n result.providers = overlay.providers;\r\n }\r\n if (overlay.tools !== undefined) {\r\n result.tools = overlay.tools;\r\n }\r\n if (overlay.plugins !== undefined) {\r\n result.plugins = overlay.plugins;\r\n }\r\n if (overlay.stock !== undefined) {\r\n result.stock = overlay.stock;\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 环境变量解析\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 环境变量前缀 */\r\nconst ENV_PREFIX = \"DSKCODE_\";\r\n\r\n/** 支持的环境变量映射表 */\r\nconst ENV_MAP: Record<string, keyof Config> = {\r\n [`${ENV_PREFIX}DEFAULT_PROVIDER`]: \"defaultProvider\",\r\n [`${ENV_PREFIX}VERBOSE`]: \"verbose\",\r\n [`${ENV_PREFIX}MAX_TOKENS`]: \"maxTokens\",\r\n [`${ENV_PREFIX}TEMPERATURE`]: \"temperature\",\r\n [`${ENV_PREFIX}MAX_TOOL_ROUNDS`]: \"maxToolRounds\",\r\n [`${ENV_PREFIX}BUDGET_LIMIT`]: \"budgetLimit\",\r\n [`${ENV_PREFIX}TOKEN_BUDGET_LIMIT`]: \"tokenBudgetLimit\",\r\n};\r\n\r\n/**\r\n * 将环境变量中读取的值覆盖到配置上。\r\n * 环境变量的优先级高于 TOML 文件,但低于 CLI flag。\r\n * 返回一个新的配置对象,不修改原始配置。\r\n */\r\nfunction applyEnvVars(config: Config): Config {\r\n // 浅拷贝以避免修改原始对象\r\n const result: Config = { ...config, providers: [...config.providers] };\r\n\r\n // 1. DSKCODE_* 前缀的环境变量\r\n for (const [envKey, configKey] of Object.entries(ENV_MAP)) {\r\n const raw = process.env[envKey];\r\n if (raw === undefined) continue;\r\n\r\n switch (configKey) {\r\n case \"verbose\":\r\n case \"defaultProvider\": {\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n (result as unknown as Record<string, unknown>)[configKey] = raw;\r\n break;\r\n }\r\n case \"maxTokens\":\r\n case \"maxToolRounds\":\r\n case \"budgetLimit\":\r\n case \"tokenBudgetLimit\": {\r\n const n = Number(raw);\r\n if (Number.isFinite(n) && n >= 0) {\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n (result as unknown as Record<string, unknown>)[configKey] = n;\r\n }\r\n break;\r\n }\r\n case \"temperature\": {\r\n const n = Number(raw);\r\n if (Number.isFinite(n) && n >= 0 && n <= 2) {\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n (result as unknown as Record<string, unknown>)[configKey] = n;\r\n }\r\n break;\r\n }\r\n }\r\n }\r\n\r\n // 2. DEEPSEEK_API_KEY — 注入到名为 deepseek 的 provider\r\n const apiKey = process.env.DEEPSEEK_API_KEY;\r\n if (apiKey) {\r\n const deepseekIdx = result.providers.findIndex((p) => p.name === \"deepseek\");\r\n if (deepseekIdx !== -1) {\r\n const existing = result.providers[deepseekIdx]!;\r\n if (!existing.apiKey) {\r\n result.providers[deepseekIdx] = { ...existing, apiKey };\r\n }\r\n } else {\r\n // 如果没有 deepseek provider,自动创建一个\r\n result.providers.unshift({\r\n name: \"deepseek\",\r\n baseUrl: \"https://api.deepseek.com\",\r\n model: \"deepseek-v4-flash\",\r\n apiKey,\r\n });\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// CLI flag 覆盖\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface CliFlags {\r\n verbose?: boolean;\r\n model?: string;\r\n maxTokens?: number;\r\n temperature?: number;\r\n budgetLimit?: number;\r\n tokenBudgetLimit?: number;\r\n}\r\n\r\n/**\r\n * 将 CLI flag 中的值覆盖到配置上。\r\n * CLI flag 的优先级最高。\r\n * 返回一个新的配置对象,不修改原始配置。\r\n */\r\nexport function applyCliOverrides(config: Config, flags: CliFlags): Config {\r\n const result: Config = { ...config, providers: [...config.providers] };\r\n\r\n if (flags.verbose !== undefined) {\r\n result.verbose = flags.verbose;\r\n }\r\n if (flags.model !== undefined) {\r\n // 将 --model 的值映射为标准 model 名称\r\n // 如果用户指定了 --model,覆盖 defaultProvider 中配置的 model\r\n const providerIdx = result.providers.findIndex(\r\n (p) => p.name === result.defaultProvider,\r\n );\r\n if (providerIdx !== -1) {\r\n result.providers[providerIdx] = {\r\n ...result.providers[providerIdx]!,\r\n model: flags.model,\r\n };\r\n }\r\n }\r\n if (flags.maxTokens !== undefined && flags.maxTokens > 0) {\r\n result.maxTokens = flags.maxTokens;\r\n }\r\n if (\r\n flags.temperature !== undefined &&\r\n flags.temperature >= 0 &&\r\n flags.temperature <= 2\r\n ) {\r\n result.temperature = flags.temperature;\r\n }\r\n if (flags.budgetLimit !== undefined && flags.budgetLimit >= 0) {\r\n result.budgetLimit = flags.budgetLimit;\r\n }\r\n if (flags.tokenBudgetLimit !== undefined && flags.tokenBudgetLimit >= 0) {\r\n result.tokenBudgetLimit = flags.tokenBudgetLimit;\r\n }\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 配置校验\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface ConfigError {\r\n field: string;\r\n message: string;\r\n}\r\n\r\n/**\r\n * 校验配置的合法性,返回错误列表。\r\n * 返回空数组表示配置合法。\r\n */\r\nexport function validateConfig(config: Config): ConfigError[] {\r\n const errors: ConfigError[] = [];\r\n\r\n // 1. 至少需要一个 Provider\r\n if (!config.providers || config.providers.length === 0) {\r\n errors.push({\r\n field: \"providers\",\r\n message: \"至少需要配置一个 Provider。请通过配置文件或 DEEPSEEK_API_KEY 环境变量设置。\",\r\n });\r\n }\r\n\r\n // 2. 每个 Provider 必须有 name 和 model,且 model 必须受支持\r\n const SUPPORTED_MODELS = [\"deepseek-v4-flash\", \"deepseek-v4-pro\"];\r\n for (let i = 0; i < config.providers.length; i++) {\r\n const p = config.providers[i]!;\r\n if (!p.name) {\r\n errors.push({\r\n field: `providers[${i}].name`,\r\n message: `第 ${i + 1} 个 Provider 缺少 name 字段。`,\r\n });\r\n }\r\n if (!p.model) {\r\n errors.push({\r\n field: `providers[${i}].model`,\r\n message: `Provider \"${p.name || i}\" 缺少 model 字段。`,\r\n });\r\n } else if (!SUPPORTED_MODELS.includes(p.model)) {\r\n errors.push({\r\n field: `providers[${i}].model`,\r\n message: `Provider \"${p.name || i}\" 的 model \"${p.model}\" 不受支持。dskcode 仅支持: ${SUPPORTED_MODELS.join(\", \")}`,\r\n });\r\n }\r\n }\r\n\r\n // 3. defaultProvider 必须存在于 providers 列表中\r\n if (config.defaultProvider) {\r\n const exists = config.providers.some(\r\n (p) => p.name === config.defaultProvider,\r\n );\r\n if (!exists) {\r\n errors.push({\r\n field: \"defaultProvider\",\r\n message: `默认 Provider \"${config.defaultProvider}\" 未在 providers 中定义。`,\r\n });\r\n }\r\n }\r\n\r\n // 4. temperature 范围校验\r\n if (\r\n config.temperature !== undefined &&\r\n (config.temperature < 0 || config.temperature > 2)\r\n ) {\r\n errors.push({\r\n field: \"temperature\",\r\n message: \"temperature 必须在 0.0 ~ 2.0 之间。\",\r\n });\r\n }\r\n\r\n // 5. maxTokens 范围校验\r\n if (config.maxTokens !== undefined && config.maxTokens < 1) {\r\n errors.push({\r\n field: \"maxTokens\",\r\n message: \"maxTokens 必须大于等于 1。\",\r\n });\r\n }\r\n\r\n // 6. maxToolRounds 范围校验\r\n if (config.maxToolRounds !== undefined && config.maxToolRounds < 1) {\r\n errors.push({\r\n field: \"maxToolRounds\",\r\n message: \"maxToolRounds 必须大于等于 1。\",\r\n });\r\n }\r\n\r\n // 7. budgetLimit 范围校验\r\n if (config.budgetLimit !== undefined && config.budgetLimit < 0) {\r\n errors.push({\r\n field: \"budgetLimit\",\r\n message: \"budgetLimit 必须大于等于 0(0 表示不限制)。\",\r\n });\r\n }\r\n\r\n // 8. tokenBudgetLimit 范围校验\r\n if (config.tokenBudgetLimit !== undefined && config.tokenBudgetLimit < 0) {\r\n errors.push({\r\n field: \"tokenBudgetLimit\",\r\n message: \"tokenBudgetLimit 必须大于等于 0(0 表示不限制)。\",\r\n });\r\n }\r\n\r\n return errors;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 核心加载流程\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 从多级配置源加载并合并配置。\r\n *\r\n * 解析顺序(后加载的优先级更高):\r\n * 1. 内置默认值 —— defaultConfig\r\n * 2. 用户全局 —— ~/.dskcode/settings.json\r\n * 3. 项目本地 —— .dskcode/settings.json(或通过 --config 指定的路径)\r\n * 4. 环境变量 —— DEEPSEEK_API_KEY、DSKCODE_* 等\r\n * 5. CLI flag —— 由调用方通过 applyCliOverrides() 单独注入\r\n */\r\nexport async function loadConfig(configPath?: string): Promise<Config> {\r\n const filePaths = resolveConfigFiles(configPath);\r\n\r\n let config: Config = structuredClone(defaultConfig);\r\n\r\n // 1-3. 依次加载 JSON 配置文件\r\n for (const filePath of filePaths) {\r\n try {\r\n const raw = await readFile(filePath, \"utf-8\");\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n const parsed = JSON.parse(raw) as Partial<Config>;\r\n config = mergeConfig(config, parsed);\r\n } catch {\r\n // 文件不存在或权限不足 — 静默跳过\r\n }\r\n }\r\n\r\n // 4. 环境变量覆盖\r\n config = applyEnvVars(config);\r\n\r\n return config;\r\n}\r\n\r\n/**\r\n * 加载配置并同时执行校验。\r\n * 校验错误不会 throw,而是通过返回值中的 errors 字段返回,\r\n * 由调用方决定如何处理(例如在 middleware 中输出警告)。\r\n */\r\nexport async function loadAndValidate(\r\n configPath?: string,\r\n): Promise<{ config: Config; errors: ConfigError[] }> {\r\n const config = await loadConfig(configPath);\r\n const errors = validateConfig(config);\r\n return { config, errors };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 配置热加载(Watch 模式)\r\n// ---------------------------------------------------------------------------\r\n\r\nexport type ConfigChangeCallback = (config: Config) => void;\r\n\r\n/**\r\n * 监听配置文件变更,在文件被修改时重新加载配置并调用回调。\r\n *\r\n * @param callback 配置变更后的回调函数\r\n * @param configPath 可选,指定配置文件路径(对应 --config flag)\r\n * @returns 一个 unwatch 函数,调用后可停止监听\r\n */\r\nexport function watchConfig(\r\n callback: ConfigChangeCallback,\r\n configPath?: string,\r\n): () => void {\r\n const filePaths = resolveConfigFiles(configPath).filter((fp) => existsSync(fp));\r\n\r\n // 如果一个文件都不存在,则监听项目本地的 .dskcode/settings.json(即使还没创建)\r\n if (filePaths.length === 0) {\r\n filePaths.push(join(process.cwd(), \".dskcode\", \"settings.json\"));\r\n }\r\n\r\n const watchers: ReturnType<typeof watch>[] = [];\r\n let debounceTimer: ReturnType<typeof setTimeout>;\r\n\r\n for (const filePath of filePaths) {\r\n try {\r\n const watcher = watch(filePath, (eventType) => {\r\n if (eventType !== \"change\") return;\r\n\r\n // 防抖:多次连续变更只触发一次\r\n if (debounceTimer) clearTimeout(debounceTimer);\r\n debounceTimer = setTimeout(() => {\r\n void (async () => {\r\n try {\r\n const config = await loadConfig(configPath);\r\n callback(config);\r\n } catch {\r\n // 重载失败时不回调,等待下一次变更\r\n }\r\n })();\r\n }, 300);\r\n });\r\n\r\n watchers.push(watcher);\r\n } catch {\r\n // 无法监听的文件(例如还不存在)— 跳过\r\n }\r\n }\r\n\r\n return () => {\r\n if (debounceTimer) clearTimeout(debounceTimer);\r\n for (const w of watchers) {\r\n w.close();\r\n }\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// API Key 持久化\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 将 API Key 保存到用户全局配置 ~/.dskcode/settings.json。\r\n * 如果文件已存在,合并写入;不存在则新建。\r\n * 返回保存的文件路径。\r\n */\r\nexport async function saveApiKey(apiKey: string): Promise<string> {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n const configDir = join(home, \".dskcode\");\r\n const configFile = join(configDir, \"settings.json\");\r\n\r\n // 确保目录存在\r\n await mkdir(configDir, { recursive: true });\r\n\r\n // 读取现有配置,或从默认配置开始\r\n let configData: Record<string, unknown>;\r\n try {\r\n const raw = await readFile(configFile, \"utf-8\");\r\n configData = JSON.parse(raw);\r\n } catch {\r\n // 文件不存在,用内置默认值填充(tools、plugins 等都会写入)\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n configData = structuredClone(defaultConfig) as unknown as Record<string, unknown>;\r\n }\r\n\r\n // 更新或创建 deepseek provider\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n const providers = (configData.providers as Record<string, unknown>[]) ?? [];\r\n const existing = providers.find((p) => p.name === \"deepseek\");\r\n\r\n if (existing) {\r\n existing.apiKey = apiKey;\r\n } else {\r\n providers.push({\r\n name: \"deepseek\",\r\n apiKey,\r\n baseUrl: \"https://api.deepseek.com\",\r\n model: \"deepseek-v4-flash\",\r\n });\r\n }\r\n\r\n configData.providers = providers;\r\n\r\n // 写回文件\r\n await writeFile(configFile, JSON.stringify(configData, null, 2), \"utf-8\");\r\n\r\n return configFile;\r\n}\r\n\r\n/**\r\n * 将模型偏好保存到用户全局配置 ~/.dskcode/settings.json。\r\n * 只更新 defaultProvider 的 model 字段,不覆盖其他配置。\r\n * 返回保存的文件路径。\r\n */\r\nexport async function saveModelConfig(model: string): Promise<string> {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n const configDir = join(home, \".dskcode\");\r\n const configFile = join(configDir, \"settings.json\");\r\n\r\n // 确保目录存在\r\n await mkdir(configDir, { recursive: true });\r\n\r\n // 读取现有配置\r\n let configData: Record<string, unknown>;\r\n try {\r\n const raw = await readFile(configFile, \"utf-8\");\r\n configData = JSON.parse(raw);\r\n } catch {\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n configData = structuredClone(defaultConfig) as unknown as Record<string, unknown>;\r\n }\r\n\r\n // 更新 defaultProvider 的 model\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n const providers = (configData.providers as Record<string, unknown>[]) ?? [];\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n const defaultProviderName = (configData.defaultProvider as string) ?? \"deepseek\";\r\n const existing = providers.find((p) => p.name === defaultProviderName);\r\n\r\n if (existing) {\r\n existing.model = model;\r\n } else {\r\n // 没有 default provider,创建一个\r\n providers.push({\r\n name: defaultProviderName,\r\n baseUrl: \"https://api.deepseek.com\",\r\n model,\r\n });\r\n }\r\n\r\n configData.providers = providers;\r\n\r\n // 写回文件\r\n await writeFile(configFile, JSON.stringify(configData, null, 2), \"utf-8\");\r\n\r\n return configFile;\r\n}\r\n\r\n/**\r\n * 将自选股配置保存到用户全局配置 ~/.dskcode/settings.json。\r\n * 如果文件已存在,合并写入;不存在则新建。\r\n * 返回保存的文件路径。\r\n */\r\nexport async function saveStockConfig(symbols: StockConfig[\"symbols\"]): Promise<string> {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n const configDir = join(home, \".dskcode\");\r\n const configFile = join(configDir, \"settings.json\");\r\n\r\n // 确保目录存在\r\n await mkdir(configDir, { recursive: true });\r\n\r\n // 读取现有配置,或从默认配置开始\r\n let configData: Record<string, unknown>;\r\n try {\r\n const raw = await readFile(configFile, \"utf-8\");\r\n configData = JSON.parse(raw);\r\n } catch {\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n configData = structuredClone(defaultConfig) as unknown as Record<string, unknown>;\r\n }\r\n\r\n // 写入自选股配置\r\n configData.stock = { symbols };\r\n\r\n // 写回文件\r\n await writeFile(configFile, JSON.stringify(configData, null, 2), \"utf-8\");\r\n\r\n return configFile;\r\n}\r\n","import type { Command } from \"commander\";\r\nimport {\r\n loadAndValidate,\r\n applyCliOverrides,\r\n defaultConfig,\r\n} from \"../config/index.js\";\r\nimport type { Config } from \"../config/index.js\";\r\n\r\n/**\r\n * dskcode 运行时上下文。\r\n * 通过 commander 的 preAction hook 注入到每个命令中。\r\n */\r\nexport interface DskcodeContext {\r\n config: Config;\r\n verbose: boolean;\r\n}\r\n\r\n/**\r\n * 在 preAction hook 中加载配置并构造上下文。\r\n *\r\n * 完整的配置解析流水线:\r\n * 1. 内置默认值 —— defaultConfig\r\n * 2. 用户全局 —— ~/.dskcode/settings.json\r\n * 3. 项目本地 —— .dskcode/settings.json(或 --config 指定的路径)\r\n * 4. 环境变量 —— DEEPSEEK_API_KEY / DSKCODE_*\r\n * 5. CLI flag —— --verbose / --model 等\r\n */\r\nexport async function loadConfigMiddleware(\r\n this: Command,\r\n): Promise<DskcodeContext> {\r\n // eslint-disable-next-line oxc/no-this-in-exported-function, @typescript-eslint/no-unsafe-type-assertion\r\n const opts = this.optsWithGlobals() as {\r\n verbose?: boolean;\r\n config?: string;\r\n model?: string;\r\n };\r\n const verbose = opts.verbose ?? false;\r\n\r\n // 1-4. 加载 TOML 文件 + 环境变量\r\n let config: Config;\r\n const errors: string[] = [];\r\n try {\r\n const result = await loadAndValidate(opts.config);\r\n config = result.config;\r\n if (result.errors.length > 0) {\r\n for (const e of result.errors) {\r\n errors.push(e.message);\r\n }\r\n }\r\n } catch {\r\n config = structuredClone(defaultConfig);\r\n }\r\n\r\n // 5. CLI flag 覆盖(优先级最高)\r\n config = applyCliOverrides(config, {\r\n verbose,\r\n model: opts.model,\r\n });\r\n\r\n // 校验错误输出(不阻断执行)\r\n if (errors.length > 0 && verbose) {\r\n for (const msg of errors) {\r\n console.error(` ⚠ ${msg}`);\r\n }\r\n }\r\n\r\n return { config, verbose };\r\n}\r\n","import type { Command } from \"commander\";\r\nimport chalk from \"chalk\";\r\n\r\nexport function customHelp(program: Command): string {\r\n const lines: string[] = [];\r\n\r\n lines.push(\"\");\r\n lines.push(chalk.bold(\"用法:\"));\r\n lines.push(` ${chalk.cyan(\"dskcode\")} ${chalk.dim(\"[global-options]\")} ${chalk.green(\"<command>\")} ${chalk.dim(\"[options]\")}`);\r\n lines.push(\"\");\r\n\r\n const globalOpts = program.options.filter(\r\n (o) => o.long !== \"--help\" && o.long !== \"--version\" && o.long !== \"--config\",\r\n );\r\n if (globalOpts.length > 0) {\r\n lines.push(chalk.bold(\"全局选项:\"));\r\n for (const opt of globalOpts) {\r\n const flags = [opt.short, opt.long].filter(Boolean).join(\", \");\r\n lines.push(` ${chalk.cyan(flags.padEnd(24))} ${opt.description ?? \"\"}`);\r\n }\r\n lines.push(\"\");\r\n }\r\n\r\n lines.push(chalk.bold(\"内置选项:\"));\r\n for (const flag of [\"-h, --help\", \"-V, --version\"]) {\r\n const opt = program.options.find(\r\n (o) => o.long === (flag.includes(\"help\") ? \"--help\" : \"--version\"),\r\n );\r\n if (opt) {\r\n lines.push(` ${chalk.cyan(flag.padEnd(24))} ${opt.description ?? \"\"}`);\r\n }\r\n }\r\n lines.push(\"\");\r\n\r\n const cmds = program.commands.filter((c) => !c.name().startsWith(\"help\"));\r\n if (cmds.length > 0) {\r\n lines.push(chalk.bold(\"命令:\"));\r\n for (const cmd of cmds) {\r\n lines.push(` ${chalk.green(cmd.name().padEnd(24))} ${cmd.description()}`);\r\n }\r\n lines.push(\"\");\r\n }\r\n\r\n lines.push(chalk.bold(\"示例:\"));\r\n lines.push(` ${chalk.dim(\"# 启动交互式对话\")}`);\r\n lines.push(\" dskcode chat\");\r\n lines.push(` ${chalk.dim(\"# 让 AI 执行一个任务\")}`);\r\n lines.push(\" dskcode run 修改所有 TODO 注释\");\r\n lines.push(` ${chalk.dim(\"# 运行配置向导\")}`);\r\n lines.push(\" dskcode setup\");\r\n lines.push(` ${chalk.dim(\"# 生成 shell 自动补全\")}`);\r\n lines.push(\" dskcode completion\");\r\n lines.push(` ${chalk.dim(\"# 查看自选股行情\")}`);\r\n lines.push(\" dskcode stock\");\r\n lines.push(` ${chalk.dim(\"# 查看指定股票行情\")}`);\r\n lines.push(\" dskcode stock sh513090 sz000001\");\r\n lines.push(\"\");\r\n\r\n return lines.join(\"\\n\");\r\n}\r\n","import { createInterface } from \"node:readline\";\r\nimport chalk from \"chalk\";\r\n\r\n/**\r\n * 检测是否有可用的 API Key。\r\n * 遍历所有 provider 检查是否配置了 apiKey,同时检查 DEEPSEEK_API_KEY 环境变量。\r\n */\r\nexport function hasApiKey(providers: { apiKey?: string }[]): boolean {\r\n if (providers.some((p) => p.apiKey)) return true;\r\n if (process.env.DEEPSEEK_API_KEY) return true;\r\n return false;\r\n}\r\n\r\n/**\r\n * API Key 最小长度校验值。\r\n * DeepSeek 的 Key 通常以 sk- 开头,长度远超 10 位。\r\n */\r\nconst MIN_API_KEY_LENGTH = 10;\r\n\r\n/**\r\n * 交互式提示用户输入 DeepSeek API Key。\r\n * 使用 Node readline 的 password 模式(输入不可见)。\r\n * 返回用户输入的 Key,如果用户取消则返回 null。\r\n */\r\nexport async function promptForApiKey(): Promise<string | null> {\r\n console.log(chalk.yellow(\"\\n ⚠ 未检测到 API Key 配置\"));\r\n console.log(chalk.dim(\" 你可以通过以下任一方式配置:\"));\r\n console.log(chalk.dim(\" · 环境变量: export DEEPSEEK_API_KEY=sk-xxx\"));\r\n console.log(chalk.dim(\" · 配置文件: ~/.dskcode/settings.json\"));\r\n console.log(chalk.dim(\" · 下面直接输入,自动保存到全局配置\\n\"));\r\n\r\n const rl = createInterface({\r\n input: process.stdin,\r\n output: process.stdout,\r\n });\r\n\r\n return new Promise<string | null>((resolve) => {\r\n let resolved = false;\r\n\r\n const cleanup = () => {\r\n if (resolved) return;\r\n resolved = true;\r\n process.stdin.removeListener(\"keypress\", onKeypress);\r\n rl.close();\r\n };\r\n\r\n const onKeypress = (_: unknown, key: { ctrl?: boolean; name?: string }) => {\r\n if (key.ctrl && key.name === \"c\") {\r\n cleanup();\r\n resolve(null);\r\n }\r\n };\r\n\r\n process.stdin.on(\"keypress\", onKeypress);\r\n\r\n rl.question(\r\n ` ${chalk.cyan(\"🔑\")} ${chalk.bold(\"请输入你的 DeepSeek API Key:\")} `,\r\n (answer) => {\r\n cleanup();\r\n const trimmed = answer.trim();\r\n if (!trimmed) {\r\n console.log(chalk.red(\" ✖ API Key 不能为空\"));\r\n resolve(null);\r\n return;\r\n }\r\n if (trimmed.length < MIN_API_KEY_LENGTH) {\r\n console.log(chalk.red(\" ✖ API Key 格式不正确,长度至少 10 位\"));\r\n resolve(null);\r\n return;\r\n }\r\n resolve(trimmed);\r\n },\r\n );\r\n });\r\n}\r\n","import { createInterface } from \"node:readline\";\r\nimport { existsSync, statSync, lstatSync } from \"node:fs\";\r\nimport { mkdir, readdir, cp, access, realpath, readFile } from \"node:fs/promises\";\r\nimport { join } from \"node:path\";\r\nimport chalk from \"chalk\";\r\n\r\n/**\r\n * Claude Code 全局 skills 目录路径(~/.claude/skills)。\r\n * 跨平台兼容:优先用 HOME,Windows 下回退到 USERPROFILE。\r\n */\r\nfunction getClaudeSkillsDir(): string {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n return join(home, \".claude\", \"skills\");\r\n}\r\n\r\n/**\r\n * dskcode 全局 skills 目录路径(~/.dskcode/skills)。\r\n */\r\nfunction getDskcodeSkillsDir(): string {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n return join(home, \".dskcode\", \"skills\");\r\n}\r\n\r\n/**\r\n * 判断某个目录是否为一个有效的 skill(包含 SKILL.md)。\r\n */\r\nasync function isSkillDir(dir: string): Promise<boolean> {\r\n try {\r\n await access(join(dir, \"SKILL.md\"));\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * 列出 Claude Code skills 目录下所有有效的 skill 名称(子目录包含 SKILL.md)。\r\n * 找不到目录返回空数组。\r\n */\r\nexport async function listClaudeSkills(): Promise<string[]> {\r\n const claudeDir = getClaudeSkillsDir();\r\n if (!existsSync(claudeDir)) return [];\r\n\r\n let entries: string[];\r\n try {\r\n entries = await readdir(claudeDir);\r\n } catch {\r\n return [];\r\n }\r\n\r\n const skills: string[] = [];\r\n for (const name of entries) {\r\n const full = join(claudeDir, name);\r\n const stat = statSync(full);\r\n if (stat.isDirectory() && (await isSkillDir(full))) {\r\n skills.push(name);\r\n }\r\n }\r\n return skills;\r\n}\r\n\r\n/**\r\n * 是否存在可导入的 Claude skills。\r\n */\r\nexport async function hasClaudeSkills(): Promise<boolean> {\r\n const skills = await listClaudeSkills();\r\n return skills.length > 0;\r\n}\r\n\r\n/**\r\n * 交互式询问用户是否确认。默认接受回车视为\"否\"。\r\n * 返回 true 表示用户确认。\r\n */\r\nfunction askConfirm(question: string): Promise<boolean> {\r\n const rl = createInterface({\r\n input: process.stdin,\r\n output: process.stdout,\r\n });\r\n\r\n return new Promise<boolean>((resolve) => {\r\n let resolved = false;\r\n\r\n const cleanup = () => {\r\n if (resolved) return;\r\n resolved = true;\r\n process.stdin.removeListener(\"keypress\", onKeypress);\r\n rl.close();\r\n };\r\n\r\n const onKeypress = (_: unknown, key: { ctrl?: boolean; name?: string }) => {\r\n if (key.ctrl && key.name === \"c\") {\r\n cleanup();\r\n resolve(false);\r\n }\r\n };\r\n\r\n process.stdin.on(\"keypress\", onKeypress);\r\n\r\n rl.question(question, (answer) => {\r\n cleanup();\r\n const trimmed = answer.trim().toLowerCase();\r\n // 默认回车 = 否;明确输入 y / yes 才算确认\r\n resolve(trimmed === \"y\" || trimmed === \"yes\");\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * 将 Claude skills 目录复制到 ~/.dskcode/skills。\r\n * 已存在的同名 skill 跳过,避免覆盖用户自定义修改。\r\n * 返回 { imported, skipped } 两个数组。\r\n */\r\nexport async function importClaudeSkills(\r\n skillNames: string[],\r\n): Promise<{ imported: string[]; skipped: string[] }> {\r\n const claudeDir = getClaudeSkillsDir();\r\n const dskcodeDir = getDskcodeSkillsDir();\r\n\r\n await mkdir(dskcodeDir, { recursive: true });\r\n\r\n const imported: string[] = [];\r\n const skipped: string[] = [];\r\n\r\n for (const name of skillNames) {\r\n const src = join(claudeDir, name);\r\n const dest = join(dskcodeDir, name);\r\n\r\n if (existsSync(dest)) {\r\n skipped.push(name);\r\n continue;\r\n }\r\n\r\n // 递归复制整个 skill 目录,保留原始结构(SKILL.md、reference 等)。\r\n // 两个解引用手段叠加:\r\n // 1) realpath() 解决 source 顶层是 symlink 的情况(cp 的 dereference 只处理源目录内部 symlink)\r\n // 2) cp 的 dereference: true 解决源目录内部遇到的 symlink\r\n // 这样可以避开 Windows 上非管理员无法创建 symlink 的 EPERM 错误\r\n // (Claude Code 的 skill 目录往往是 symlink 指向 ~/.agents/skills)。\r\n const realSrc = lstatSync(src).isSymbolicLink() ? await realpath(src) : src;\r\n await cp(realSrc, dest, { recursive: true, dereference: true });\r\n imported.push(name);\r\n }\r\n\r\n return { imported, skipped };\r\n}\r\n\r\n/**\r\n * 统计全局 ~/.dskcode/skills 下的有效 skill 数量。\r\n */\r\nexport async function countDskcodeSkills(): Promise<number> {\r\n const dskcodeDir = getDskcodeSkillsDir();\r\n if (!existsSync(dskcodeDir)) return 0;\r\n\r\n let entries: string[];\r\n try {\r\n entries = await readdir(dskcodeDir);\r\n } catch {\r\n return 0;\r\n }\r\n\r\n let count = 0;\r\n for (const name of entries) {\r\n const full = join(dskcodeDir, name);\r\n if (statSync(full).isDirectory() && (await isSkillDir(full))) {\r\n count++;\r\n }\r\n }\r\n return count;\r\n}\r\n\r\n/**\r\n * 项目本地 skill 目录路径({cwd}/.dskcode/skill)。\r\n */\r\nexport function getProjectSkillDir(cwd: string): string {\r\n return join(cwd, \".dskcode\", \"skill\");\r\n}\r\n\r\n/**\r\n * 检测项目本地 .dskcode/skill 目录下是否存在有效 skill。\r\n */\r\nexport async function hasProjectLocalSkills(cwd: string): Promise<boolean> {\r\n const count = await countProjectLocalSkills(cwd);\r\n return count > 0;\r\n}\r\n\r\n/**\r\n * 统计项目本地 .dskcode/skill 下的有效 skill 数量。\r\n */\r\nexport async function countProjectLocalSkills(cwd: string): Promise<number> {\r\n const skillDir = getProjectSkillDir(cwd);\r\n if (!existsSync(skillDir)) return 0;\r\n\r\n let entries: string[];\r\n try {\r\n entries = await readdir(skillDir);\r\n } catch {\r\n return 0;\r\n }\r\n\r\n let count = 0;\r\n for (const name of entries) {\r\n const full = join(skillDir, name);\r\n const stat = statSync(full);\r\n if (stat.isDirectory() && (await isSkillDir(full))) {\r\n count++;\r\n }\r\n }\r\n return count;\r\n}\r\n\r\n/**\r\n * 检测全局 ~/.dskcode/skills 目录下是否存在有效 skill。\r\n */\r\nexport async function hasDskcodeSkills(): Promise<boolean> {\r\n const dskcodeDir = getDskcodeSkillsDir();\r\n if (!existsSync(dskcodeDir)) return false;\r\n\r\n let entries: string[];\r\n try {\r\n entries = await readdir(dskcodeDir);\r\n } catch {\r\n return false;\r\n }\r\n\r\n for (const name of entries) {\r\n const full = join(dskcodeDir, name);\r\n const stat = statSync(full);\r\n if (stat.isDirectory() && (await isSkillDir(full))) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n}\r\n\r\n/**\r\n * 主流程:检测已有 skill 并决定是否提示导入 Claude Code skill。\r\n * 通常在 chat 命令的 API Key 检查之后调用。\r\n *\r\n * 规则(按优先级):\r\n * 1. 如果项目本地 .dskcode/skill 下有 skill,跳过导入\r\n * 2. 如果全局 ~/.dskcode/skills 下有 skill(已导入过),跳过导入\r\n * 3. 否则检测 Claude Code 并询问是否导入\r\n */\r\nexport async function promptImportClaudeSkills(cwd?: string): Promise<void> {\r\n // 检查项目本地 .dskcode/skill\r\n if (cwd) {\r\n const hasLocal = await hasProjectLocalSkills(cwd);\r\n if (hasLocal) {\r\n console.log(chalk.dim(\" 检测到项目本地 .dskcode/skill,跳过导入 Claude Code skill\\n\"));\r\n return;\r\n }\r\n }\r\n\r\n // 检查全局 ~/.dskcode/skills(已经导入过的无需再提示)\r\n const hasGlobal = await hasDskcodeSkills();\r\n if (hasGlobal) {\r\n return;\r\n }\r\n\r\n const skills = await listClaudeSkills();\r\n if (skills.length === 0) return;\r\n\r\n console.log(chalk.cyan(`\\n ✦ 检测到你在 Claude Code 中安装了 ${String(skills.length)} 个 skill:`));\r\n // 每行最多展示 5 个,避免列表过长\r\n const preview = skills.slice(0, 5).map((s) => chalk.dim(` · ${s}`)).join(\"\\n\");\r\n console.log(preview);\r\n if (skills.length > 5) {\r\n console.log(chalk.dim(` · ...等 ${String(skills.length)} 个`));\r\n }\r\n console.log(chalk.dim(` 源目录: ${getClaudeSkillsDir()}`));\r\n console.log(chalk.dim(` 目标目录: ${getDskcodeSkillsDir()}\\n`));\r\n\r\n const confirmed = await askConfirm(\r\n ` ${chalk.cyan(\"📦\")} 是否将这些 skill 导入到 dskcode?${chalk.dim(\"[y/N] \")} `,\r\n );\r\n\r\n if (!confirmed) {\r\n console.log(chalk.dim(\" 已跳过 skill 导入\\n\"));\r\n return;\r\n }\r\n\r\n try {\r\n const { imported, skipped } = await importClaudeSkills(skills);\r\n if (imported.length > 0) {\r\n console.log(\r\n chalk.green(` ✔ 已导入 ${String(imported.length)} 个 skill 到 ~/.dskcode/skills`),\r\n );\r\n }\r\n if (skipped.length > 0) {\r\n console.log(\r\n chalk.yellow(` ⚠ ${String(skipped.length)} 个 skill 已存在,已跳过:${skipped.join(\", \")}`),\r\n );\r\n }\r\n console.log(\"\");\r\n } catch (err) {\r\n console.log(\r\n chalk.red(` ✖ 导入 skill 失败: ${err instanceof Error ? err.message : String(err)}`),\r\n );\r\n console.log(chalk.dim(\" 你可以稍后手动复制 ~/.claude/skills 到 ~/.dskcode/skills\\n\"));\r\n }\r\n}\r\n\r\n/** 单个 skill 的展示信息 */\r\nexport interface SkillInfo {\r\n name: string;\r\n description: string;\r\n}\r\n\r\n/**\r\n * 列出全局 ~/.dskcode/skills 下所有有效的 skill 名称。\r\n */\r\nexport async function listDskcodeSkills(): Promise<string[]> {\r\n const dskcodeDir = getDskcodeSkillsDir();\r\n if (!existsSync(dskcodeDir)) return [];\r\n\r\n let entries: string[];\r\n try {\r\n entries = await readdir(dskcodeDir);\r\n } catch {\r\n return [];\r\n }\r\n\r\n const skills: string[] = [];\r\n for (const name of entries) {\r\n const full = join(dskcodeDir, name);\r\n if (statSync(full).isDirectory() && (await isSkillDir(full))) {\r\n skills.push(name);\r\n }\r\n }\r\n return skills;\r\n}\r\n\r\n/**\r\n * 列出项目本地 .dskcode/skill 下所有有效的 skill 名称。\r\n */\r\nexport async function listProjectSkills(cwd: string): Promise<string[]> {\r\n const skillDir = getProjectSkillDir(cwd);\r\n if (!existsSync(skillDir)) return [];\r\n\r\n let entries: string[];\r\n try {\r\n entries = await readdir(skillDir);\r\n } catch {\r\n return [];\r\n }\r\n\r\n const skills: string[] = [];\r\n for (const name of entries) {\r\n const full = join(skillDir, name);\r\n if (statSync(full).isDirectory() && (await isSkillDir(full))) {\r\n skills.push(name);\r\n }\r\n }\r\n return skills;\r\n}\r\n\r\n/**\r\n * 解析 SKILL.md 的 YAML front matter,获取 skill 名称和描述。\r\n * 解析失败时返回 null。\r\n */\r\nasync function readSkillInfo(skillDir: string): Promise<SkillInfo | null> {\r\n try {\r\n const content = await readFile(join(skillDir, \"SKILL.md\"), \"utf-8\");\r\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---/);\r\n if (!match) return null;\r\n\r\n const frontMatter = match[1]!;\r\n const nameMatch = frontMatter.match(/^name:\\s*(.+)$/m);\r\n const descMatch = frontMatter.match(/^description:\\s*(.+)$/m);\r\n\r\n return {\r\n name: nameMatch?.[1]?.trim() ?? \"\",\r\n description: descMatch?.[1]?.trim() ?? \"\",\r\n };\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * 获取所有可用 skill 的详细信息(全局 + 项目本地)。\r\n * 返回 SkillInfo 列表,解析失败时仍使用目录名作为 name。\r\n */\r\nexport async function getAllSkills(cwd: string): Promise<SkillInfo[]> {\r\n const [globalNames, localNames] = await Promise.all([\r\n listDskcodeSkills(),\r\n listProjectSkills(cwd),\r\n ]);\r\n\r\n // 合并去重,项目本地优先\r\n const seen = new Set<string>();\r\n const allNames: string[] = [];\r\n for (const name of [...localNames, ...globalNames]) {\r\n if (!seen.has(name)) {\r\n seen.add(name);\r\n allNames.push(name);\r\n }\r\n }\r\n\r\n const dskcodeDir = getDskcodeSkillsDir();\r\n const projectDir = getProjectSkillDir(cwd);\r\n\r\n const results: SkillInfo[] = [];\r\n for (const name of allNames) {\r\n // 优先读项目本地的 SKILL.md,再读全局\r\n const localPath = join(projectDir, name);\r\n let info: SkillInfo | null = null;\r\n if (existsSync(localPath)) {\r\n info = await readSkillInfo(localPath);\r\n }\r\n if (!info) {\r\n const globalPath = join(dskcodeDir, name);\r\n if (existsSync(globalPath)) {\r\n info = await readSkillInfo(globalPath);\r\n }\r\n }\r\n results.push(info ?? { name, description: \"\" });\r\n }\r\n\r\n return results;\r\n}\r\n\r\n// 暴露路径获取函数,便于测试\r\nexport { getClaudeSkillsDir, getDskcodeSkillsDir };","import { render } from \"ink\";\r\nimport type { ReactNode } from \"react\";\r\n\r\nexport interface RenderScopeHandle {\r\n waitUntilExit: Promise<unknown>;\r\n unmount: () => void;\r\n clear: () => void;\r\n}\r\n\r\nexport function renderApp(node: ReactNode): RenderScopeHandle {\r\n // exitOnCtrlC: false — 让组件自行处理 Ctrl+C(双击退出)\r\n const { waitUntilExit, clear, unmount } = render(node, { exitOnCtrlC: false });\r\n return { waitUntilExit: waitUntilExit(), clear, unmount };\r\n}\r\n\r\nexport async function unmountApp(handle: RenderScopeHandle): Promise<void> {\r\n handle.unmount();\r\n await new Promise((resolve) => setTimeout(resolve, 50));\r\n}\r\n","import { Text } from \"ink\";\r\nimport InkSpinner from \"ink-spinner\";\r\n\r\ninterface SpinnerProps {\r\n type?: \"dots\" | \"line\" | \"bouncingBar\" | \"aesthetic\";\r\n label?: string;\r\n}\r\n\r\nexport function Spinner({ type = \"dots\", label }: SpinnerProps) {\r\n return (\r\n <Text>\r\n <Text color=\"cyan\">\r\n <InkSpinner type={type} />\r\n </Text>\r\n {label ? <Text> {label}</Text> : null}\r\n </Text>\r\n );\r\n}\r\n","import { Box, Text } from \"ink\";\r\n\r\ntype MessageType = \"info\" | \"success\" | \"warning\" | \"error\";\r\n\r\ninterface StatusMessageProps {\r\n type?: MessageType;\r\n label: string;\r\n detail?: string;\r\n}\r\n\r\nconst STYLES: Record<MessageType, { color: string; icon: string }> = {\r\n info: { color: \"cyan\", icon: \"ℹ\" },\r\n success: { color: \"green\", icon: \"✔\" },\r\n warning: { color: \"yellow\", icon: \"⚠\" },\r\n error: { color: \"red\", icon: \"✖\" },\r\n};\r\n\r\nexport function StatusMessage({ type = \"info\", label, detail }: StatusMessageProps) {\r\n const { color, icon } = STYLES[type];\r\n return (\r\n <Box>\r\n <Text color={color}>\r\n {icon} {label}\r\n </Text>\r\n {detail ? <Text dimColor>: {detail}</Text> : null}\r\n </Box>\r\n );\r\n}\r\n","import { Box, Text } from \"ink\";\r\nimport { useEffect, useState } from \"react\";\r\n\r\n/** 赛博朋克调色板 — 多处组件复用 */\r\nexport const CYBER_PALETTE = [\"#00ffff\", \"#ff00ff\", \"#00ff41\", \"#ff1493\", \"#8b00ff\"] as const;\r\n\r\n/** ASCII LOGO 行 — 多处组件复用 */\r\nexport const LOGO_LINES = [\r\n \" ██████╗ ███████╗██╗ ██╗\",\r\n \" ██╔══██╗██╔════╝██║ ██╔╝\",\r\n \" ██║ ██║███████╗█████╔╝ \",\r\n \" ██║ ██║╚════██║██╔═██╗ \",\r\n \" ██████╔╝███████║██║ ██╗\",\r\n \" ╚═════╝ ╚══════╝╚═╝ ╚═╝\",\r\n] as const;\r\n\r\nexport function DskcodeSplash() {\r\n const [offset, setOffset] = useState(0);\r\n\r\n useEffect(() => {\r\n const timer = setInterval(() => {\r\n setOffset((prev) => (prev + 1) % CYBER_PALETTE.length);\r\n }, 500);\r\n return () => clearInterval(timer);\r\n }, []);\r\n\r\n return (\r\n <Box flexDirection=\"column\" paddingLeft={1}>\r\n {LOGO_LINES.map((line, i) => {\r\n const colorIndex = (i + offset) % CYBER_PALETTE.length;\r\n return (\r\n <Box key={i}>\r\n <Text bold color={CYBER_PALETTE[colorIndex]}>\r\n {line}\r\n </Text>\r\n </Box>\r\n );\r\n })}\r\n </Box>\r\n );\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 交互式聊天会话组件 — 接入 Agent 主循环,实现流式对话\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text, useInput, Static } from \"ink\";\r\nimport TextInput from \"ink-text-input\";\r\nimport { useEffect, useState, useCallback, useMemo, useRef } from \"react\";\r\nimport { useDoubleCtrlC } from \"./useDoubleCtrlC.js\";\r\nimport { CYBER_PALETTE, LOGO_LINES } from \"./DskcodeSplash.js\";\r\nimport InkSpinner from \"ink-spinner\";\r\nimport { AssistantMessage } from \"./AssistantMessage.js\";\r\nimport { DiffPreview } from \"./DiffPreview.js\";\r\nimport { SkillSelector } from \"./SkillSelector.js\";\r\nimport { FileSelector } from \"./FileSelector.js\";\r\nimport type { SkillInfo } from \"../cli/skill-import.js\";\r\nimport { CostTracker } from \"../provider/cost-tracker.js\";\r\nimport type { ModelId, ProviderToolCall, UsageInfo } from \"../provider/index.js\";\r\nimport type { FileDiff } from \"../tool/types.js\";\r\nimport { TodoListPanel } from \"./TodoListPanel.js\";\r\nimport type { TodoItem } from \"../harness/todo-list.js\";\r\nimport { createProvider } from \"../provider/index.js\";\r\nimport {\r\n Session,\r\n type MessageCheckpointInfo,\r\n type RewindResult,\r\n} from \"../agent/index.js\";\r\nimport type { SessionMode } from \"../agent/types.js\";\r\nimport { builtinTools } from \"../tool/index.js\";\r\nimport { ToolRegistry } from \"../tool/registry.js\";\r\nimport {\r\n IDLE_GRADIENT_STOPS,\r\n STREAMING_GRADIENT_STOPS,\r\n CMD_TIP_GRADIENT_STOPS,\r\n GRADIENT_ANIMATION,\r\n getGradientColors,\r\n} from \"../utils/gradient.js\";\r\nimport { SUPPORTED_MODELS, calculateCost } from \"../provider/models.js\";\r\nimport { saveModelConfig } from \"../config/loader.js\";\r\n\r\n/** 流式阶段配置:图标、标签、颜色 */\r\nconst PHASE_CONFIG = {\r\n thinking: { icon: \"🧠\", label: \"深度思考中\", color: \"#ff9800\" },\r\n generating: { icon: \"✨\", label: \"生成中\", color: \"#00ff41\" },\r\n calling_tools: { icon: \"🛠\", label: \"调用工具\", color: \"#f59e0b\" },\r\n executing_tools: { icon: \"⚡\", label: \"执行工具\", color: \"#00ffff\" },\r\n} as const;\r\n\r\n/** 判断工具名是否属于会修改文件系统的类(用于决定 rewind 提示是否出现) */\r\nexport function isFileMutatingTool(name: string): boolean {\r\n return (\r\n name === \"edit_file\" ||\r\n name === \"write_file\" ||\r\n name === \"multi_edit\" ||\r\n name === \"delete_range\"\r\n );\r\n}\r\n\r\n/** 命令处理结果的类型,支持文本响应和动作跳转 */\r\nexport type CommandAction =\r\n | { kind: \"text\"; content: string }\r\n | { kind: \"exit\" }\r\n | { kind: \"clear\" }\r\n | { kind: \"navigate\"; target: \"game\" | \"stock\" };\r\n\r\n/**\r\n * 从 toolCalls 中过滤掉 `todo_*` 任务进度工具。\r\n *\r\n * todo_* 工具是任务进度的元数据(todo_add / todo_mark_done / todo_mark_failed / todo_retry),\r\n * 它们不表示 AI 实际做了什么,UI 上交给独立的 <TodoListPanel> 维护,不进入消息列表。\r\n *\r\n * @param calls — 原始 tool_calls 列表(可能为 undefined)\r\n * @returns 过滤后的列表(若过滤后为空返回 undefined)\r\n *\r\n * @pure\r\n */\r\nfunction filterTodoToolCalls(\r\n calls: ReadonlyArray<ProviderToolCall> | undefined,\r\n): ProviderToolCall[] | undefined {\r\n if (!calls) return undefined;\r\n const filtered = calls.filter((tc) => !tc.name.startsWith(\"todo_\"));\r\n return filtered.length > 0 ? filtered : undefined;\r\n}\r\n\r\nexport interface ChatCommand {\r\n desc: string;\r\n handler: () => CommandAction;\r\n}\r\n\r\n/** 命令注册表,支持动态注册新命令 */\r\nconst commandRegistry = new Map<string, ChatCommand>();\r\n\r\n/** 注册一个命令 */\r\nexport function registerCommand(name: string, cmd: ChatCommand): void {\r\n commandRegistry.set(name, cmd);\r\n}\r\n\r\n/** 获取所有已注册命令(用于 /help 生成帮助文本) */\r\nfunction getRegisteredCommands(): Map<string, ChatCommand> {\r\n return commandRegistry;\r\n}\r\n\r\n// 注册内置命令\r\nregisterCommand(\"/exit\", { desc: \"退出对话\", handler: () => ({ kind: \"exit\" }) });\r\nregisterCommand(\"/quit\", { desc: \"退出对话\", handler: () => ({ kind: \"exit\" }) });\r\nregisterCommand(\"/help\", {\r\n desc: \"显示帮助信息\",\r\n handler: () => {\r\n const commands = getRegisteredCommands();\r\n const lines = [\"可用命令:\"];\r\n for (const [name, cmd] of commands) {\r\n lines.push(` ${name.padEnd(16)}${cmd.desc}`);\r\n }\r\n return { kind: \"text\", content: lines.join(\"\\n\") };\r\n },\r\n});\r\nregisterCommand(\"/clear\", { desc: \"清空对话历史\", handler: () => ({ kind: \"clear\" }) });\r\nregisterCommand(\"/version\", {\r\n desc: \"显示版本信息\",\r\n handler: () => ({ kind: \"text\", content: \"dskcode v0.1.10\" }),\r\n});\r\nregisterCommand(\"/model\", {\r\n desc: \"切换模型\",\r\n handler: () => ({ kind: \"text\", content: \"请直接输入 /model 进入选择界面\" }),\r\n});\r\nregisterCommand(\"/thinking\", {\r\n desc: \"切换深度思考模式\",\r\n handler: () => ({ kind: \"text\", content: \"请直接输入 /thinking 切换\" }),\r\n});\r\nregisterCommand(\"/effort\", {\r\n desc: \"切换推理等级 High/Max\",\r\n handler: () => ({ kind: \"text\", content: \"请直接输入 /effort 切换\" }),\r\n});\r\nregisterCommand(\"/game\", {\r\n desc: \"启动游戏\",\r\n handler: () => ({ kind: \"navigate\", target: \"game\" }),\r\n});\r\nregisterCommand(\"/stock\", {\r\n desc: \"查看股票行情\",\r\n handler: () => ({ kind: \"navigate\", target: \"stock\" }),\r\n});\r\nregisterCommand(\"/plan\", {\r\n desc: \"切换为计划模式(Shift+Tab)\",\r\n handler: () => ({ kind: \"text\", content: \"输入 /plan 或按 Shift+Tab 切换为计划模式\" }),\r\n});\r\nregisterCommand(\"/code\", {\r\n desc: \"切换回代码模式(Shift+Tab)\",\r\n handler: () => ({ kind: \"text\", content: \"输入 /code 或按 Shift+Tab 切换回代码模式\" }),\r\n});\r\nregisterCommand(\"/rewind\", {\r\n desc: \"回退到历史检查点(1 = 最新)\",\r\n handler: () => ({\r\n kind: \"text\",\r\n content:\r\n \"请直接输入 /rewind 查看可回退的检查点列表,或 /rewind <序号> 直接回退(1 = 最新,2 = 上一次,依此类推)\",\r\n }),\r\n});\r\n\r\n/** 流式输出时,输入框随机展示的占位符列表 */\r\nconst STREAMING_PLACEHOLDERS = [\r\n \"让子弹飞一会儿...\",\r\n \"马上就好...\",\r\n \"正在憋大招...\",\r\n \"稍等一下下~\",\r\n \"码字中...\",\r\n \"脑子转得飞快...\",\r\n];\r\n\r\n/** 空闲时输入框随机展示的占位符列表 */\r\nconst IDLE_PLACEHOLDERS = [\r\n \"想干啥?直接说~\",\r\n \"来吧,吩咐点啥\",\r\n \"随时待命...\",\r\n \"戳这里开聊...\",\r\n \"等你开口...\",\r\n \"尽管使唤~\",\r\n];\r\n\r\nfunction pickRandom<T>(arr: readonly T[]): T {\r\n return arr[Math.floor(Math.random() * arr.length)]!;\r\n}\r\n\r\n/** 单条已完成的助手消息 */\r\ninterface CompletedAssistant {\r\n content: string;\r\n /**\r\n * 思考链段列表(thinking 模式的 CoT),可选。\r\n * 模型一轮内可能经历多轮“思考→调工具”,每一段都保留在这里。\r\n */\r\n reasoning?: string[];\r\n toolCalls?: ProviderToolCall[];\r\n usage?: UsageInfo;\r\n elapsed?: number;\r\n cost?: number;\r\n model?: string;\r\n}\r\n\r\n/** 单条显示消息 */\r\ninterface DisplayMessage {\r\n role: \"user\" | \"assistant\" | \"tool\";\r\n content: string;\r\n /** 已完成的助手消息详情(仅在 role=assistant 时) */\r\n assistantDetail?: CompletedAssistant;\r\n /** 文件变更 diff(仅在 role=tool 且有 diff 时) */\r\n diff?: FileDiff;\r\n}\r\n\r\ninterface ChatSessionProps {\r\n skillCount: number;\r\n /** 可用 skill 详情列表(用于 / 输入时展示) */\r\n skills?: SkillInfo[];\r\n /** 项目源码文件路径列表(用于 @ 输入时展示) */\r\n files?: string[];\r\n toolCount: number;\r\n verbose: boolean;\r\n apiKey?: string;\r\n baseUrl?: string;\r\n costTracker?: CostTracker;\r\n model?: string;\r\n onLaunchGame?: () => void;\r\n onLaunchStock?: () => void;\r\n}\r\n\r\nexport function ChatSession({\r\n skillCount,\r\n skills = [],\r\n files = [],\r\n toolCount,\r\n verbose,\r\n apiKey,\r\n baseUrl,\r\n costTracker: externalCostTracker,\r\n model = \"deepseek-v4-flash\",\r\n onLaunchGame,\r\n onLaunchStock,\r\n}: ChatSessionProps) {\r\n const termWidth =\r\n typeof process.stdout.columns === \"number\" ? process.stdout.columns : 80;\r\n const dividerWidth = Math.max(termWidth - 2, 1);\r\n\r\n const [offset, setOffset] = useState(0);\r\n const [displayMessages, setDisplayMessages] = useState<DisplayMessage[]>([]);\r\n const [staticKey, setStaticKey] = useState(0);\r\n const [input, setInput] = useState(\"\");\r\n const [balance, setBalance] = useState<number | null>(null);\r\n const [balanceLoading, setBalanceLoading] = useState(false);\r\n const [todayCost, setTodayCost] = useState<number | null>(null);\r\n\r\n // 流式状态\r\n const [isStreaming, setIsStreaming] = useState(false);\r\n const [streamingPhase, setStreamingPhase] = useState<\r\n \"thinking\" | \"generating\" | \"calling_tools\" | \"executing_tools\" | null\r\n >(null);\r\n const [streamingPlaceholder, setStreamingPlaceholder] = useState(\"\");\r\n const [idlePlaceholder, setIdlePlaceholder] = useState(() =>\r\n pickRandom(IDLE_PLACEHOLDERS),\r\n );\r\n const [gradientColors, setGradientColors] = useState<string[]>([]);\r\n const gradientPhaseRef = useRef(0);\r\n const [streamingGradientColors, setStreamingGradientColors] = useState<string[]>([]);\r\n const streamingPhaseRef = useRef(0);\r\n const [currentContent, setCurrentContent] = useState(\"\");\r\n /**\r\n * 本轮已累积的思考链段列表。一轮内可能经历多轮“思考→调工具→思考”,\r\n * 每一段都 push 到这里,避免被后一段覆盖。\r\n */\r\n const [currentReasoning, setCurrentReasoning] = useState<string[]>([]);\r\n const [currentToolCalls, setCurrentToolCalls] = useState<ProviderToolCall[]>([]);\r\n const [_currentUsage, setCurrentUsage] = useState<UsageInfo | undefined>(undefined);\r\n const [_currentElapsed, setCurrentElapsed] = useState<number | undefined>(undefined);\r\n const [_currentCost, setCurrentCost] = useState<number | undefined>(undefined);\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const [activeModel, setActiveModel] = useState<ModelId>(model as unknown as ModelId);\r\n const [_streamingModel, setStreamingModel] = useState<string | undefined>(undefined);\r\n const [streamError, setStreamError] = useState<string | undefined>(undefined);\r\n // todo 任务进度面板 state — todo_* 工具结果更新此状态,<TodoListPanel> 独立渲染\r\n const [todoSnapshot, setTodoSnapshot] = useState<ReadonlyArray<TodoItem>>([]);\r\n // 任务全部结束后延迟隐藏的面板可见性(默认 true:有 snapshot 就显示)\r\n const [todoPanelVisible, setTodoPanelVisible] = useState(true);\r\n // “全部结束后 2s 隐藏”的定时器句柄\r\n const todoHideTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\r\n\r\n // 会话模式(code / plan)\r\n const [sessionMode, setSessionMode] = useState<SessionMode>(\"code\");\r\n\r\n // 高级设置\r\n const [thinkingEnabled, setThinkingEnabled] = useState(true);\r\n const [thinkingEffort, setThinkingEffort] = useState<\"high\" | \"max\">(\"high\");\r\n const [responseFormat] = useState<\"text\" | \"json_object\">(\"text\");\r\n const [toolChoice, setToolChoice] = useState<\"auto\" | \"required\" | \"none\" | undefined>(\r\n undefined,\r\n );\r\n\r\n // 本次会话累计费用(从已完成的助手消息中汇总)\r\n const sessionCost = useMemo(() => {\r\n return displayMessages.reduce((sum, msg) => {\r\n if (msg.assistantDetail?.cost) {\r\n return sum + msg.assistantDetail.cost;\r\n }\r\n return sum;\r\n }, 0);\r\n }, [displayMessages]);\r\n\r\n const hasConversationStarted = displayMessages.length > 0;\r\n\r\n // 命令提示轮播索引\r\n const cmdTips = Array.from(getRegisteredCommands())\r\n .filter(([name]) => name !== \"/exit\" && name !== \"/quit\")\r\n .map(([name, cmd]) => ({ name, desc: cmd.desc }));\r\n const [cmdTipIndex, setCmdTipIndex] = useState(0);\r\n const [cmdTipGradientColors, setCmdTipGradientColors] = useState<string[]>([]);\r\n const cmdTipPhaseRef = useRef(0);\r\n\r\n // skill 选择索引(用于上下键导航)\r\n const [skillSelectIndex, setSkillSelectIndex] = useState(0);\r\n // 文件选择索引\r\n const [fileSelectIndex, setFileSelectIndex] = useState(0);\r\n // 输入框 key(补全时递增,强制 TextInput 重挂载以重置光标到末尾)\r\n const [inputKey, setInputKey] = useState(0);\r\n\r\n // /rewind 回退选择模式\r\n const [rewindSelecting, setRewindSelecting] = useState(false);\r\n const [rewindSelectIndex, setRewindSelectIndex] = useState(0);\r\n const [rewindList, setRewindList] = useState<MessageCheckpointInfo[]>([]);\r\n const [rewinding, setRewinding] = useState(false);\r\n\r\n // /rewind 提示:某轮对话修改了文件后,在原状态 loading 处显示「/rewind 1 可撤回本次修改」。\r\n // 三阶段:idle(不显示)→ pending(流式刚结束,等 2s)→ visible(展示提示)\r\n // 提示一旦出现会一直保留到下次对话开始,不会被用户输入打断。\r\n const [rewindHintPhase, setRewindHintPhase] = useState<\"idle\" | \"pending\" | \"visible\">(\r\n \"idle\",\r\n );\r\n // 本轮是否触发了文件修改类工具调用(edit_file/write_file/multi_edit/delete_range)\r\n const currentRoundModifiedRef = useRef(false);\r\n // 提示计时器句柄\r\n const rewindHintTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\r\n\r\n // 模型选择模式\r\n const [selectingModel, setSelectingModel] = useState(false);\r\n const [modelSelectIndex, setModelSelectIndex] = useState(0);\r\n const modelOptions: ModelId[] = [\"deepseek-v4-flash\", \"deepseek-v4-pro\"];\r\n\r\n // Session 引用(保持跨渲染稳定)\r\n const sessionRef = useRef<Session | null>(null);\r\n const abortRef = useRef<AbortController>(null);\r\n\r\n // 用 ref 跟踪流式内容的最新值,以便 finally 块能获取非闭包过期的值\r\n const currentContentRef = useRef(\"\");\r\n const currentReasoningRef = useRef<string[]>([]);\r\n const currentToolCallsRef = useRef<ProviderToolCall[]>([]);\r\n const currentUsageRef = useRef<UsageInfo | undefined>(undefined);\r\n const currentElapsedRef = useRef<number | undefined>(undefined);\r\n const currentCostRef = useRef<number | undefined>(undefined);\r\n const currentModelRef = useRef<string | undefined>(undefined);\r\n const streamErrorRef = useRef<string | undefined>(undefined);\r\n\r\n // 输入变更时仅重置选择索引;rewind 提示由下一轮对话开始时清掉\r\n useEffect(() => {\r\n setSkillSelectIndex(0);\r\n setFileSelectIndex(0);\r\n }, [input]);\r\n\r\n // 组件卸载时清掉 rewind 提示计时器,避免内存泄露\r\n useEffect(() => {\r\n return () => {\r\n if (rewindHintTimerRef.current) {\r\n clearTimeout(rewindHintTimerRef.current);\r\n rewindHintTimerRef.current = null;\r\n }\r\n };\r\n }, []);\r\n\r\n // todo 面板可见性:\r\n // - 有未完成项(pending / running / failed)→ 一直显示\r\n // - 全部结束(done / skipped)→ 立即隐藏\r\n // - snapshot 为空 → 隐藏\r\n // 新任务开始时(snapshot 从空 → 非空)会重新出现\r\n useEffect(() => {\r\n // 顺手清掉可能残留的隐藏计时器(保留 ref 以备未来需要)\r\n if (todoHideTimerRef.current) {\r\n clearTimeout(todoHideTimerRef.current);\r\n todoHideTimerRef.current = null;\r\n }\r\n\r\n if (todoSnapshot.length === 0) {\r\n setTodoPanelVisible(false);\r\n return;\r\n }\r\n\r\n // 是否有未完成项(pending / running / failed 都算\"还在动\")\r\n const hasUnfinished = todoSnapshot.some(\r\n (it) => it.status === \"pending\" || it.status === \"running\" || it.status === \"failed\",\r\n );\r\n\r\n setTodoPanelVisible(hasUnfinished);\r\n }, [todoSnapshot]);\r\n\r\n // 获取当前输入匹配的 skill 列表\r\n const getFilteredSkills = useCallback(\r\n (value: string) => {\r\n const match = value.match(/(?:^|\\s)\\/([^/]*)$/);\r\n if (!match) return [];\r\n const q = match[1]!.toLowerCase().trim();\r\n if (!q) {\r\n if (value.startsWith(\"/\")) return skills.slice(0, 3);\r\n return [];\r\n }\r\n const matched = skills.filter((s) => s.name.toLowerCase().includes(q)).slice(0, 3);\r\n // 精确匹配时表示已补全完成,不显示列表\r\n if (matched.some((s) => s.name.toLowerCase() === q)) return [];\r\n return matched;\r\n },\r\n [skills],\r\n );\r\n\r\n // 获取当前输入匹配的文件列表\r\n const getFilteredFiles = useCallback(\r\n (value: string) => {\r\n const match = value.match(/(?:^|\\s)@([^@]*)$/);\r\n if (!match) return [];\r\n const q = match[1]!.toLowerCase().trim();\r\n // @ 在开头且无后续内容时,展示前 5 个文件作为提示\r\n if (!q) {\r\n if (value.startsWith(\"@\")) return files.slice(0, 5);\r\n return [];\r\n }\r\n const matched = files.filter((f) => f.toLowerCase().includes(q)).slice(0, 5);\r\n // 精确匹配时表示已补全完成,不显示列表\r\n if (matched.some((f) => f.toLowerCase() === q)) return [];\r\n return matched;\r\n },\r\n [files],\r\n );\r\n\r\n // 根据 session.messages 重建 displayMessages,绕开下标对齐问题\r\n function rebuildDisplayFromSession(sess: Session): void {\r\n const next: DisplayMessage[] = [];\r\n for (const m of sess.messages) {\r\n if (m.role === \"user\") {\r\n next.push({ role: \"user\", content: m.content });\r\n } else if (m.role === \"assistant\") {\r\n next.push({\r\n role: \"assistant\",\r\n content: m.content,\r\n assistantDetail: {\r\n content: m.content,\r\n toolCalls: filterTodoToolCalls(m.toolCalls),\r\n },\r\n });\r\n } else if (m.role === \"tool\") {\r\n next.push({ role: \"tool\", content: m.content });\r\n }\r\n // system 消息不展示\r\n }\r\n setDisplayMessages(next);\r\n setStaticKey((prev) => prev + 1);\r\n }\r\n\r\n const doRewind = useCallback(\r\n async (target: MessageCheckpointInfo, displayNumber: number) => {\r\n const session = sessionRef.current;\r\n if (!session) return;\r\n setRewinding(true);\r\n setIsStreaming(true);\r\n setStreamingPhase(\"thinking\");\r\n setStreamingPlaceholder(\"⏪ 正在回退到检查点…\");\r\n setInput(\"\");\r\n try {\r\n const r: RewindResult = await session.rewind(target.index);\r\n if (r.ok) {\r\n rebuildDisplayFromSession(session);\r\n const tail = r.fileRestored\r\n ? \",工作区文件已恢复\"\r\n : \"(仅对话回退,未恢复文件)\";\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n {\r\n role: \"assistant\",\r\n content: `⏪ 已回退到检查点 #${displayNumber}${tail}。`,\r\n },\r\n ]);\r\n } else {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"assistant\", content: `⚠ 回退失败:${r.error}` },\r\n ]);\r\n }\r\n } catch (err: unknown) {\r\n const msg = err instanceof Error ? err.message : String(err);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"assistant\", content: `⚠ 回退异常:${msg}` },\r\n ]);\r\n } finally {\r\n setRewinding(false);\r\n setIsStreaming(false);\r\n setStreamingPhase(null);\r\n setStreamingPlaceholder(\"\");\r\n }\r\n },\r\n [],\r\n );\r\n\r\n const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(() => {\r\n // 双击 Ctrl+C 退出进程 — 先刷新日志再退出\r\n const s = sessionRef.current;\r\n if (s) {\r\n void s.flushLog().finally(() => process.exit(0));\r\n } else {\r\n process.exit(0);\r\n }\r\n });\r\n\r\n // 捕获 Ctrl+C 和渐变占位符状态下的字符输入\r\n useInput(\r\n useCallback(\r\n (_input, key) => {\r\n // /rewind 选择模式\r\n if (rewindSelecting) {\r\n if (key.upArrow) {\r\n setRewindSelectIndex(\r\n (prev) => (prev - 1 + rewindList.length) % rewindList.length,\r\n );\r\n } else if (key.downArrow) {\r\n setRewindSelectIndex((prev) => (prev + 1) % rewindList.length);\r\n } else if (key.return) {\r\n const target = rewindList[rewindSelectIndex];\r\n setRewindSelecting(false);\r\n if (target) {\r\n void doRewind(target, rewindSelectIndex + 1);\r\n }\r\n } else if (key.escape) {\r\n setRewindSelecting(false);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"assistant\", content: \"已取消回退。\" },\r\n ]);\r\n }\r\n return;\r\n }\r\n\r\n // 模型选择模式\r\n if (selectingModel) {\r\n if (key.upArrow) {\r\n setModelSelectIndex(\r\n (prev) => (prev - 1 + modelOptions.length) % modelOptions.length,\r\n );\r\n } else if (key.downArrow) {\r\n setModelSelectIndex((prev) => (prev + 1) % modelOptions.length);\r\n } else if (key.return) {\r\n const selected = modelOptions[modelSelectIndex]!;\r\n if (selected === activeModel) {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n {\r\n role: \"assistant\",\r\n content: `已经在使用 ${SUPPORTED_MODELS[selected].displayName}`,\r\n },\r\n ]);\r\n } else {\r\n // 切换模型:持久化到配置 + 重置 Session\r\n setActiveModel(selected);\r\n sessionRef.current?.reset();\r\n saveModelConfig(selected).catch(() => {\r\n // 持久化失败静默处理,不影响当前会话\r\n });\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n {\r\n role: \"assistant\",\r\n content: `模型已切换为 ${SUPPORTED_MODELS[selected].displayName}(${selected})`,\r\n },\r\n ]);\r\n }\r\n setSelectingModel(false);\r\n } else if (key.escape) {\r\n setSelectingModel(false);\r\n }\r\n return;\r\n }\r\n\r\n // 文件 @ 选择模式 / skill 选择模式\r\n // 优先 @(文件匹配),没有文件匹配再走 skill 匹配\r\n const fileList = getFilteredFiles(input);\r\n const skillList = fileList.length === 0 ? getFilteredSkills(input) : [];\r\n\r\n if (fileList.length > 0) {\r\n if (key.upArrow) {\r\n setFileSelectIndex((prev) => (prev - 1 + fileList.length) % fileList.length);\r\n return;\r\n }\r\n if (key.downArrow) {\r\n setFileSelectIndex((prev) => (prev + 1) % fileList.length);\r\n return;\r\n }\r\n // Tab 补全选中的文件\r\n if (key.tab) {\r\n const selected = fileList[fileSelectIndex];\r\n if (selected) {\r\n const atIdx = input.lastIndexOf(\"@\");\r\n if (atIdx >= 0) {\r\n setInput(input.slice(0, atIdx) + \"@\" + selected + \" \");\r\n setInputKey((k) => k + 1);\r\n }\r\n }\r\n return;\r\n }\r\n }\r\n\r\n if (skillList.length > 0) {\r\n if (key.upArrow) {\r\n setSkillSelectIndex(\r\n (prev) => (prev - 1 + skillList.length) % skillList.length,\r\n );\r\n return;\r\n }\r\n if (key.downArrow) {\r\n setSkillSelectIndex((prev) => (prev + 1) % skillList.length);\r\n return;\r\n }\r\n // Tab 补全选中的 skill(补全后光标自动移至末尾)\r\n if (key.tab) {\r\n const selected = skillList[skillSelectIndex];\r\n if (selected) {\r\n // 只替换最后一个 / 之后的部分,保留之前输入的内容\r\n // 用 lastIndexOf 定位 / 的精确位置,确保前置空格不被吃掉\r\n const slashIdx = input.lastIndexOf(\"/\");\r\n if (slashIdx >= 0) {\r\n // 补全后加空格分隔,方便继续输入下一个 skill 或问题\r\n setInput(input.slice(0, slashIdx) + \"/\" + selected.name + \" \");\r\n setInputKey((k) => k + 1);\r\n }\r\n }\r\n return;\r\n }\r\n }\r\n\r\n // 模型选择 / 设置选择模式下的通用键盘处理\r\n if (selectingModel) {\r\n if (key.upArrow || key.downArrow) {\r\n // 已在模型选择模式下处理上下键\r\n }\r\n return;\r\n }\r\n\r\n if (key.ctrl && _input === \"c\") {\r\n if (isStreaming) {\r\n abortRef.current?.abort();\r\n } else {\r\n handleCtrlC();\r\n }\r\n return;\r\n }\r\n\r\n // Shift+Tab 切换计划模式 / 代码模式\r\n if (key.shift && key.tab) {\r\n if (!isStreaming) {\r\n const newMode: SessionMode = sessionMode === \"plan\" ? \"code\" : \"plan\";\r\n setSessionMode(newMode);\r\n sessionRef.current?.setMode(newMode);\r\n setToolChoice(undefined);\r\n sessionRef.current?.reset();\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n {\r\n role: \"assistant\",\r\n content:\r\n newMode === \"plan\"\r\n ? \"📋 已切换为 **计划模式**(Shift+Tab)\\n\\n在此模式下,我只能读取和分析代码,不会执行任何修改。\"\r\n : \"🛠 已切换为 **代码模式**(Shift+Tab)\\n\\n现在可以正常读取和修改代码了。\",\r\n },\r\n ]);\r\n }\r\n return;\r\n }\r\n\r\n // 渐变占位符显示时(TextInput 未渲染),将按键字符加入 input 触发切换\r\n if (!input && !isStreaming && _input) {\r\n setInput(_input);\r\n }\r\n },\r\n [\r\n selectingModel,\r\n modelSelectIndex,\r\n modelOptions,\r\n activeModel,\r\n isStreaming,\r\n handleCtrlC,\r\n input,\r\n skills,\r\n skillSelectIndex,\r\n fileSelectIndex,\r\n getFilteredSkills,\r\n getFilteredFiles,\r\n sessionMode,\r\n setSessionMode,\r\n rewindSelecting,\r\n rewindSelectIndex,\r\n rewindList,\r\n doRewind,\r\n ],\r\n ),\r\n );\r\n\r\n // 命令提示轮播 — 每 2 秒切换下一条\r\n useEffect(() => {\r\n if (cmdTips.length <= 1) return;\r\n const timer = setInterval(() => {\r\n setCmdTipIndex((prev) => (prev + 1) % cmdTips.length);\r\n }, 2000);\r\n return () => clearInterval(timer);\r\n }, [cmdTips.length]);\r\n\r\n // 命令提示条渐变动画(反向流动:1 - phase)\r\n useEffect(() => {\r\n const tip = cmdTips[cmdTipIndex % cmdTips.length];\r\n if (!tip) {\r\n setCmdTipGradientColors([]);\r\n return;\r\n }\r\n const text = `${tip.name} ${tip.desc}`;\r\n cmdTipPhaseRef.current = 0;\r\n setCmdTipGradientColors(getGradientColors(text, 1, CMD_TIP_GRADIENT_STOPS));\r\n\r\n const interval = setInterval(() => {\r\n cmdTipPhaseRef.current =\r\n (cmdTipPhaseRef.current + GRADIENT_ANIMATION.cmdTipPhaseStep) % 1;\r\n // 反向 phase 使色彩从左到右流动\r\n setCmdTipGradientColors(\r\n getGradientColors(text, 1 - cmdTipPhaseRef.current, CMD_TIP_GRADIENT_STOPS),\r\n );\r\n }, GRADIENT_ANIMATION.cmdTipInterval);\r\n\r\n return () => clearInterval(interval);\r\n }, [cmdTipIndex, cmdTips.length]);\r\n\r\n // Logo 色彩动画\r\n useEffect(() => {\r\n const timer = setInterval(() => {\r\n setOffset((prev) => (prev + 1) % CYBER_PALETTE.length);\r\n }, 500);\r\n return () => clearInterval(timer);\r\n }, []);\r\n\r\n // 初始化 Session(当模型切换时重建)\r\n useEffect(() => {\r\n if (!apiKey || !baseUrl) return;\r\n\r\n const provider = createProvider({\r\n name: \"deepseek\",\r\n apiKey,\r\n baseUrl,\r\n model: activeModel,\r\n });\r\n\r\n const tracker = externalCostTracker ?? new CostTracker();\r\n // 注册所有内置工具\r\n const toolRegistry = new ToolRegistry();\r\n toolRegistry.registerAll(builtinTools);\r\n const session = new Session(provider, toolRegistry, tracker, {\r\n cwd: process.cwd(),\r\n });\r\n sessionRef.current = session;\r\n\r\n return () => {\r\n sessionRef.current = null;\r\n };\r\n }, [apiKey, baseUrl, activeModel, externalCostTracker]);\r\n\r\n // 查询余额\r\n useEffect(() => {\r\n if (!apiKey || !baseUrl) return;\r\n let cancelled = false;\r\n setBalanceLoading(true);\r\n import(\"../provider/deepseek.js\")\r\n .then(({ DeepSeekProvider }) => {\r\n const provider = new DeepSeekProvider({\r\n apiKey,\r\n baseUrl,\r\n model: \"deepseek-v4-flash\",\r\n });\r\n return provider.getBalance();\r\n })\r\n .then((result) => {\r\n if (cancelled) return;\r\n const cny = result.balances.find((b) => b.currency === \"CNY\");\r\n if (cny) {\r\n setBalance(cny.totalBalance);\r\n }\r\n })\r\n .catch(() => {\r\n // 查询失败静默处理,不影响主流程\r\n })\r\n .finally(() => {\r\n if (!cancelled) setBalanceLoading(false);\r\n });\r\n return () => {\r\n cancelled = true;\r\n };\r\n }, [apiKey, baseUrl]);\r\n\r\n // 加载今日消耗历史数据,并定时刷新\r\n useEffect(() => {\r\n if (!externalCostTracker) return;\r\n let cancelled = false;\r\n let timer: ReturnType<typeof setInterval>;\r\n\r\n const refresh = () => {\r\n setTodayCost(externalCostTracker.todayTotalCost);\r\n };\r\n\r\n externalCostTracker\r\n .load()\r\n .then(() => {\r\n if (cancelled) return;\r\n refresh();\r\n timer = setInterval(refresh, 5000);\r\n })\r\n .catch(() => {\r\n // 加载失败静默处理\r\n });\r\n\r\n return () => {\r\n cancelled = true;\r\n if (timer) clearInterval(timer);\r\n };\r\n }, [externalCostTracker]);\r\n\r\n // 空闲占位符渐变动画\r\n useEffect(() => {\r\n if (isStreaming || !idlePlaceholder) {\r\n setGradientColors([]);\r\n return;\r\n }\r\n\r\n gradientPhaseRef.current = 0;\r\n setGradientColors(getGradientColors(idlePlaceholder, 1, IDLE_GRADIENT_STOPS));\r\n\r\n const interval = setInterval(() => {\r\n gradientPhaseRef.current =\r\n (gradientPhaseRef.current + GRADIENT_ANIMATION.idlePhaseStep) % 1;\r\n setGradientColors(\r\n getGradientColors(\r\n idlePlaceholder,\r\n 1 - gradientPhaseRef.current,\r\n IDLE_GRADIENT_STOPS,\r\n ),\r\n );\r\n }, GRADIENT_ANIMATION.idleInterval);\r\n\r\n return () => clearInterval(interval);\r\n }, [isStreaming, idlePlaceholder]);\r\n\r\n // 流式占位符渐变动画\r\n useEffect(() => {\r\n if (!isStreaming || !streamingPlaceholder) {\r\n setStreamingGradientColors([]);\r\n return;\r\n }\r\n\r\n streamingPhaseRef.current = 0;\r\n setStreamingGradientColors(\r\n getGradientColors(streamingPlaceholder, 1, STREAMING_GRADIENT_STOPS),\r\n );\r\n\r\n const interval = setInterval(() => {\r\n streamingPhaseRef.current =\r\n (streamingPhaseRef.current + GRADIENT_ANIMATION.streamingPhaseStep) % 1;\r\n setStreamingGradientColors(\r\n getGradientColors(\r\n streamingPlaceholder,\r\n 1 - streamingPhaseRef.current,\r\n STREAMING_GRADIENT_STOPS,\r\n ),\r\n );\r\n }, GRADIENT_ANIMATION.streamingInterval);\r\n\r\n return () => clearInterval(interval);\r\n }, [isStreaming, streamingPlaceholder]);\r\n\r\n /** 处理用户输入 */\r\n const handleSubmit = useCallback(\r\n async (value: string) => {\r\n const trimmed = value.trim();\r\n if (!trimmed) return;\r\n\r\n // 处理斜杠命令\r\n if (trimmed.startsWith(\"/\") && trimmed.length > 1) {\r\n const cmdLower = trimmed.toLowerCase();\r\n\r\n // /rewind 命令:回退到历史检查点\r\n if (cmdLower === \"/rewind\" || cmdLower.startsWith(\"/rewind \")) {\r\n if (isStreaming || rewinding) {\r\n const reason = rewinding ? \"回退中\" : \"生成中\";\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: `⚠ 正在${reason},请稍后再试 /rewind。` },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n if (!sessionRef.current) {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: \"⚠ Session 未就绪,无法回退。\" },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n const cps = sessionRef.current.listCheckpoints();\r\n if (cps.length === 0) {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n {\r\n role: \"assistant\",\r\n content:\r\n \"⚠ 没有可回退的检查点。\\n只有在 git 仓库内且发生过对话后才会生成检查点。\",\r\n },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n // /rewind <N>:1 表示最新(与 git reset HEAD~ 习惯一致),2 表示上一个,依次类推\r\n const parts = trimmed.split(/\\s+/);\r\n if (parts.length >= 2) {\r\n const n = Number(parts[1]);\r\n if (!Number.isInteger(n) || n < 1 || n > cps.length) {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n {\r\n role: \"assistant\",\r\n content: `⚠ 无效的序号「${parts[1]}」。可用范围 1~${cps.length}(1 表示最新)。`,\r\n },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n // 将用户输入的 1-based \"最新优先\"序号转换为 cps 列表里的索引\r\n const target = cps[cps.length - n]!;\r\n setInput(\"\");\r\n await doRewind(target, n);\r\n return;\r\n }\r\n // 无参数:进入选择模式。逆序展示(最新在第 1 位),默认选中最新\r\n setRewindList([...cps].reverse());\r\n setRewindSelectIndex(0);\r\n setRewindSelecting(true);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n {\r\n role: \"assistant\",\r\n content: `⏷↑↓ 选择检查点,Enter 确认,Esc 取消(共 ${cps.length} 个可回退位置)`,\r\n },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n // /model 命令:进入模型选择模式\r\n if (cmdLower === \"/model\") {\r\n const curIdx = modelOptions.indexOf(activeModel);\r\n setModelSelectIndex(curIdx >= 0 ? curIdx : 0);\r\n setSelectingModel(true);\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n // /thinking 命令:切换深度思考\r\n if (cmdLower === \"/thinking\") {\r\n setThinkingEnabled((prev) => !prev);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n {\r\n role: \"assistant\",\r\n content: `深度思考已${thinkingEnabled ? \"关闭\" : \"开启\"}`,\r\n },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n // /effort 命令:切换推理等级\r\n if (cmdLower === \"/effort\") {\r\n const next = thinkingEffort === \"high\" ? \"max\" : \"high\";\r\n setThinkingEffort(next);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n {\r\n role: \"assistant\",\r\n content: `推理等级已切换为 ${next === \"high\" ? \"High\" : \"Max\"}`,\r\n },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n // /plan 命令:切换为计划模式(只读分析,不能修改代码)\r\n if (cmdLower === \"/plan\") {\r\n if (sessionMode === \"plan\") {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n {\r\n role: \"assistant\",\r\n content: \"已经在计划模式中。输入 /code 切回代码模式。\",\r\n },\r\n ]);\r\n } else {\r\n setSessionMode(\"plan\");\r\n sessionRef.current?.setMode(\"plan\");\r\n // 计划模式下自动启用工具调用,确保模型可以使用读工具\r\n setToolChoice(undefined);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n {\r\n role: \"assistant\",\r\n content:\r\n \"📋 已切换为 **计划模式**\\n\\n在此模式下,我只能读取和分析代码,不会执行任何修改。\\n输入 /code 切回代码模式。\",\r\n },\r\n ]);\r\n // 清空会话历史,防止旧模式下产生的工具调用消息干扰新模式\r\n sessionRef.current?.reset();\r\n }\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n // /code 命令:切换回代码模式\r\n if (cmdLower === \"/code\") {\r\n if (sessionMode === \"code\") {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n {\r\n role: \"assistant\",\r\n content: \"已经在代码模式中。输入 /plan 切回计划模式。\",\r\n },\r\n ]);\r\n } else {\r\n setSessionMode(\"code\");\r\n sessionRef.current?.setMode(\"code\");\r\n // 回到代码模式时重置工具调用策略,确保可以正常使用所有工具\r\n setToolChoice(undefined);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n {\r\n role: \"assistant\",\r\n content:\r\n \"🛠 已切换为 **代码模式**\\n\\n现在可以正常读取和修改代码了。\\n输入 /plan 切换为计划模式(只读分析)。\",\r\n },\r\n ]);\r\n // 清空会话历史,防止计划模式下产生的消息干扰代码模式\r\n sessionRef.current?.reset();\r\n }\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n // /tools 命令:切换工具调用策略\r\n if (cmdLower === \"/tools\") {\r\n const next =\r\n toolChoice === undefined\r\n ? \"none\"\r\n : toolChoice === \"none\"\r\n ? \"required\"\r\n : toolChoice === \"required\"\r\n ? \"auto\"\r\n : undefined;\r\n setToolChoice(next);\r\n const label =\r\n next === undefined\r\n ? \"自动(默认)\"\r\n : next === \"none\"\r\n ? \"禁止调用\"\r\n : next === \"required\"\r\n ? \"强制调用\"\r\n : \"自动(默认)\";\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: `工具调用策略已切换为 ${label}` },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n const cmd = commandRegistry.get(cmdLower);\r\n if (cmd) {\r\n const result = cmd.handler();\r\n\r\n switch (result.kind) {\r\n case \"exit\":\r\n await sessionRef.current?.flushLog();\r\n process.exit(0);\r\n return;\r\n case \"clear\":\r\n // 清空所有会话状态,回到初始界面\r\n setDisplayMessages([]);\r\n setStaticKey((prev) => prev + 1);\r\n setInput(\"\");\r\n setStreamError(undefined);\r\n // 先清屏再清滚动区,确保终端完全重置\r\n process.stdout.write(\"\\x1b[2J\\x1b[H\\x1b[3J\");\r\n // 重置 Session 历史\r\n sessionRef.current?.reset();\r\n return;\r\n case \"navigate\":\r\n setInput(\"\");\r\n if (result.target === \"game\") {\r\n onLaunchGame?.();\r\n } else if (result.target === \"stock\") {\r\n onLaunchStock?.();\r\n }\r\n return;\r\n case \"text\":\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: result.content },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n }\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: `未知命令:${trimmed}。输入 /help 查看。` },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n // 检查 Session 是否就绪\r\n if (!sessionRef.current) {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n {\r\n role: \"assistant\",\r\n content: \"⚠ 无法连接到 Provider。请检查 API Key 和网络配置。\",\r\n },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n // ---- 正常对话:接入 Agent 流式主循环 ----\r\n\r\n // 追加用户消息到显示列表\r\n setDisplayMessages((prev) => [...prev, { role: \"user\", content: trimmed }]);\r\n\r\n // 进入流式状态\r\n setInput(\"\");\r\n // 重置流式状态\r\n setIsStreaming(true);\r\n setStreamingPhase(\"thinking\");\r\n setStreamingPlaceholder(pickRandom(STREAMING_PLACEHOLDERS));\r\n setCurrentContent(\"\");\r\n setCurrentReasoning([]);\r\n setCurrentToolCalls([]);\r\n setCurrentUsage(undefined);\r\n setCurrentElapsed(undefined);\r\n setCurrentCost(undefined);\r\n setStreamingModel(undefined);\r\n setStreamError(undefined);\r\n // 同步重置 ref\r\n currentContentRef.current = \"\";\r\n currentReasoningRef.current = [];\r\n currentToolCallsRef.current = [];\r\n currentUsageRef.current = undefined;\r\n currentElapsedRef.current = undefined;\r\n currentCostRef.current = undefined;\r\n currentModelRef.current = undefined;\r\n streamErrorRef.current = undefined;\r\n // 新一轮对话开始:清除上轮可能残留的 rewind 提示\r\n currentRoundModifiedRef.current = false;\r\n setRewindHintPhase(\"idle\");\r\n if (rewindHintTimerRef.current) {\r\n clearTimeout(rewindHintTimerRef.current);\r\n rewindHintTimerRef.current = null;\r\n }\r\n\r\n const session = sessionRef.current;\r\n const abortController = new AbortController();\r\n abortRef.current = abortController;\r\n\r\n try {\r\n for await (const event of session.chat(trimmed, {\r\n thinkingAllowed: thinkingEnabled || undefined,\r\n thinkingEffort: thinkingEnabled ? thinkingEffort : undefined,\r\n responseFormat: responseFormat !== \"text\" ? responseFormat : undefined,\r\n toolChoice,\r\n })) {\r\n // 如果请求被取消,跳过后续事件\r\n if (abortController.signal.aborted) break;\r\n\r\n switch (event.type) {\r\n case \"text_delta\":\r\n setStreamingPhase(\"generating\");\r\n setCurrentContent((prev) => {\r\n const next = prev + event.content;\r\n currentContentRef.current = next;\r\n return next;\r\n });\r\n break;\r\n\r\n case \"reasoning_delta\":\r\n // 思考链仍处于 thinking 阶段,UI 上以暗色块单独展示\r\n // 连续多段之间如果间隔了 text/tool 会被切为多个独立段,避免合并\r\n setStreamingPhase(\"thinking\");\r\n setCurrentReasoning((prev) => {\r\n const last = prev[prev.length - 1];\r\n const next =\r\n last !== undefined\r\n ? [...prev.slice(0, -1), last + event.content]\r\n : [...prev, event.content];\r\n currentReasoningRef.current = next;\r\n return next;\r\n });\r\n break;\r\n\r\n case \"tool_calls\":\r\n setStreamingPhase(\"calling_tools\");\r\n setCurrentToolCalls((prev) => {\r\n const next = [...prev, ...event.calls];\r\n currentToolCallsRef.current = next;\r\n return next;\r\n });\r\n // 标记本轮是否触发了文件修改类工具调用\r\n for (const call of event.calls) {\r\n if (isFileMutatingTool(call.name)) {\r\n currentRoundModifiedRef.current = true;\r\n break;\r\n }\r\n }\r\n break;\r\n\r\n case \"tool_result\":\r\n // 工具执行完成 — 重置流式状态,准备接收模型的新一轮回复\r\n // 这是因为 Agent 循环会在工具执行后再次调用模型\r\n setStreamingPhase(\"executing_tools\");\r\n // 延迟重置为思考中,表示模型正在处理工具结果进行下一轮推理\r\n setTimeout(() => setStreamingPhase(\"thinking\"), 300);\r\n setCurrentContent(\"\");\r\n currentContentRef.current = \"\";\r\n // 思考链:工具结果代表一个 sub-turn 结束,把“正在累积的那一段”封口(推 \"\" 占位),\r\n // 下一段思考会 push 到数组末尾,原有前几段保留可见。\r\n setCurrentReasoning((prev) => {\r\n const next = [...prev, \"\"];\r\n currentReasoningRef.current = next;\r\n return next;\r\n });\r\n setCurrentToolCalls([]);\r\n currentToolCallsRef.current = [];\r\n // 将工具结果追加为一条用户可见的消息\r\n const r = event.result;\r\n if (event.name.startsWith(\"todo_\") && event.todoSnapshot) {\r\n // todo_* 工具:更新独立的任务进度 state(覆盖而非追加),不产生消息条目。\r\n // 全部结束后(所有项都是 done / failed / skipped)→ 直接清空 snapshot,\r\n // 让 <TodoListPanel> 立即消失,避免结束后面板还赖着占位。\r\n // AssistantMessage 里已渲染的 todo_* 工具块保留(那是历史回放)。\r\n const allTerminated =\r\n event.todoSnapshot.length > 0 &&\r\n event.todoSnapshot.every(\r\n (it) =>\r\n it.status === \"done\" ||\r\n it.status === \"failed\" ||\r\n it.status === \"skipped\",\r\n );\r\n if (allTerminated) {\r\n setTodoSnapshot([]);\r\n } else {\r\n setTodoSnapshot(event.todoSnapshot);\r\n }\r\n } else {\r\n // 非 todo 工具:优先使用 summary(一行简短摘要),避免在 UI 中撑出大段文件内容\r\n const line = r.success\r\n ? (r.summary ??\r\n `✅ ${event.name}: ${r.data.slice(0, 500)}${r.data.length > 500 ? \"...\" : \"\"}`)\r\n : `❌ ${event.name}: ${r.error ?? \"执行失败\"}`;\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n {\r\n role: \"tool\" as const,\r\n content: line,\r\n diff: r.diff,\r\n },\r\n ]);\r\n }\r\n break;\r\n\r\n case \"usage\":\r\n setCurrentUsage(event.usage);\r\n setStreamingModel(event.model);\r\n currentUsageRef.current = event.usage;\r\n currentModelRef.current = event.model;\r\n // 同步计算费用\r\n {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const cost = calculateCost(\r\n event.usage,\r\n event.model as unknown as ModelId,\r\n );\r\n setCurrentCost(cost.totalCost);\r\n currentCostRef.current = cost.totalCost;\r\n }\r\n break;\r\n\r\n case \"done\":\r\n setCurrentElapsed(event.elapsed);\r\n currentElapsedRef.current = event.elapsed;\r\n break;\r\n\r\n case \"error\":\r\n setStreamError(event.error.message);\r\n streamErrorRef.current = event.error.message;\r\n break;\r\n }\r\n }\r\n } catch (err: unknown) {\r\n const msg = err instanceof Error ? err.message : String(err);\r\n setStreamError(msg);\r\n streamErrorRef.current = msg;\r\n } finally {\r\n setIsStreaming(false);\r\n setStreamingPhase(null);\r\n setIdlePlaceholder(pickRandom(IDLE_PLACEHOLDERS));\r\n abortRef.current = null;\r\n\r\n // 流式结束后,用 ref 拿到最新值,直接追加完成的助手消息\r\n const finContent = currentContentRef.current;\r\n // 过滤掉占位的空串和首尾空白,避免在 UI 上生成空白思考块\r\n const finReasoning = currentReasoningRef.current\r\n .map((s) => s.trim())\r\n .filter((s) => s.length > 0);\r\n // 过滤掉 todo_* 工具:它们是任务进度的元数据,不在消息列表里展示\r\n // (TodoListPanel 独立维护了一份进度状态)\r\n const finToolCalls = filterTodoToolCalls(currentToolCallsRef.current);\r\n const finStreamError = streamErrorRef.current;\r\n\r\n if (finContent || finToolCalls || finStreamError) {\r\n const completed: CompletedAssistant = {\r\n content: finStreamError ? `⚠ 请求出错:${finStreamError}` : finContent || \"\",\r\n ...(finReasoning.length > 0 ? { reasoning: finReasoning } : {}),\r\n toolCalls: finToolCalls,\r\n usage: currentUsageRef.current,\r\n elapsed: currentElapsedRef.current,\r\n cost: currentCostRef.current,\r\n model: currentModelRef.current,\r\n };\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n {\r\n role: \"assistant\" as const,\r\n content: completed.content,\r\n assistantDetail: completed,\r\n },\r\n ]);\r\n }\r\n\r\n // 如果本轮触发了文件修改且无流错误 -> 2s 后展示 rewind 提示\r\n // 提示会一直保留到下次对话开始(流式再次启动)为止\r\n if (currentRoundModifiedRef.current && !finStreamError) {\r\n setRewindHintPhase(\"pending\");\r\n if (rewindHintTimerRef.current) clearTimeout(rewindHintTimerRef.current);\r\n rewindHintTimerRef.current = setTimeout(() => {\r\n setRewindHintPhase((prev) => (prev === \"pending\" ? \"visible\" : prev));\r\n rewindHintTimerRef.current = null;\r\n }, 2000);\r\n }\r\n }\r\n },\r\n [\r\n onLaunchGame,\r\n onLaunchStock,\r\n currentContent,\r\n currentReasoning,\r\n currentToolCalls,\r\n skills,\r\n skillSelectIndex,\r\n getFilteredSkills,\r\n thinkingEnabled,\r\n thinkingEffort,\r\n responseFormat,\r\n toolChoice,\r\n activeModel,\r\n sessionMode,\r\n isStreaming,\r\n rewinding,\r\n ],\r\n );\r\n\r\n // 从 costTracker 更新今日消耗(每次流式结束后刷新)\r\n useEffect(() => {\r\n if (!isStreaming && externalCostTracker) {\r\n setTodayCost(externalCostTracker.todayTotalCost);\r\n }\r\n }, [isStreaming, externalCostTracker]);\r\n\r\n return (\r\n <Box flexDirection=\"column\" paddingLeft={1} paddingRight={1}>\r\n {/* Logo + 状态栏 + 余额 — 三栏布局(仅对话未开始时显示) */}\r\n {!hasConversationStarted && (\r\n <Box flexDirection=\"row\" marginBottom={1}>\r\n {/* Logo */}\r\n <Box flexDirection=\"column\" marginRight={4}>\r\n {LOGO_LINES.map((line, i) => {\r\n const colorIndex = (i + offset) % CYBER_PALETTE.length;\r\n return (\r\n <Box key={i}>\r\n <Text bold color={CYBER_PALETTE[colorIndex]}>\r\n {line}\r\n </Text>\r\n </Box>\r\n );\r\n })}\r\n </Box>\r\n\r\n {/* 状态信息 */}\r\n <Box flexDirection=\"column\" justifyContent=\"center\">\r\n <Text color=\"#00ff41\">\r\n {\" ✔ \"}已就绪 {skillCount} 个 Skill\r\n </Text>\r\n <Text color=\"#00ffff\">\r\n {\" ℹ \"}已就绪 {toolCount} 个工具\r\n </Text>\r\n <Text color=\"#00ffff\">\r\n {\" 🔧 模型 \"}\r\n {SUPPORTED_MODELS[activeModel]?.displayName ?? activeModel}\r\n </Text>\r\n {thinkingEnabled && (\r\n <Text color=\"#ff9800\">\r\n {\" 🧠 深度思考 \"}\r\n {thinkingEffort === \"max\" ? \"Max\" : \"High\"}\r\n </Text>\r\n )}\r\n {/* 模式指示器 */}\r\n {sessionMode === \"plan\" && (\r\n <Text color=\"#ff69b4\" bold>\r\n {\" 📋 计划模式\"}\r\n </Text>\r\n )}\r\n {responseFormat === \"json_object\" && (\r\n <Text color=\"#4caf50\">{\" 📄 JSON\"}</Text>\r\n )}\r\n {toolChoice !== undefined && (\r\n <Text color=\"#e91e63\">\r\n {\" 🛠 \"}\r\n {toolChoice === \"none\"\r\n ? \"禁止工具\"\r\n : toolChoice === \"required\"\r\n ? \"强制工具\"\r\n : \"\"}\r\n </Text>\r\n )}\r\n {/* 命令提示轮播 */}\r\n {cmdTips.length > 0 &&\r\n (() => {\r\n const tip = cmdTips[cmdTipIndex % cmdTips.length];\r\n if (!tip) return null;\r\n const text = `${tip.name} ${tip.desc}`;\r\n return (\r\n <Text>\r\n <Text color=\"#808080\">{\" 💡 \"}</Text>\r\n {cmdTipGradientColors.length > 0 ? (\r\n text.split(\"\").map((ch, i) => (\r\n <Text key={i} color={cmdTipGradientColors[i] || undefined}>\r\n {ch}\r\n </Text>\r\n ))\r\n ) : (\r\n <Text color=\"#808080\">{text}</Text>\r\n )}\r\n </Text>\r\n );\r\n })()}\r\n {verbose ? <Text color=\"#ff1493\">{\" ⚡ Verbose\"}</Text> : null}\r\n </Box>\r\n\r\n {/* 右侧余额 + 今日消耗 */}\r\n <Box\r\n flexGrow={1}\r\n flexDirection=\"column\"\r\n justifyContent=\"center\"\r\n alignItems=\"flex-end\"\r\n >\r\n {balanceLoading && balance === null ? (\r\n <Text color=\"yellow\">{\" ⏳ 查询余额...\"}</Text>\r\n ) : balance !== null ? (\r\n <Box flexDirection=\"row\">\r\n <Text color=\"yellow\">{\"💰 \"}</Text>\r\n <Text color=\"yellow\">\r\n {\"余额 ¥\"}\r\n {balance.toFixed(2)}\r\n </Text>\r\n </Box>\r\n ) : null}\r\n {todayCost !== null ? (\r\n <Box flexDirection=\"row\">\r\n <Text color=\"cyan\">{\"📊 \"}</Text>\r\n <Text color=\"cyan\">\r\n {\"今日 ¥\"}\r\n {todayCost.toFixed(2)}\r\n </Text>\r\n </Box>\r\n ) : null}\r\n </Box>\r\n </Box>\r\n )}\r\n\r\n {/* 消息列表 - 已完成的消息用 Static 固定,避免重绘时丢失滚动位置 */}\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n <Static key={staticKey} items={displayMessages}>\r\n {(msg, i) => {\r\n if (msg.role === \"user\") {\r\n return (\r\n <Box key={i} marginTop={1}>\r\n <Box width={4} flexShrink={0}>\r\n <Text bold color=\"#00ff41\">\r\n {\"👤\"}\r\n </Text>\r\n </Box>\r\n <Box flexGrow={1}>\r\n <Text wrap=\"wrap\">{msg.content}</Text>\r\n </Box>\r\n </Box>\r\n );\r\n }\r\n\r\n // 工具消息 — 显示文本内容 + Diff 预览\r\n // 工具调用属于“过程信息”,使用 dimColor 让主内容(助手回复)的视觉权重更高\r\n if (msg.role === \"tool\") {\r\n return (\r\n <Box key={i} marginTop={1} flexDirection=\"column\">\r\n <Box flexDirection=\"row\">\r\n <Box width={4} flexShrink={0}>\r\n <Text dimColor>{\"🔧\"}</Text>\r\n </Box>\r\n <Box flexGrow={1}>\r\n <Text dimColor wrap=\"wrap\">\r\n {msg.content}\r\n </Text>\r\n </Box>\r\n </Box>\r\n {msg.diff && <DiffPreview diff={msg.diff} />}\r\n </Box>\r\n );\r\n }\r\n\r\n // 已完成的助手消息\r\n const detail = msg.assistantDetail;\r\n return (\r\n <AssistantMessage\r\n key={i}\r\n content={msg.content}\r\n reasoning={detail?.reasoning}\r\n toolCalls={detail?.toolCalls}\r\n isStreaming={false}\r\n usage={detail?.usage}\r\n elapsed={detail?.elapsed}\r\n cost={detail?.cost}\r\n model={detail?.model}\r\n />\r\n );\r\n }}\r\n </Static>\r\n\r\n {/* 正在流式输出的助手消息 */}\r\n {isStreaming && (\r\n <AssistantMessage\r\n content={currentContent}\r\n reasoning={currentReasoning}\r\n toolCalls={currentToolCalls.length > 0 ? currentToolCalls : undefined}\r\n isStreaming={true}\r\n usage={_currentUsage}\r\n cost={_currentCost}\r\n model={_streamingModel}\r\n />\r\n )}\r\n\r\n {/* 错误信息(流式结束后显示) */}\r\n {!isStreaming && streamError && (\r\n <Box marginTop={1} marginLeft={3}>\r\n <Text color=\"red\">⚠ {streamError}</Text>\r\n </Box>\r\n )}\r\n </Box>\r\n\r\n {/* 独立的任务进度面板 — 不混入消息列表,每次 todo_* 刷新原地更新。\r\n 任务全部结束后 2s 自动隐藏;新任务开启时重新显示。 */}\r\n {todoPanelVisible && todoSnapshot.length > 0 && (\r\n <TodoListPanel items={todoSnapshot} />\r\n )}\r\n\r\n {/* 输入区 */}\r\n {selectingModel ? (\r\n <Box marginTop={1} flexDirection=\"column\">\r\n <Text color=\"#00ffff\" dimColor>\r\n {\"─\".repeat(dividerWidth)}\r\n </Text>\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n <Text bold color=\"#00ffff\">\r\n 选择模型:\r\n </Text>\r\n {modelOptions.map((id, i) => {\r\n const meta = SUPPORTED_MODELS[id];\r\n const isCurrent = id === activeModel;\r\n const isSelected = i === modelSelectIndex;\r\n const marker = isSelected ? \" > \" : \" \";\r\n const suffix = isCurrent ? \" (当前)\" : \"\";\r\n return (\r\n <Box key={id}>\r\n <Text color={isSelected ? \"#00ff41\" : undefined} bold={isSelected}>\r\n {marker}\r\n {meta.displayName}\r\n {suffix}\r\n </Text>\r\n {isSelected && <Text color=\"#808080\"> — {id}</Text>}\r\n </Box>\r\n );\r\n })}\r\n <Box marginTop={1}>\r\n <Text color=\"#808080\" dimColor>\r\n ↑↓ 选择 · Enter 确认 · Esc 取消\r\n </Text>\r\n </Box>\r\n </Box>\r\n <Text color=\"#00ffff\" dimColor>\r\n {\"─\".repeat(dividerWidth)}\r\n </Text>\r\n </Box>\r\n ) : rewindSelecting ? (\r\n <Box marginTop={1} flexDirection=\"column\">\r\n <Text color=\"#00ffff\" dimColor>\r\n {\"─\".repeat(dividerWidth)}\r\n </Text>\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n <Text bold color=\"#ff9800\">\r\n 选择要回退的检查点:\r\n </Text>\r\n {rewindList.map((cp, i) => {\r\n const isSelected = i === rewindSelectIndex;\r\n const marker = isSelected ? \" > \" : \" \";\r\n const time = new Date(cp.timestamp).toLocaleTimeString();\r\n const preview = cp.preview || \"(空)\";\r\n const tag = cp.isGitRepo ? \"\" : \" [非 git,仅对话]\";\r\n return (\r\n <Box key={cp.index}>\r\n <Text color={isSelected ? \"#ff9800\" : undefined} bold={isSelected}>\r\n {marker}#{i + 1} {time} `{preview}\r\n {tag}`\r\n </Text>\r\n </Box>\r\n );\r\n })}\r\n <Box marginTop={1}>\r\n <Text color=\"#808080\" dimColor>\r\n ↑↓ 选择 · Enter 确认 · Esc 取消\r\n </Text>\r\n </Box>\r\n </Box>\r\n <Text color=\"#00ffff\" dimColor>\r\n {\"─\".repeat(dividerWidth)}\r\n </Text>\r\n </Box>\r\n ) : (\r\n <>\r\n {/* 流式状态指示器 — 上框上方居中显示 */}\r\n {(hasConversationStarted || sessionMode === \"plan\") &&\r\n isStreaming &&\r\n streamingPhase ? (\r\n <Box marginTop={1} justifyContent=\"center\">\r\n <Text bold color={PHASE_CONFIG[streamingPhase].color}>\r\n {PHASE_CONFIG[streamingPhase].icon} {PHASE_CONFIG[streamingPhase].label}{\" \"}\r\n <InkSpinner type=\"dots\" />\r\n </Text>\r\n </Box>\r\n ) : rewindHintPhase === \"visible\" ? (\r\n // 本轮修改了文件时,流式结束后 2s 在原位置展示 /rewind 提示\r\n // 一直保留到下次对话开始,与 /rewind 1(最新检查点)配套\r\n <Box marginTop={1} justifyContent=\"center\">\r\n <Text color=\"#808080\">{\"↩ /rewind 1 可撤回本次修改\"}</Text>\r\n </Box>\r\n ) : null}\r\n <Box marginTop={1}>\r\n {hasConversationStarted || sessionMode === \"plan\" ? (\r\n <Text\r\n color={sessionMode === \"plan\" ? \"#ff69b4\" : \"#00ffff\"}\r\n dimColor={sessionMode !== \"plan\"}\r\n >\r\n <Text>\r\n {sessionMode === \"plan\"\r\n ? \"─\".repeat(Math.max(dividerWidth - 48, 1))\r\n : \"─\".repeat(Math.max(dividerWidth - 35, 10))}\r\n </Text>\r\n {balance !== null && (\r\n <Text color=\"yellow\">\r\n {\" 💰 余额 ¥\"}\r\n {balance.toFixed(2)}\r\n </Text>\r\n )}\r\n {isStreaming ? (\r\n <Text color=\"cyan\">\r\n {\" 📊 本次 ¥\"}\r\n {sessionCost > 0 ? sessionCost.toFixed(4) + \" \" : \"\"}\r\n <InkSpinner type=\"dots\" />\r\n </Text>\r\n ) : sessionCost > 0 ? (\r\n <Text color=\"cyan\">\r\n {\" 📊 本次 ¥\"}\r\n {sessionCost.toFixed(4)}\r\n </Text>\r\n ) : null}\r\n {sessionMode === \"plan\" && (\r\n <Text color=\"#ff69b4\" bold>\r\n {\" 📋 计划模式\"}\r\n </Text>\r\n )}\r\n </Text>\r\n ) : (\r\n <Text color=\"#00ffff\" dimColor>\r\n {\"─\".repeat(dividerWidth)}\r\n </Text>\r\n )}\r\n </Box>\r\n <Box>\r\n <Box width={4} flexShrink={0}>\r\n <Text bold color=\"#00ff41\">\r\n {\"⚡\"}\r\n </Text>\r\n </Box>\r\n <Box flexGrow={1}>\r\n {!input && !isStreaming && idlePlaceholder && gradientColors.length > 0 ? (\r\n <Text>\r\n {idlePlaceholder.split(\"\").map((ch, i) => (\r\n <Text key={i} color={gradientColors[i] ?? undefined}>\r\n {ch}\r\n </Text>\r\n ))}\r\n </Text>\r\n ) : !input &&\r\n isStreaming &&\r\n streamingPlaceholder &&\r\n streamingGradientColors.length > 0 ? (\r\n <Text>\r\n {streamingPlaceholder.split(\"\").map((ch, i) => (\r\n <Text key={i} color={streamingGradientColors[i] ?? undefined}>\r\n {ch}\r\n </Text>\r\n ))}\r\n </Text>\r\n ) : (\r\n <TextInput\r\n key={inputKey}\r\n value={input}\r\n onChange={setInput}\r\n onSubmit={handleSubmit}\r\n placeholder=\"\"\r\n />\r\n )}\r\n </Box>\r\n </Box>\r\n <Box>\r\n <Text color=\"#00ffff\" dimColor>\r\n {\"─\".repeat(dividerWidth)}\r\n </Text>\r\n </Box>\r\n\r\n {/* 用户输入 / 时显示 skill 列表 */}\r\n <SkillSelector skills={skills} input={input} selectedIndex={skillSelectIndex} />\r\n {/* 用户输入 @ 时显示文件列表 */}\r\n <FileSelector files={files} input={input} selectedIndex={fileSelectIndex} />\r\n </>\r\n )}\r\n\r\n {/* 双击 Ctrl+C 退出提示 */}\r\n {doubleCtrlC && !isStreaming && (\r\n <Box marginTop={1}>\r\n <Text color=\"#ff1493\" bold>\r\n {\" ⚠ 再按一次 Ctrl+C 退出 dskcode\"}\r\n </Text>\r\n </Box>\r\n )}\r\n\r\n {/* 流式中 Ctrl+C 取消提示 */}\r\n {isStreaming && (\r\n <Box marginTop={1}>\r\n <Text color=\"yellow\" dimColor>\r\n {\" 提示:按 Ctrl+C 取消当前请求\"}\r\n </Text>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}\r\n","import { useState, useCallback, useEffect, useRef } from \"react\";\r\n\r\nconst CTRL_C_TIMEOUT_MS = 1500;\r\n\r\n/**\r\n * 提供\"双击 Ctrl+C 才退出\"的交互逻辑。\r\n *\r\n * - 第一次 Ctrl+C:设置提示状态,1.5 秒后自动重置\r\n * - 第二次 Ctrl+C(1.5 秒内):执行 onExit 回调\r\n *\r\n * 返回 { doubleCtrlC, handleCtrlC },组件通过 useInput 捕获\r\n * Ctrl+C 后调用 handleCtrlC,并根据 doubleCtrlC 显示提示。\r\n */\r\nexport function useDoubleCtrlC(onExit: () => void) {\r\n const [doubleCtrlC, setDoubleCtrlC] = useState(false);\r\n const timerRef = useRef<ReturnType<typeof setTimeout>>(null);\r\n const onExitRef = useRef(onExit);\r\n onExitRef.current = onExit;\r\n\r\n // 组件卸载时清理定时器\r\n useEffect(() => {\r\n return () => {\r\n if (timerRef.current) clearTimeout(timerRef.current);\r\n };\r\n }, []);\r\n\r\n const handleCtrlC = useCallback(() => {\r\n if (doubleCtrlC) {\r\n // 第二次 Ctrl+C → 退出\r\n onExitRef.current();\r\n return;\r\n }\r\n\r\n // 第一次 Ctrl+C → 显示提示,启动倒计时重置\r\n setDoubleCtrlC(true);\r\n if (timerRef.current) clearTimeout(timerRef.current);\r\n timerRef.current = setTimeout(() => {\r\n setDoubleCtrlC(false);\r\n timerRef.current = null;\r\n }, CTRL_C_TIMEOUT_MS);\r\n }, [doubleCtrlC]);\r\n\r\n return { doubleCtrlC, handleCtrlC };\r\n}","// ---------------------------------------------------------------------------\r\n// 助手消息组件 — 渲染单条 assistant 回复(流式文本 + 工具调用 + 成本行)\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\nimport { useEffect, useRef, useState } from \"react\";\r\nimport type { ProviderToolCall, UsageInfo } from \"../provider/index.js\";\r\nimport { formatMoney } from \"../provider/cost-tracker.js\";\r\nimport { ToolCallBlock } from \"./ToolCallBlock.js\";\r\nimport { formatUsageSummary } from \"../agent/message-builder.js\";\r\nimport { HighlightedText } from \"./HighlightedText.js\";\r\nimport {\r\n DEFAULT_REASONING_MAX_LINES,\r\n joinReasoningSegments,\r\n truncateReasoningLines,\r\n} from \"./reasoning-utils.js\";\r\n\r\ninterface AssistantMessageProps {\r\n /** 助手回复的文本内容 */\r\n content: string;\r\n /**\r\n * 思考链段列表(thinking 模式的 CoT),可选。\r\n * 一个回合可能包含多段 CoT(多轮“思考→调工具”),每段独立成块展示。\r\n */\r\n reasoning?: string[];\r\n /** 工具调用列表 */\r\n toolCalls?: ProviderToolCall[];\r\n /** 是否正在流式输出中 */\r\n isStreaming?: boolean;\r\n /** Token 使用统计 */\r\n usage?: UsageInfo;\r\n /** 本次调用耗时(毫秒) */\r\n elapsed?: number;\r\n /** 本次调用费用 */\r\n cost?: number;\r\n /** 使用的模型标识 */\r\n model?: string;\r\n}\r\n\r\n/** 格式化毫秒为人类可读的时间 */\r\nfunction formatElapsed(ms: number): string {\r\n if (ms < 1000) return `${ms}ms`;\r\n const seconds = (ms / 1000).toFixed(1);\r\n return `${seconds}s`;\r\n}\r\n\r\n/**\r\n * 平滑动画的实时消耗行。\r\n *\r\n * props 中的 token/cost 是“目标值”,组件内部以 33ms (~30fps) 步进\r\n * 用 easeOutCubic 缓动插值,避免上游 300ms 节流推送造成的跳变感。\r\n * 当 target ≤ displayed 时直接跳到目标(支持估算/真实值互相覆盖)。\r\n */\r\nfunction AnimatedUsage({ usage, cost }: { usage?: UsageInfo; cost?: number }) {\r\n const targetTokens = usage ? usage.promptTokens + usage.completionTokens : 0;\r\n const targetCost = cost ?? 0;\r\n\r\n // 初次进入时直接定位到首帧,避免从 0 长动画\r\n const [displayedTokens, setDisplayedTokens] = useState(targetTokens);\r\n const [displayedCost, setDisplayedCost] = useState(targetCost);\r\n\r\n // 记录“上次起点”,动画以该起点为 0% 位置,趋近 targetTokens / targetCost\r\n const startRef = useRef<{\r\n fromTokens: number;\r\n fromCost: number;\r\n toTokens: number;\r\n toCost: number;\r\n startMs: number;\r\n durationMs: number;\r\n } | null>(null);\r\n const rafRef = useRef<ReturnType<typeof setInterval> | null>(null);\r\n\r\n useEffect(() => {\r\n // 目标值不变时跳过\r\n if (\r\n startRef.current &&\r\n startRef.current.toTokens === targetTokens &&\r\n startRef.current.toCost === targetCost\r\n ) {\r\n return;\r\n }\r\n\r\n // 目标值 ≤ 当前显示值(例:真实 usage 覆盖估算值,且可能偏低)\r\n // 或首次进入 → 直接跳到目标\r\n if (targetTokens <= displayedTokens && targetCost <= displayedCost) {\r\n setDisplayedTokens(targetTokens);\r\n setDisplayedCost(targetCost);\r\n startRef.current = {\r\n fromTokens: targetTokens,\r\n fromCost: targetCost,\r\n toTokens: targetTokens,\r\n toCost: targetCost,\r\n startMs: Date.now(),\r\n durationMs: 0,\r\n };\r\n return;\r\n }\r\n\r\n // 按变化量粗略估算动画时长:300ms 节流周期 + 少许渐变\r\n const tokensDelta = targetTokens - displayedTokens;\r\n const costDelta = targetCost - displayedCost;\r\n const durationMs = Math.min(600, Math.max(220, Math.max(tokensDelta, 0) * 1.2 + 220));\r\n startRef.current = {\r\n fromTokens: displayedTokens,\r\n fromCost: displayedCost,\r\n toTokens: targetTokens,\r\n toCost: targetCost,\r\n startMs: Date.now(),\r\n durationMs,\r\n };\r\n\r\n if (rafRef.current) clearInterval(rafRef.current);\r\n rafRef.current = setInterval(() => {\r\n const s = startRef.current;\r\n if (!s) return;\r\n const elapsed = Date.now() - s.startMs;\r\n if (elapsed >= s.durationMs) {\r\n setDisplayedTokens(s.toTokens);\r\n setDisplayedCost(s.toCost);\r\n if (rafRef.current) {\r\n clearInterval(rafRef.current);\r\n rafRef.current = null;\r\n }\r\n return;\r\n }\r\n // easeOutCubic:1 - (1 - t)^3\r\n const t = elapsed / s.durationMs;\r\n const eased = 1 - Math.pow(1 - t, 3);\r\n const tokens = s.fromTokens + (s.toTokens - s.fromTokens) * eased;\r\n const cost = s.fromCost + (s.toCost - s.fromCost) * eased;\r\n setDisplayedTokens(tokens);\r\n setDisplayedCost(cost);\r\n }, 33);\r\n\r\n return () => {\r\n if (rafRef.current) {\r\n clearInterval(rafRef.current);\r\n rafRef.current = null;\r\n }\r\n };\r\n // 只在目标值变化时重启动画,不依赖 displayedX\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [targetTokens, targetCost]);\r\n\r\n return (\r\n <>\r\n {usage && (\r\n <Text color=\"#888888\">{Math.round(displayedTokens).toLocaleString()} tokens</Text>\r\n )}\r\n {cost !== undefined && cost > 0 && (\r\n <Text color=\"#888888\">\r\n {\" · \"}¥{displayedCost.toFixed(4)}\r\n </Text>\r\n )}\r\n </>\r\n );\r\n}\r\n\r\n/**\r\n * 渲染单条助手回复。\r\n *\r\n * 显示结构:\r\n * 🤖 流式文本内容...\r\n * 📦 tool_name ────\r\n * 参数摘要\r\n * ────────────────────────────\r\n * 💰 本次 ¥0.0012 │ 🕐 3.2s │ 📦 1.2k tokens\r\n */\r\nexport function AssistantMessage({\r\n content,\r\n reasoning,\r\n toolCalls,\r\n isStreaming = false,\r\n usage,\r\n elapsed,\r\n cost,\r\n model: _model,\r\n}: AssistantMessageProps) {\r\n // 内容为空且无工具调用且无思考链时不渲染\r\n if (\r\n !content &&\r\n (!toolCalls || toolCalls.length === 0) &&\r\n (!reasoning || reasoning.length === 0) &&\r\n !isStreaming\r\n ) {\r\n return null;\r\n }\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n {/* 思考链:所有段拼接为单文本(段间空行分隔),统一截断到 8 行。\r\n 多 sub-turn 的短段不再各自画框,避免空块堆点屏幕。 */}\r\n {reasoning &&\r\n reasoning.length > 0 &&\r\n (() => {\r\n const merged = joinReasoningSegments(reasoning);\r\n if (!merged) return null;\r\n // 滚动窗口:保留最后 N 行(最新思考),丢弃开头的旧行,与终端自动滚屏一致\r\n const { visible } = truncateReasoningLines(\r\n merged,\r\n DEFAULT_REASONING_MAX_LINES,\r\n );\r\n return (\r\n <Box flexDirection=\"row\" marginBottom={1}>\r\n <Box width={4} flexShrink={0}>\r\n <Text dimColor>{\"🧠\"}</Text>\r\n </Box>\r\n <Box\r\n flexGrow={1}\r\n flexDirection=\"column\"\r\n borderStyle=\"single\"\r\n borderColor=\"#444444\"\r\n paddingLeft={1}\r\n paddingRight={1}\r\n >\r\n <Text dimColor wrap=\"wrap\">\r\n {visible}\r\n </Text>\r\n </Box>\r\n </Box>\r\n );\r\n })()}\r\n\r\n {/* 助手标识 + 内容 */}\r\n <Box flexDirection=\"row\">\r\n <Box width={4} flexShrink={0}>\r\n <Text bold color=\"#ff00ff\">\r\n {\"🤖\"}\r\n </Text>\r\n </Box>\r\n <Box flexGrow={1} flexDirection=\"column\">\r\n {/* 文本内容(带语法高亮) */}\r\n {content && <HighlightedText>{content}</HighlightedText>}\r\n {/* 流式输出时的光标 */}\r\n {isStreaming && !content && (!reasoning || reasoning.length === 0) && (\r\n <Text color=\"#888888\">...</Text>\r\n )}\r\n </Box>\r\n </Box>\r\n\r\n {/* 工具调用 */}\r\n {toolCalls && toolCalls.length > 0 && (\r\n <Box flexDirection=\"column\">\r\n {toolCalls.map((tc, i) => (\r\n <ToolCallBlock key={tc.id ?? i} call={tc} />\r\n ))}\r\n </Box>\r\n )}\r\n\r\n {/* 流式输出中的实时消耗(带平滑动画) */}\r\n {isStreaming && (usage || cost !== undefined) && (\r\n <Box flexDirection=\"row\" marginTop={1} marginLeft={3}>\r\n <Text color=\"#666666\" dimColor>\r\n ⏳ 已消耗 <AnimatedUsage usage={usage} cost={cost} />\r\n </Text>\r\n </Box>\r\n )}\r\n\r\n {/* 成本/耗时摘要行(仅在流式结束后显示) */}\r\n {!isStreaming && (usage || elapsed !== undefined) && (\r\n <Box flexDirection=\"column\" marginTop={1} marginLeft={3}>\r\n <Text color=\"#555555\">{\"─\".repeat(36)}</Text>\r\n <Box flexDirection=\"row\" gap={2}>\r\n {cost !== undefined && cost > 0 && (\r\n <Text color=\"yellow\">💰 本次 {formatMoney(cost)}</Text>\r\n )}\r\n {elapsed !== undefined && (\r\n <Text color=\"cyan\">🕐 {formatElapsed(elapsed)}</Text>\r\n )}\r\n {usage && <Text color=\"#888888\">{formatUsageSummary(usage)}</Text>}\r\n </Box>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 成本追踪器 — 会话级 / 日级 / 历史级三层成本统计与持久化\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\r\nimport { join } from \"node:path\";\r\nimport type { UsageInfo, CostInfo, ModelId } from \"./types.js\";\r\nimport { calculateCost } from \"./models.js\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 类型定义\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 单次 API 调用的成本记录 */\r\nexport interface CostRecord {\r\n /** 记录时间戳(ISO 8601) */\r\n timestamp: string;\r\n /** 使用的模型 */\r\n model: ModelId;\r\n /** Token 使用量 */\r\n usage: UsageInfo;\r\n /** 费用明细(单位:元) */\r\n cost: CostInfo;\r\n}\r\n\r\n/** 单个会话的成本汇总 */\r\nexport interface SessionCostSummary {\r\n /** 会话 ID */\r\n sessionId: string;\r\n /** 会话开始时间(ISO 8601) */\r\n startedAt: string;\r\n /** 总输入 token 数 */\r\n totalPromptTokens: number;\r\n /** 总输出 token 数 */\r\n totalCompletionTokens: number;\r\n /** 总缓存命中 token 数 */\r\n totalCachedTokens: number;\r\n /** 总费用(元) */\r\n totalCost: number;\r\n /** 每次调用的详细记录 */\r\n records: CostRecord[];\r\n}\r\n\r\n/** 一天的成本汇总 */\r\nexport interface DailyCostSummary {\r\n /** 日期(YYYY-MM-DD 格式) */\r\n date: string;\r\n /** 当日总输入 token 数 */\r\n totalPromptTokens: number;\r\n /** 当日总输出 token 数 */\r\n totalCompletionTokens: number;\r\n /** 当日总缓存命中 token 数 */\r\n totalCachedTokens: number;\r\n /** 当日总费用(元) */\r\n totalCost: number;\r\n /** 当日 API 调用次数 */\r\n totalCalls: number;\r\n /** 按模型分类的明细 */\r\n byModel: Record<string, ModelCostSummary>;\r\n}\r\n\r\n/** 按模型分类的成本汇总 */\r\nexport interface ModelCostSummary {\r\n /** 模型标识 */\r\n model: ModelId;\r\n /** 该模型的总输入 token 数 */\r\n totalPromptTokens: number;\r\n /** 该模型的总输出 token 数 */\r\n totalCompletionTokens: number;\r\n /** 该模型的总缓存命中 token 数 */\r\n totalCachedTokens: number;\r\n /** 该模型的总费用(元) */\r\n totalCost: number;\r\n /** 该模型的 API 调用次数 */\r\n totalCalls: number;\r\n}\r\n\r\n/** 持久化文件格式 */\r\nexport interface CostStore {\r\n /** 版本号,便于未来格式迁移 */\r\n version: 1;\r\n /** 按日期索引的汇总数据 */\r\n daily: Record<string, DailyCostSummary>;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// CostTracker 配置\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface CostTrackerOptions {\r\n /** 成本持久化目录,默认 ~/.dskcode/costs */\r\n costDir?: string;\r\n /** 预算上限(元),超过后触发回调,0 表示不限制 */\r\n budgetLimit?: number;\r\n /** Token 预算上限,超过后触发回调,0 表示不限制 */\r\n tokenBudgetLimit?: number;\r\n /** 预算超限回调 */\r\n onBudgetExceeded?: (tracker: CostTracker) => void;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// CostTracker 类\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 成本追踪器 — 管理三层成本统计:\r\n *\r\n * 1. **会话级**(内存):当前会话内的累计 token 与费用\r\n * 2. **日级**(内存 + 持久化):按日聚合,支持\"今日消耗\"查询\r\n * 3. **历史级**(持久化):跨日的历史汇总查询\r\n *\r\n * 使用方式:\r\n * ```ts\r\n * const tracker = new CostTracker({ budgetLimit: 10 });\r\n * tracker.record(usage, 'deepseek-v4-flash');\r\n * console.log(tracker.todaySummary());\r\n * ```\r\n */\r\nexport class CostTracker {\r\n /** 全局活跃实例表,用于进程退出时统一 flush。 */\r\n static readonly #instances = new Set<CostTracker>();\r\n\r\n /** 进程退出兜底:统一 flush 所有活跃 CostTracker。 */\r\n static async flushAll(): Promise<void> {\r\n await Promise.allSettled(\r\n [...CostTracker.#instances].map((t) => t.flush()),\r\n );\r\n }\r\n\r\n readonly #costDir: string;\r\n readonly #budgetLimit: number;\r\n readonly #tokenBudgetLimit: number;\r\n readonly #onBudgetExceeded?: (tracker: CostTracker) => void;\r\n\r\n // 会话级统计(内存)\r\n #sessionId: string;\r\n #sessionStartedAt: string;\r\n readonly #sessionRecords: CostRecord[] = [];\r\n\r\n // 日级统计(内存缓存,启动时从磁盘恢复)\r\n #todayDate: string;\r\n #todaySummary: DailyCostSummary;\r\n\r\n // 持久化标记\r\n #dirty = false;\r\n #flushInProgress = false;\r\n\r\n constructor(options: CostTrackerOptions = {}) {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n this.#costDir = options.costDir ?? join(home, \".dskcode\", \"costs\");\r\n this.#budgetLimit = options.budgetLimit ?? 0;\r\n this.#tokenBudgetLimit = options.tokenBudgetLimit ?? 0;\r\n this.#onBudgetExceeded = options.onBudgetExceeded;\r\n\r\n // 初始化会话\r\n this.#sessionId = generateSessionId();\r\n this.#sessionStartedAt = new Date().toISOString();\r\n\r\n // 初始化日级统计\r\n const today = getTodayStr();\r\n this.#todayDate = today;\r\n this.#todaySummary = createEmptyDailySummary(today);\r\n\r\n // 注册到全局实例表(用于进程退出时统一 flush)\r\n CostTracker.#instances.add(this);\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 核心方法\r\n // -----------------------------------------------------------------------\r\n\r\n /**\r\n * 记录一次 API 调用的 Token 使用量和成本。\r\n * 自动计算费用,累加到会话级和日级统计。\r\n * 标记脏数据(dirty),由上层在合适的时机调用 flush() 持久化到磁盘。\r\n */\r\n record(usage: UsageInfo, model: ModelId): CostInfo {\r\n const cost = calculateCost(usage, model);\r\n const record: CostRecord = {\r\n timestamp: new Date().toISOString(),\r\n model,\r\n usage: { ...usage },\r\n cost: { ...cost },\r\n };\r\n\r\n // 1. 会话级累加\r\n this.#sessionRecords.push(record);\r\n\r\n // 2. 日级累加(自动处理日期变更)\r\n this.#ensureTodayBucket();\r\n this.#addToDaily(record);\r\n\r\n // 3. 标记需要持久化(上层在对话周期结束时调用 flush() 落盘)\r\n this.#dirty = true;\r\n\r\n // 4. 预算检查\r\n this.#checkBudget();\r\n\r\n return cost;\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 会话级查询\r\n // -----------------------------------------------------------------------\r\n\r\n /** 当前会话 ID */\r\n get sessionId(): string {\r\n return this.#sessionId;\r\n }\r\n\r\n /** 当前会话的所有成本记录 */\r\n get records(): readonly CostRecord[] {\r\n return this.#sessionRecords;\r\n }\r\n\r\n /** 当前会话累计成本汇总 */\r\n get sessionSummary(): SessionCostSummary {\r\n let totalPromptTokens = 0;\r\n let totalCompletionTokens = 0;\r\n let totalCachedTokens = 0;\r\n let totalCost = 0;\r\n\r\n for (const r of this.#sessionRecords) {\r\n totalPromptTokens += r.usage.promptTokens;\r\n totalCompletionTokens += r.usage.completionTokens;\r\n totalCachedTokens += r.usage.cachedPromptTokens ?? 0;\r\n totalCost += r.cost.totalCost;\r\n }\r\n\r\n return {\r\n sessionId: this.#sessionId,\r\n startedAt: this.#sessionStartedAt,\r\n totalPromptTokens,\r\n totalCompletionTokens,\r\n totalCachedTokens,\r\n totalCost,\r\n records: [...this.#sessionRecords],\r\n };\r\n }\r\n\r\n /** 当前会话总费用(元) */\r\n get sessionTotalCost(): number {\r\n return this.#sessionRecords.reduce((sum, r) => sum + r.cost.totalCost, 0);\r\n }\r\n\r\n /** 当前会话总 prompt token 数 */\r\n get sessionPromptTokens(): number {\r\n return this.#sessionRecords.reduce((sum, r) => sum + r.usage.promptTokens, 0);\r\n }\r\n\r\n /** 当前会话总 completion token 数 */\r\n get sessionCompletionTokens(): number {\r\n return this.#sessionRecords.reduce((sum, r) => sum + r.usage.completionTokens, 0);\r\n }\r\n\r\n /** 当前会话 API 调用次数 */\r\n get sessionCallCount(): number {\r\n return this.#sessionRecords.length;\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 日级查询\r\n // -----------------------------------------------------------------------\r\n\r\n /** 今日成本汇总 */\r\n get todaySummary(): DailyCostSummary {\r\n this.#ensureTodayBucket();\r\n return { ...this.#todaySummary };\r\n }\r\n\r\n /** 今日总费用(元) */\r\n get todayTotalCost(): number {\r\n this.#ensureTodayBucket();\r\n return this.#todaySummary.totalCost;\r\n }\r\n\r\n /** 今日 API 调用次数 */\r\n get todayCallCount(): number {\r\n this.#ensureTodayBucket();\r\n return this.#todaySummary.totalCalls;\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 预算检查\r\n // -----------------------------------------------------------------------\r\n\r\n /** 是否已超出预算 */\r\n get isBudgetExceeded(): boolean {\r\n if (this.#budgetLimit > 0 && this.todayTotalCost >= this.#budgetLimit) {\r\n return true;\r\n }\r\n if (this.#tokenBudgetLimit > 0) {\r\n const totalTokens =\r\n this.#todaySummary.totalPromptTokens +\r\n this.#todaySummary.totalCompletionTokens;\r\n if (totalTokens >= this.#tokenBudgetLimit) return true;\r\n }\r\n return false;\r\n }\r\n\r\n /** 剩余预算(元),无限制时返回 Infinity */\r\n get remainingBudget(): number {\r\n if (this.#budgetLimit <= 0) return Infinity;\r\n return Math.max(0, this.#budgetLimit - this.todayTotalCost);\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 持久化\r\n // -----------------------------------------------------------------------\r\n\r\n /**\r\n * 从磁盘加载历史成本数据。\r\n * 启动时调用,用于恢复今日和历史数据。\r\n * 副作用:确保成本目录存在(首次运行时创建 ~/.dskcode/costs)。\r\n */\r\n async load(): Promise<void> {\r\n await this.#ensureCostDir();\r\n const store = await this.#loadStore();\r\n const today = getTodayStr();\r\n\r\n if (store.daily[today]) {\r\n this.#todaySummary = store.daily[today];\r\n this.#todayDate = today;\r\n }\r\n }\r\n\r\n /**\r\n * 将脏数据持久化到磁盘。\r\n * 会话结束/进程退出时主动调用一次即可。需要每日数据精确时也可在每次\r\n * record() 后调用。并发安全:靠 #flushInProgress 互斥。\r\n */\r\n async flush(): Promise<void> {\r\n if (!this.#dirty || this.#flushInProgress) return;\r\n this.#flushInProgress = true;\r\n this.#dirty = false;\r\n\r\n try {\r\n await this.#ensureCostDir();\r\n await this.#saveStore();\r\n } catch (err) {\r\n // 落盘失败:把 dirty 标记还回去,等下次重试\r\n this.#dirty = true;\r\n console.error(\"[CostTracker] flush 失败:\", err);\r\n } finally {\r\n this.#flushInProgress = false;\r\n }\r\n }\r\n\r\n /**\r\n * 查询指定日期范围的成本汇总。\r\n * @param startDate 起始日期(YYYY-MM-DD),包含\r\n * @param endDate 截止日期(YYYY-MM-DD),包含,默认同 startDate\r\n */\r\n async queryRange(\r\n startDate: string,\r\n endDate?: string,\r\n ): Promise<DailyCostSummary[]> {\r\n const store = await this.#loadStore();\r\n const end = endDate ?? startDate;\r\n const result: DailyCostSummary[] = [];\r\n\r\n for (const [date, summary] of Object.entries(store.daily)) {\r\n if (date >= startDate && date <= end) {\r\n result.push(summary);\r\n }\r\n }\r\n\r\n result.sort((a, b) => a.date.localeCompare(b.date));\r\n return result;\r\n }\r\n\r\n /** 重置会话(开始新会话,不影响日级累计) */\r\n resetSession(): void {\r\n this.#sessionId = generateSessionId();\r\n this.#sessionStartedAt = new Date().toISOString();\r\n this.#sessionRecords.length = 0;\r\n }\r\n\r\n /**\r\n * 销毁实例:从全局实例表移除并 flush。\r\n * 一般用不到,仅在测试或显式生命周期管理时使用。\r\n */\r\n async dispose(): Promise<void> {\r\n CostTracker.#instances.delete(this);\r\n await this.flush();\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 内部方法\r\n // -----------------------------------------------------------------------\r\n\r\n /** 确保今日的统计桶存在(处理跨日情况) */\r\n #ensureTodayBucket(): void {\r\n const today = getTodayStr();\r\n if (today !== this.#todayDate) {\r\n // 日期变更:将旧日数据持久化后切换到新日\r\n this.#todayDate = today;\r\n this.#todaySummary = createEmptyDailySummary(today);\r\n this.#dirty = true;\r\n }\r\n }\r\n\r\n /** 将一条记录累加到日级汇总 */\r\n #addToDaily(record: CostRecord): void {\r\n this.#todaySummary.totalPromptTokens += record.usage.promptTokens;\r\n this.#todaySummary.totalCompletionTokens += record.usage.completionTokens;\r\n this.#todaySummary.totalCachedTokens += record.usage.cachedPromptTokens ?? 0;\r\n this.#todaySummary.totalCost += record.cost.totalCost;\r\n this.#todaySummary.totalCalls += 1;\r\n\r\n // 按模型分类累加\r\n const modelKey = record.model;\r\n if (!this.#todaySummary.byModel[modelKey]) {\r\n this.#todaySummary.byModel[modelKey] = {\r\n model: record.model,\r\n totalPromptTokens: 0,\r\n totalCompletionTokens: 0,\r\n totalCachedTokens: 0,\r\n totalCost: 0,\r\n totalCalls: 0,\r\n };\r\n }\r\n\r\n const modelSummary = this.#todaySummary.byModel[modelKey];\r\n modelSummary.totalPromptTokens += record.usage.promptTokens;\r\n modelSummary.totalCompletionTokens += record.usage.completionTokens;\r\n modelSummary.totalCachedTokens += record.usage.cachedPromptTokens ?? 0;\r\n modelSummary.totalCost += record.cost.totalCost;\r\n modelSummary.totalCalls += 1;\r\n }\r\n\r\n /** 预算超限检查 */\r\n #checkBudget(): void {\r\n if (!this.isBudgetExceeded) return;\r\n this.#onBudgetExceeded?.(this);\r\n }\r\n\r\n /** 从磁盘加载成本存储 */\r\n async #loadStore(): Promise<CostStore> {\r\n const filePath = join(this.#costDir, \"history.json\");\r\n try {\r\n const raw = await readFile(filePath, \"utf-8\");\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n return JSON.parse(raw) as unknown as CostStore;\r\n } catch {\r\n return { version: 1, daily: {} };\r\n }\r\n }\r\n\r\n /** 确保成本目录存在(首次运行时创建 ~/.dskcode/costs) */\r\n async #ensureCostDir(): Promise<void> {\r\n await mkdir(this.#costDir, { recursive: true });\r\n }\r\n\r\n /** 保存成本存储到磁盘 */\r\n async #saveStore(): Promise<void> {\r\n const store = await this.#loadStore();\r\n\r\n // 合并今日数据到存储中\r\n store.daily[this.#todayDate] = this.#todaySummary;\r\n\r\n // 保留最近 90 天的数据(避免文件过大)\r\n const cutoffDate = getDateNDaysAgo(90);\r\n const datesToRemove = Object.keys(store.daily).filter(\r\n (date) => date < cutoffDate,\r\n );\r\n for (const date of datesToRemove) {\r\n // eslint-disable-next-line @typescript-eslint/no-dynamic-delete\r\n delete store.daily[date];\r\n }\r\n\r\n const filePath = join(this.#costDir, \"history.json\");\r\n await writeFile(filePath, JSON.stringify(store, null, 2), \"utf-8\");\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 工具函数\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 生成会话 ID:时间戳 + 随机后缀 */\r\nfunction generateSessionId(): string {\r\n const ts = Date.now().toString(36);\r\n const rand = Math.random().toString(36).slice(2, 8);\r\n return `${ts}-${rand}`;\r\n}\r\n\r\n/** 获取今日日期字符串(YYYY-MM-DD) */\r\nfunction getTodayStr(): string {\r\n return new Date().toISOString().slice(0, 10);\r\n}\r\n\r\n/** 获取 N 天前的日期字符串 */\r\nfunction getDateNDaysAgo(n: number): string {\r\n const d = new Date();\r\n d.setDate(d.getDate() - n);\r\n return d.toISOString().slice(0, 10);\r\n}\r\n\r\n/** 创建空的日级成本汇总 */\r\nfunction createEmptyDailySummary(date: string): DailyCostSummary {\r\n return {\r\n date,\r\n totalPromptTokens: 0,\r\n totalCompletionTokens: 0,\r\n totalCachedTokens: 0,\r\n totalCost: 0,\r\n totalCalls: 0,\r\n byModel: {},\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 格式化工具\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 格式化费用金额(保留4位小数) */\r\nexport function formatMoney(yuan: number): string {\r\n return `¥${yuan.toFixed(4)}`;\r\n}\r\n\r\n/** 格式化 token 数量(带千分位) */\r\nexport function formatTokens(tokens: number): string {\r\n return tokens.toLocaleString(\"en-US\");\r\n}\r\n\r\n/** 格式化缓存命中率百分比 */\r\nexport function formatCacheHitRate(cached: number, total: number): string {\r\n if (total === 0) return \"0.0%\";\r\n return `${((cached / total) * 100).toFixed(1)}%`;\r\n}\r\n\r\n/** 生成今日消耗报告的终端友好文本 */\r\nexport function formatTodayReport(summary: DailyCostSummary): string {\r\n const lines: string[] = [];\r\n\r\n lines.push(`📊 今日消耗报告 (${summary.date})`);\r\n lines.push(\"─\".repeat(40));\r\n lines.push(` 💰 总费用: ${formatMoney(summary.totalCost)}`);\r\n lines.push(` 📞 调用次数: ${String(summary.totalCalls)} 次`);\r\n lines.push(` 📥 输入 Token: ${formatTokens(summary.totalPromptTokens)}`);\r\n lines.push(` 📤 输出 Token: ${formatTokens(summary.totalCompletionTokens)}`);\r\n lines.push(\r\n ` 🗄️ 缓存命中: ${formatTokens(summary.totalCachedTokens)} (${formatCacheHitRate(summary.totalCachedTokens, summary.totalPromptTokens)})`,\r\n );\r\n lines.push(\"\");\r\n\r\n // 按模型分类明细\r\n const models = Object.values(summary.byModel);\r\n if (models.length > 0) {\r\n lines.push(\"📈 按模型分类:\");\r\n for (const m of models) {\r\n lines.push(` ─ ${m.model} ─`);\r\n lines.push(` 费用: ${formatMoney(m.totalCost)} | 调用: ${String(m.totalCalls)} 次`);\r\n lines.push(\r\n ` 输入: ${formatTokens(m.totalPromptTokens)} | 输出: ${formatTokens(m.totalCompletionTokens)}`,\r\n );\r\n lines.push(\r\n ` 缓存命中: ${formatTokens(m.totalCachedTokens)} (${formatCacheHitRate(m.totalCachedTokens, m.totalPromptTokens)})`,\r\n );\r\n }\r\n }\r\n\r\n return lines.join(\"\\n\");\r\n}\r\n\r\n/** 生成会话成本摘要的单行文本 */\r\nexport function formatSessionCostLine(summary: SessionCostSummary): string {\r\n const cacheRate = formatCacheHitRate(\r\n summary.totalCachedTokens,\r\n summary.totalPromptTokens,\r\n );\r\n return (\r\n `💰 ¥${summary.totalCost.toFixed(4)} | ` +\r\n `📥${formatTokens(summary.totalPromptTokens)} ` +\r\n `📤${formatTokens(summary.totalCompletionTokens)} ` +\r\n `🗄️${cacheRate} | ` +\r\n `📞${String(summary.records.length)}次`\r\n );\r\n}\r\n\r\n/** 生成单次调用的成本单行文本 */\r\nexport function formatCallCostLine(record: CostRecord): string {\r\n const cacheRate = formatCacheHitRate(\r\n record.usage.cachedPromptTokens ?? 0,\r\n record.usage.promptTokens,\r\n );\r\n return (\r\n `≈¥${record.cost.totalCost.toFixed(4)} ` +\r\n `(📥${String(record.usage.promptTokens)} 📤${String(record.usage.completionTokens)} 🗄️${cacheRate})`\r\n );\r\n}","// ---------------------------------------------------------------------------\r\n// 工具调用块组件 — 在终端中渲染单个工具调用\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\nimport type { ProviderToolCall } from \"../provider/index.js\";\r\n\r\ninterface ToolCallBlockProps {\r\n call: ProviderToolCall;\r\n}\r\n\r\n/**\r\n * 格式化工具调用参数的摘要。\r\n * 短参数直接展示,长参数截断。\r\n */\r\nfunction formatArgsSummary(args: string): string {\r\n try {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const parsed = JSON.parse(args) as unknown as Record<string, unknown>;\r\n const lines = Object.entries(parsed).map(([key, value]) => {\r\n const val = String(value);\r\n const truncated = val.length > 80 ? val.slice(0, 77) + \"...\" : val;\r\n return ` ${key}: ${truncated}`;\r\n });\r\n return lines.join(\"\\n\");\r\n } catch {\r\n // 无法解析的 JSON,直接截断展示\r\n const truncated = args.length > 120 ? args.slice(0, 117) + \"...\" : args;\r\n return ` ${truncated}`;\r\n }\r\n}\r\n\r\n/**\r\n * 渲染单个工具调用信息块。\r\n *\r\n * 显示效果:\r\n * 📦 read_file ─────────────────\r\n * 📂 path: src/provider/types.ts\r\n */\r\nexport function ToolCallBlock({ call }: ToolCallBlockProps) {\r\n const argsDisplay = formatArgsSummary(call.arguments);\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginLeft={3} marginTop={1}>\r\n <Box>\r\n <Text dimColor>📦 {call.name}</Text>\r\n <Text dimColor> {\"─\".repeat(Math.max(1, 30 - call.name.length))}</Text>\r\n </Box>\r\n <Box flexDirection=\"column\">\r\n <Text dimColor>{argsDisplay}</Text>\r\n </Box>\r\n </Box>\r\n );\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 消息组装 — 将历史消息与系统提示词合并,支持上下文裁剪\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type { ChatMessage, UsageInfo } from \"../provider/index.js\";\r\nimport type { ModelId } from \"../provider/types.js\";\r\nimport { getModelMeta, estimateTokens } from \"../provider/models.js\";\r\n\r\n/** 上下文裁剪策略 */\r\nexport interface TrimOptions {\r\n /** 模型标识,用于获取上下文窗口大小 */\r\n model: ModelId;\r\n /** 为模型输出预留的 token 数 */\r\n reservedForOutput: number;\r\n /** 系统提示词(始终保留) */\r\n systemPrompt: string;\r\n /** 最近 N 轮对话强制保留(不参与裁剪) */\r\n preserveRecentRounds: number;\r\n}\r\n\r\n/**\r\n * 估算消息的 token 数。\r\n * 简单估算:content 长度 × 0.3 + 角色开销 ~10 tokens\r\n */\r\nfunction estimateMessageTokens(msg: ChatMessage): number {\r\n let text = msg.content;\r\n // 工具调用的参数也算\r\n if (msg.toolCalls) {\r\n for (const tc of msg.toolCalls) {\r\n text += tc.name + tc.arguments;\r\n }\r\n }\r\n return estimateTokens(text) + 10;\r\n}\r\n\r\n/**\r\n * 将消息历史划分为不可分割的「回合」单元。\r\n *\r\n * 一个回合的定义:\r\n * - 以一条 user 消息开头\r\n * - 后续连续的 assistant / tool 附属属于该回合,直到下一条 user 出现\r\n * - 特别地,assistant 携带 tool_calls 时,其后紧跟的若干 tool 消息必须与该 assistant 绑定\r\n * (DeepSeek/OpenAI 兼容 API 要求 tool_calls 与对应的 tool 结果消息一一配对,\r\n * 否则返回 400 错误)。因此裁剪时不能把这对拆开。\r\n *\r\n * @returns 回合数组;每个回合是一个非空消息数组,原顺序保留\r\n */\r\nfunction groupIntoTurns(messages: ChatMessage[]): ChatMessage[][] {\r\n const turns: ChatMessage[][] = [];\r\n let current: ChatMessage[] | null = null;\r\n\r\n for (const msg of messages) {\r\n if (msg.role === \"user\") {\r\n // 新回合开始\r\n if (current) turns.push(current);\r\n current = [msg];\r\n } else {\r\n // assistant / tool:归属当前回合;若无当前回合(历史以非 user 开头)\r\n // 则单独为保证不丢失而新建一个虚拟回合\r\n if (!current) current = [];\r\n current.push(msg);\r\n }\r\n }\r\n if (current && current.length > 0) turns.push(current);\r\n return turns;\r\n}\r\n\r\n/**\r\n * 估算一个回合内所有消息的总 token 数。\r\n */\r\nfunction estimateTurnTokens(turn: ChatMessage[]): number {\r\n let sum = 0;\r\n for (const msg of turn) sum += estimateMessageTokens(msg);\r\n return sum;\r\n}\r\n\r\n/**\r\n * 裁剪消息历史以适应上下文窗口,保证工具调用回合不被拆散。\r\n *\r\n * 策略:\r\n * 1. 系统提示词始终保留,不计入裁剪范围\r\n * 2. 将历史按「回合」分组(user 起头 → 直到下一个 user 之前的所有 assistant/tool)\r\n * 3. 最近的 `preserveRecentRounds` 个回合整体强制保留\r\n * 4. 若保留区超限,从最前的回合整组丢弃,直到不超限\r\n * 5. 否则,从最近向 earliest 后填更早的整回合,装不下就停\r\n *\r\n * 关键不变量:任何含 toolCalls 的 assistant 与其后续的 tool 结果消息\r\n * 始终在同一回合里,裁剪不会拆开它们。\r\n *\r\n * @param messages 原始消息历史(不含 system prompt)\r\n * @param opts 裁剪选项\r\n * @returns [裁剪后的消息数组, 是否发生了裁剪]\r\n */\r\nexport function trimMessages(\r\n messages: ChatMessage[],\r\n opts: TrimOptions,\r\n): [ChatMessage[], boolean] {\r\n const meta = getModelMeta(opts.model);\r\n const maxInputTokens = meta.contextWindow - opts.reservedForOutput;\r\n\r\n // 系统提示词 token 估算\r\n const systemTokens = estimateTokens(opts.systemPrompt);\r\n let remaining = maxInputTokens - systemTokens;\r\n\r\n const turns = groupIntoTurns(messages);\r\n\r\n // 保留最近 N 个回合\r\n const preservedTurns: ChatMessage[][] = [];\r\n let roundsPreserved = 0;\r\n for (let i = turns.length - 1; i >= 0 && roundsPreserved < opts.preserveRecentRounds; i--) {\r\n preservedTurns.unshift(turns[i]!);\r\n roundsPreserved++;\r\n }\r\n\r\n const preserved: ChatMessage[] = preservedTurns.flat();\r\n for (const msg of preserved) {\r\n remaining -= estimateMessageTokens(msg);\r\n }\r\n\r\n // 保留区本身超限:从最前的回合整组丢\r\n if (remaining < 0) {\r\n while (preservedTurns.length > 1 && remaining < 0) {\r\n const dropped = preservedTurns.shift()!;\r\n remaining += estimateTurnTokens(dropped);\r\n }\r\n return [preservedTurns.flat(), true];\r\n }\r\n\r\n // 向 earliest 填更早的回合\r\n const olderTurns = turns.slice(0, turns.length - preservedTurns.length);\r\n const keptTurns: ChatMessage[][] = [];\r\n\r\n for (let i = olderTurns.length - 1; i >= 0; i--) {\r\n const cost = estimateTurnTokens(olderTurns[i]!);\r\n if (remaining - cost < 0) break;\r\n remaining -= cost;\r\n keptTurns.unshift(olderTurns[i]!);\r\n }\r\n\r\n const result = [...keptTurns, ...preservedTurns].flat();\r\n const trimmed = result.length < messages.length;\r\n return [result, trimmed];\r\n}\r\n\r\n/**\r\n * 构建完整的 API 请求消息数组。\r\n *\r\n * 在消息历史前面插入 system prompt,返回可直接传给 provider.chat() 的消息数组。\r\n */\r\nexport function buildApiMessages(\r\n systemPrompt: string,\r\n history: ChatMessage[],\r\n): ChatMessage[] {\r\n return [\r\n { role: \"system\", content: systemPrompt },\r\n ...history,\r\n ];\r\n}\r\n\r\n/**\r\n * 将 Token 使用量格式化为人类可读的摘要字符串。\r\n */\r\nexport function formatUsageSummary(usage: UsageInfo): string {\r\n const prompt = usage.promptTokens.toLocaleString();\r\n const completion = usage.completionTokens.toLocaleString();\r\n const total = (usage.promptTokens + usage.completionTokens).toLocaleString();\r\n\r\n let summary = `📥 ${prompt} + 📤 ${completion} = 📦 ${total} tokens`;\r\n\r\n if (usage.cachedPromptTokens && usage.cachedPromptTokens > 0) {\r\n const cacheRate = ((usage.cachedPromptTokens / usage.promptTokens) * 100).toFixed(1);\r\n summary += ` │ 🗄️ 缓存命中 ${cacheRate}%`;\r\n }\r\n\r\n return summary;\r\n}","// ---------------------------------------------------------------------------\r\n// HighlightedText — AI 回复文本高亮渲染\r\n//\r\n// 支持四种语法:\r\n// `` `code` `` → 天蓝色(代码标识符、文件路径)\r\n// `**bold**` → 紫色(强调内容,如\"第 12 行\")\r\n// ```...``` 代码块 → 独立块渲染,diff 内容自动绿/红着色\r\n// | col | col | 表格 → 框线表格,自动对齐\r\n// 流式传输中未闭合的标记自动降级为纯文本,不会闪烁。\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\nimport type { ReactNode } from \"react\";\r\nimport { detectTermWidth, layoutTable } from \"./table-layout.js\";\r\n\r\n/** 解析后的文本段类型 */\r\ntype SegmentType = \"plain\" | \"code\" | \"bold\" | \"code_block\" | \"table\";\r\n\r\ninterface Segment {\r\n text: string;\r\n type: SegmentType;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 第 0 遍:提取三反引号代码块\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 在全文中查找 ```...``` 代码块,将其标记为 \"code_block\" 类型。\r\n * 代码块之外的内容保持 \"plain\" 类型,后续交给 bold/code 解析。\r\n *\r\n * 未闭合的三反引号降级为纯文本(流式传输安全)。\r\n */\r\nfunction parseCodeBlocks(text: string): Segment[] {\r\n const segments: Segment[] = [];\r\n let current = 0;\r\n\r\n while (current < text.length) {\r\n const openIdx = text.indexOf(\"```\", current);\r\n\r\n if (openIdx === -1) {\r\n segments.push({ text: text.slice(current), type: \"plain\" });\r\n break;\r\n }\r\n\r\n if (openIdx > current) {\r\n segments.push({ text: text.slice(current, openIdx), type: \"plain\" });\r\n }\r\n\r\n const closeIdx = text.indexOf(\"```\", openIdx + 3);\r\n\r\n if (closeIdx === -1) {\r\n // 未闭合 → 降级为纯文本\r\n segments.push({ text: text.slice(openIdx), type: \"plain\" });\r\n break;\r\n }\r\n\r\n // 提取代码块内容(含开头的语言标识行)\r\n const codeContent = text.slice(openIdx + 3, closeIdx);\r\n segments.push({ text: codeContent, type: \"code_block\" });\r\n current = closeIdx + 3;\r\n }\r\n\r\n return segments;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 第 0.5 遍:Markdown 表格检测(在 plain 段内)\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 判断一行是不是表格行(以 | 开头并以 | 结尾) */\r\nfunction isTableRow(line: string): boolean {\r\n const t = line.trim();\r\n return /^\\|.+\\|$/.test(t);\r\n}\r\n\r\n/** 判断一行是不是表格分隔行(|:---:|:---:| 等) */\r\nfunction isTableSepRow(line: string): boolean {\r\n const t = line.trim();\r\n // 字符类需包含 | 以匹配多列分隔行,如 |---|---|\r\n return /^\\|[-: |]+\\|$/.test(t) && t.includes(\"-\");\r\n}\r\n\r\n/**\r\n * 在 plain 文本中检测 Markdown 表格块(连续的 `|...|` 行),标记为 \"table\" 类型。\r\n *\r\n * 表格判定条件:\r\n * 1. 开头行是表格行\r\n * 2. 第二行是分隔行(含 `-`)\r\n * 3. 第三行也是表格行\r\n */\r\nfunction parseTables(text: string): Segment[] {\r\n const lines = text.split(\"\\n\");\r\n const result: Segment[] = [];\r\n let plainBuf = \"\";\r\n\r\n function flushPlain() {\r\n if (plainBuf) {\r\n result.push({ text: plainBuf, type: \"plain\" });\r\n plainBuf = \"\";\r\n }\r\n }\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n const line = lines[i] ?? \"\";\r\n\r\n if (\r\n isTableRow(line) &&\r\n i + 1 < lines.length &&\r\n isTableSepRow(lines[i + 1] ?? \"\") &&\r\n i + 2 < lines.length &&\r\n isTableRow(lines[i + 2] ?? \"\")\r\n ) {\r\n flushPlain();\r\n\r\n // 收集所有连续的表格行(含分隔行)\r\n const tableLineList: string[] = [];\r\n let j = i;\r\n while (j < lines.length && isTableRow(lines[j] ?? \"\")) {\r\n tableLineList.push(lines[j]!);\r\n j++;\r\n }\r\n\r\n result.push({ text: tableLineList.join(\"\\n\"), type: \"table\" });\r\n i = j - 1;\r\n } else {\r\n if (plainBuf) plainBuf += \"\\n\" + line;\r\n else plainBuf = line;\r\n }\r\n }\r\n\r\n flushPlain();\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 第 1 遍:**bold** 解析(与之前相同)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 按 `**...**` 对拆分为段。\r\n * 未闭合的 `**`(流式传输中)降级为纯文本。\r\n */\r\nfunction parseBoldPairs(text: string): Segment[] {\r\n const segments: Segment[] = [];\r\n let current = 0;\r\n\r\n while (current < text.length) {\r\n const openIdx = text.indexOf(\"**\", current);\r\n\r\n if (openIdx === -1) {\r\n segments.push({ text: text.slice(current), type: \"plain\" });\r\n break;\r\n }\r\n\r\n // ** 前的纯文本\r\n if (openIdx > current) {\r\n segments.push({ text: text.slice(current, openIdx), type: \"plain\" });\r\n }\r\n\r\n const closeIdx = text.indexOf(\"**\", openIdx + 2);\r\n\r\n // 未闭合 → 降级为纯文本\r\n if (closeIdx === -1) {\r\n segments.push({ text: text.slice(openIdx), type: \"plain\" });\r\n break;\r\n }\r\n\r\n // 成对 **...** 内的内容 → bold\r\n segments.push({ text: text.slice(openIdx + 2, closeIdx), type: \"bold\" });\r\n current = closeIdx + 2;\r\n }\r\n\r\n return segments;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 第 2 遍:`code` 解析(处理 plain 和 bold 段内的单反引号对)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 在纯文本中查找成对的单反引号,返回 [plain, code, plain, ...] 段序列。\r\n *\r\n * - 只处理 `...` 单反引号对;多反引号序列降级为纯文本。\r\n * - 未闭合的反引号降级为纯文本。\r\n */\r\nfunction parseInlineCode(text: string): Segment[] {\r\n const result: Segment[] = [];\r\n let current = 0;\r\n\r\n while (current < text.length) {\r\n const openIdx = text.indexOf(\"`\", current);\r\n\r\n if (openIdx === -1) {\r\n result.push({ text: text.slice(current), type: \"plain\" });\r\n break;\r\n }\r\n\r\n if (openIdx > current) {\r\n result.push({ text: text.slice(current, openIdx), type: \"plain\" });\r\n }\r\n\r\n // 计算连续反引号的数量\r\n let runLength = 1;\r\n while (\r\n openIdx + runLength < text.length &&\r\n text[openIdx + runLength] === \"`\"\r\n ) {\r\n runLength++;\r\n }\r\n\r\n if (runLength === 1) {\r\n // 单反引号对 → code 段\r\n const closeIdx = text.indexOf(\"`\", openIdx + 1);\r\n\r\n if (closeIdx === -1) {\r\n result.push({ text: text.slice(openIdx), type: \"plain\" });\r\n break;\r\n }\r\n\r\n result.push({ text: text.slice(openIdx + 1, closeIdx), type: \"code\" });\r\n current = closeIdx + 1;\r\n } else {\r\n // 多反引号序列(```、`` 等)保持原样\r\n result.push({\r\n text: text.slice(openIdx, openIdx + runLength),\r\n type: \"plain\",\r\n });\r\n current = openIdx + runLength;\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * 在 plain 和 bold 段内按 `` `...` `` 对拆分为 code 段。\r\n *\r\n * - **plain** 段内的 code → 标蓝,剩余部分保持默认色\r\n * - **bold** 段内的 code → 标蓝,剩余部分保持紫色\r\n * - 多反引号序列(```` ```code``` ````、`` ``code`` `` 等)统一降级为纯文本\r\n * - code_block / table 段直接透传\r\n */\r\nfunction parseCodePairs(segments: Segment[]): Segment[] {\r\n const result: Segment[] = [];\r\n\r\n for (const seg of segments) {\r\n if (seg.type === \"code_block\" || seg.type === \"table\") {\r\n result.push(seg);\r\n continue;\r\n }\r\n\r\n // 对 plain 和 bold 的内容都做反引号解析\r\n const parts = parseInlineCode(seg.text);\r\n\r\n for (const part of parts) {\r\n if (part.type === \"code\") {\r\n // 反引号内容始终标蓝\r\n result.push(part);\r\n } else if (seg.type === \"bold\") {\r\n // bold 段内非反引号部分 → 保持紫色\r\n result.push({ text: part.text, type: \"bold\" });\r\n } else {\r\n // plain 段内非反引号部分 → 保持默认色\r\n result.push(part);\r\n }\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 代码块渲染\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 添加行绿色 */\r\nconst DIFF_ADD_COLOR = \"#22c55e\";\r\n/** 删除行红色 */\r\nconst DIFF_DEL_COLOR = \"#ef4444\";\r\n/** Hunk 头青色 */\r\nconst DIFF_HUNK_COLOR = \"#00cccc\";\r\n\r\n/**\r\n * 渲染三反引号代码块的内容,对 diff 风格的内容行着色。\r\n *\r\n * 代码块内的第一行若为纯文本(非 `+`/`-`/`@@`/空格开头),\r\n * 视为语言标识(如 \"diff\"、\"python\"),用灰色标出。\r\n */\r\nfunction CodeBlockRenderer({ code }: { code: string }): ReactNode {\r\n const lines = code.split(\"\\n\");\r\n\r\n // 判断第一行是否为语言标识行\r\n const firstLine = lines[0] ?? \"\";\r\n const hasLangLine =\r\n firstLine.trim().length > 0 &&\r\n !firstLine.startsWith(\"+\") &&\r\n !firstLine.startsWith(\"-\") &&\r\n !firstLine.startsWith(\"@@\") &&\r\n !firstLine.startsWith(\" \");\r\n\r\n const lang = hasLangLine ? firstLine : undefined;\r\n const codeLines = hasLangLine ? lines.slice(1) : lines;\r\n\r\n // 空内容\r\n if (codeLines.length === 0) {\r\n return hasLangLine ? (\r\n <Box marginLeft={2}>\r\n <Text color=\"#888888\">┌ {lang}</Text>\r\n </Box>\r\n ) : null;\r\n }\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginLeft={2} marginTop={1}>\r\n {lang && <Text color=\"#888888\">┌ {lang}</Text>}\r\n {codeLines.map((line, i) => {\r\n if (line.startsWith(\"+\")) {\r\n return (\r\n <Text key={i} color={DIFF_ADD_COLOR}>\r\n {line}\r\n </Text>\r\n );\r\n }\r\n if (line.startsWith(\"-\")) {\r\n return (\r\n <Text key={i} color={DIFF_DEL_COLOR}>\r\n {line}\r\n </Text>\r\n );\r\n }\r\n if (line.startsWith(\"@@\")) {\r\n return (\r\n <Text key={i} color={DIFF_HUNK_COLOR}>\r\n {line}\r\n </Text>\r\n );\r\n }\r\n // 上下文行或非 diff 代码行\r\n return <Text key={i}>{line}</Text>;\r\n })}\r\n </Box>\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 表格渲染(计算逻辑已抽到 table-layout.ts,本文件仅负责把结果印到 Ink)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 渲染 Markdown 表格为终端框线表格。\r\n *\r\n * 输出示例(终端宽度足够时):\r\n * ┌──────────┬──────────────────────────┬──────────────────────────┐\r\n * │ 位置 │ 原内容 │ 新内容 │\r\n * ├──────────┼──────────────────────────┼──────────────────────────┤\r\n * │ 第 12 行 │ interface FileDiffs { │ interface Diffs { │\r\n * └──────────┴──────────────────────────┴──────────────────────────┘\r\n *\r\n * 行为:\r\n * - 列宽跟随终端宽度自适应(不再硬编码 90)\r\n * - 单 cell 超长时按视觉宽度自动换行;表格行高 = 该行最大 wrap 行数\r\n * - 内容不会被截断丢失(不像之前的 cell.slice)\r\n * - 终端宽度未知时使用 80 列兜底\r\n */\r\nfunction TableRenderer({ text }: { text: string }): ReactNode {\r\n const termWidth = detectTermWidth();\r\n const layout = layoutTable(text, { termWidth });\r\n\r\n // 无效输入(无分隔行)→ 降级为纯文本\r\n if (layout.colWidths.length === 0) {\r\n return <Text>{text}</Text>;\r\n }\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n {layout.lines.map((line, i) => {\r\n // 框线(top / mid / bot)用灰色;数据行用默认色\r\n const isFirst = i === 0;\r\n const isLast = i === layout.lines.length - 1;\r\n const isMid = !isFirst && !isLast && line.startsWith(\"├\");\r\n const color = isFirst || isLast || isMid ? \"#888888\" : undefined;\r\n return (\r\n <Text key={i} color={color}>{line}</Text>\r\n );\r\n })}\r\n </Box>\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Emoji 检测 — 跳过图标字符的颜色包裹\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 匹配 emoji 相关 code point 的正则 */\r\nconst 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;\r\n\r\n/**\r\n * 检测一个 grapheme cluster 是否属于 emoji 或图标字符。\r\n * 此类字符在 bold 渲染中应跳过颜色包裹,避免终端渲染异常。\r\n */\r\nfunction isEmojiCluster(text: string): boolean {\r\n return EMOJI_CP_RE.test(text);\r\n}\r\n\r\n/**\r\n * 将文本按 grapheme cluster 分割,标记每个 cluster 是否为 emoji。\r\n * 降级策略:若 Intl.Segmenter 不可用,将整个文本视为纯文本。\r\n */\r\nfunction splitEmojiClusters(\r\n text: string,\r\n): Array<{ text: string; isEmoji: boolean }> {\r\n try {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const segmenter = new (Intl as any).Segmenter(\"en\", {\r\n granularity: \"grapheme\",\r\n });\r\n const segments = [...segmenter.segment(text)];\r\n return segments.map((s) => ({\r\n text: s.segment,\r\n isEmoji: isEmojiCluster(s.segment),\r\n }));\r\n } catch {\r\n // 降级:整个文本作为一个纯文本块\r\n return [{ text, isEmoji: false }];\r\n }\r\n}\r\n\r\n/**\r\n * 渲染 bold(紫色)文本,对 emoji 字符跳过颜色包裹。\r\n */\r\nfunction renderBoldText(text: string, key: number | string): ReactNode {\r\n const clusters = splitEmojiClusters(text);\r\n\r\n // 不含 emoji — 简单路径(和之前一致)\r\n if (clusters.length === 1 && !clusters[0]!.isEmoji) {\r\n return (\r\n <Text key={key} color={BOLD_COLOR}>\r\n {text}\r\n </Text>\r\n );\r\n }\r\n\r\n // 合并相邻的同类 cluster 减少嵌套\r\n const groups: Array<{ text: string; isEmoji: boolean }> = [];\r\n for (const c of clusters) {\r\n const last = groups[groups.length - 1];\r\n if (last && last.isEmoji === c.isEmoji) {\r\n last.text += c.text;\r\n } else {\r\n groups.push({ text: c.text, isEmoji: c.isEmoji });\r\n }\r\n }\r\n\r\n return (\r\n <Text key={key}>\r\n {groups.map((g, i) =>\r\n g.isEmoji ? (\r\n g.text\r\n ) : (\r\n <Text key={i} color={BOLD_COLOR}>\r\n {g.text}\r\n </Text>\r\n ),\r\n )}\r\n </Text>\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 高亮颜色常量\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 行内代码高亮色 — 天蓝 */\r\nconst CODE_COLOR = \"#00BFFF\";\r\n/** 加粗强调色 — 紫色 */\r\nconst BOLD_COLOR = \"#A855F7\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 主组件\r\n// ---------------------------------------------------------------------------\r\n\r\ninterface HighlightedTextProps {\r\n children: string;\r\n}\r\n\r\n/**\r\n * 渲染 AI 回复文本,支持四种标记高亮:\r\n * - `` `code` `` → 天蓝色\r\n * - `**bold**` → 紫色\r\n * - ```` ```diff ```` → 独立块,`+` 绿色 / `-` 红色 / `@@` 青色\r\n * - `| ... |` Markdown → 框线表格\r\n */\r\nexport function HighlightedText({ children: text }: HighlightedTextProps): ReactNode {\r\n // 第 0 遍:提取 ```...``` 代码块\r\n const codeBlockSegments = parseCodeBlocks(text);\r\n\r\n // 第 0.5 遍:在非代码块的 plain 段中检测 Markdown 表格\r\n const tableSegments: Segment[] = [];\r\n for (const seg of codeBlockSegments) {\r\n if (seg.type === \"code_block\") {\r\n tableSegments.push(seg);\r\n } else {\r\n tableSegments.push(...parseTables(seg.text));\r\n }\r\n }\r\n\r\n // 第 1 遍:在 inline plain 段内解析 **bold**\r\n const boldSegments: Segment[] = [];\r\n for (const seg of tableSegments) {\r\n if (seg.type === \"code_block\" || seg.type === \"table\") {\r\n boldSegments.push(seg);\r\n } else {\r\n boldSegments.push(...parseBoldPairs(seg.text));\r\n }\r\n }\r\n\r\n // 第 2 遍:在 inline plain 段内解析 `code`\r\n const segments = parseCodePairs(boldSegments);\r\n\r\n // 检查是否包含块级元素\r\n const hasBlock = segments.some(\r\n (s) => s.type === \"code_block\" || s.type === \"table\",\r\n );\r\n\r\n // ---- 无块级元素:纯内联渲染 ----\r\n if (!hasBlock) {\r\n const isSimple = segments.length === 1 && segments[0]!.type === \"plain\";\r\n if (isSimple) {\r\n return <Text wrap=\"wrap\">{text}</Text>;\r\n }\r\n\r\n return (\r\n <Text wrap=\"wrap\">\r\n {segments.map((seg, i) => {\r\n if (seg.type === \"code\") {\r\n return (\r\n <Text key={i} color={CODE_COLOR}>\r\n {seg.text}\r\n </Text>\r\n );\r\n }\r\n if (seg.type === \"bold\") {\r\n return renderBoldText(seg.text, i);\r\n }\r\n return seg.text;\r\n })}\r\n </Text>\r\n );\r\n }\r\n\r\n // ---- 有块级元素:分组渲染(inline 组 + 块级渲染) ----\r\n const rendered: ReactNode[] = [];\r\n let inlineGroup: Segment[] = [];\r\n\r\n function flushInline(groupIdx: number) {\r\n if (inlineGroup.length === 0) return;\r\n const isSimpleInline =\r\n inlineGroup.length === 1 && inlineGroup[0]!.type === \"plain\";\r\n if (isSimpleInline) {\r\n rendered.push(\r\n <Text key={groupIdx} wrap=\"wrap\">\r\n {inlineGroup[0]!.text}\r\n </Text>,\r\n );\r\n } else {\r\n rendered.push(\r\n <Text key={groupIdx} wrap=\"wrap\">\r\n {inlineGroup.map((seg, j) => {\r\n if (seg.type === \"code\") {\r\n return (\r\n <Text key={j} color={CODE_COLOR}>\r\n {seg.text}\r\n </Text>\r\n );\r\n }\r\n if (seg.type === \"bold\") {\r\n return renderBoldText(seg.text, j);\r\n }\r\n return seg.text;\r\n })}\r\n </Text>,\r\n );\r\n }\r\n inlineGroup = [];\r\n }\r\n\r\n for (const seg of segments) {\r\n if (seg.type === \"code_block\") {\r\n flushInline(rendered.length);\r\n rendered.push(\r\n <CodeBlockRenderer key={`b${rendered.length}`} code={seg.text} />,\r\n );\r\n } else if (seg.type === \"table\") {\r\n flushInline(rendered.length);\r\n rendered.push(\r\n <TableRenderer key={`t${rendered.length}`} text={seg.text} />,\r\n );\r\n } else {\r\n inlineGroup.push(seg);\r\n }\r\n }\r\n flushInline(rendered.length);\r\n\r\n return <Box flexDirection=\"column\">{rendered}</Box>;\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// Table layout — 纯计算模块(无 React/无 IO,方便单测)\r\n//\r\n// 设计目标:\r\n// - 终端宽度自适应:不再硬编码最大宽度,改为读取 stdout.columns\r\n// - 单 cell 超长时按视觉宽度换行(不截断),表格行高自适应\r\n// - 中文字符宽度补全(0x3000 系列 CJK 标点 / ZWJ / 全角空格等)\r\n// - 函数全 @pure,方便单测覆盖布局算法\r\n//\r\n// 函数注释规范见仓库根 AGENTS.md「函数注释规范」一节。\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 表格列最小宽度(视觉宽度) */\r\nexport const TABLE_MIN_COL_WIDTH = 3;\r\n\r\n/** 默认终端宽度(process.stdout.columns 不可用时的兜底) */\r\nexport const DEFAULT_TERM_WIDTH = 80;\r\n\r\n/** 表格在终端里左右各留的边距(视觉宽度),避免贴边 */\r\nexport const TABLE_MARGIN = 2;\r\n\r\n/** 对齐方式 */\r\nexport type Alignment = \"left\" | \"center\" | \"right\";\r\n\r\n/** 表格布局选项 */\r\nexport interface TableLayoutOptions {\r\n /** 终端可用宽度(含 margin 前的总宽),默认 80 */\r\n termWidth?: number;\r\n /** 表格本身左右各留多少列(默认 2),留 0 可贴边 */\r\n outerMargin?: number;\r\n}\r\n\r\n/** 表格布局结果:所有要按行打印的字符串(含框线) */\r\nexport interface TableLayout {\r\n /** 总行数(含 top/mid/bot 框线) */\r\n lines: string[];\r\n /** 计算出的列宽(视觉宽度,含 margin 不含 │) */\r\n colWidths: number[];\r\n /** 表格总视觉宽度 */\r\n totalWidth: number;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 视觉宽度\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 单个码点在终端里的显示宽度。\r\n *\r\n * CJK / 全角 / Emoji 算 2 格;其他算 1 格。\r\n *\r\n * @param cp — Unicode 码点\r\n * @returns 显示宽度\r\n */\r\nfunction cpWidth(cp: number): number {\r\n if (\r\n (cp >= 0x1100 && cp <= 0x115F) || // Hangul Jamo\r\n cp === 0x2329 || cp === 0x232A ||\r\n (cp >= 0x2E80 && cp <= 0xA4CF) || // CJK Radicals ~ Yi\r\n (cp >= 0xA960 && cp <= 0xA97F) || // Hangul Extended\r\n (cp >= 0xAC00 && cp <= 0xD7AF) || // Hangul Syllables\r\n (cp >= 0xF900 && cp <= 0xFAFF) || // CJK Compat\r\n (cp >= 0xFE10 && cp <= 0xFE1F) || // Vertical forms\r\n (cp >= 0xFF01 && cp <= 0xFF60) || // Fullwidth ASCII / 标点\r\n (cp >= 0xFFE0 && cp <= 0xFFE6) || // Fullwidth signs\r\n (cp >= 0x1F000 && cp <= 0x1FFFF) || // Emoji / Symbols\r\n (cp >= 0x3000 && cp <= 0x303F) || // CJK Symbols & Punctuation(含「、。·」等)\r\n (cp >= 0x20000 && cp <= 0x2FFFF) // CJK Ext B~F + 罕用区\r\n ) {\r\n return 2;\r\n }\r\n // 控制字符不算宽度\r\n if (cp < 0x20 || (cp >= 0x7F && cp < 0xA0)) return 0;\r\n return 1;\r\n}\r\n\r\n/**\r\n * 字符串在终端里的视觉宽度。\r\n * 跳过零宽连接符(ZWJ 0x200D)和变体选择符(FE0F~FE0F)——这些是组合 emoji 的辅助字符,不占格。\r\n *\r\n * @param text — 任意字符串\r\n * @returns 视觉宽度\r\n */\r\nexport function visualWidth(text: string): number {\r\n let w = 0;\r\n for (const ch of text) {\r\n const cp = ch.codePointAt(0);\r\n if (cp === undefined) continue;\r\n if (cp === 0x200D) continue; // ZWJ\r\n if (cp >= 0xFE00 && cp <= 0xFE0F) continue; // Variation selectors\r\n w += cpWidth(cp);\r\n }\r\n return w;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 字符串按视觉宽度切分\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 遍历字符串,产出「码点 + 视觉宽度增量」。用于 wrap / slice 的按视觉宽度操作。\r\n */\r\nfunction* iterateChars(text: string): Generator<{ ch: string; width: number }> {\r\n for (const ch of text) {\r\n const cp = ch.codePointAt(0);\r\n if (cp === undefined) continue;\r\n if (cp === 0x200D) continue;\r\n if (cp >= 0xFE00 && cp <= 0xFE0F) continue;\r\n yield { ch, width: cpWidth(cp) };\r\n }\r\n}\r\n\r\n/**\r\n * 在给定的视觉宽度限制内切分字符串为多行(不丢字符)。\r\n *\r\n * 策略:\r\n * - 在空格 / 零宽非断字点处优先换行;找不到则在任意字符处硬切\r\n * - 末行若仍超宽,逐字符截到 maxWidth\r\n *\r\n * @param text — 原文本(可含 CJK / 换行)\r\n * @param maxWidth — 每行最大视觉宽度\r\n * @returns 切分后的多行\r\n */\r\nexport function wrapByWidth(text: string, maxWidth: number): string[] {\r\n if (maxWidth <= 0) return [text];\r\n\r\n // 先按 \\n 切,再对每段做视觉宽度 wrap\r\n const out: string[] = [];\r\n for (const para of text.split(\"\\n\")) {\r\n if (para.length === 0) {\r\n out.push(\"\");\r\n continue;\r\n }\r\n const wrapped = wrapOneLine(para, maxWidth);\r\n for (const w of wrapped) out.push(w);\r\n }\r\n return out.length === 0 ? [\"\"] : out;\r\n}\r\n\r\nfunction wrapOneLine(text: string, maxWidth: number): string[] {\r\n if (visualWidth(text) <= maxWidth) return [text];\r\n\r\n const lines: string[] = [];\r\n let current = \"\";\r\n let currentW = 0;\r\n // 候选断点:在最近的「空格后」处换行(保留英文 / 拼音整词)\r\n let breakCandidate = -1; // current 中最后一个空格的位置\r\n let breakCandidateW = 0;\r\n\r\n for (const { ch, width } of iterateChars(text)) {\r\n if (ch === \" \" || ch === \"\\t\") {\r\n // 记录断点:若后续再加字符会超宽,就在这里切\r\n breakCandidate = current.length;\r\n breakCandidateW = currentW + width;\r\n current += ch;\r\n currentW += width;\r\n continue;\r\n }\r\n if (currentW + width > maxWidth) {\r\n // 需要换行\r\n if (breakCandidate > 0) {\r\n // 在上一个空格处切\r\n const split = current.slice(0, breakCandidate).replace(/[ \\t]+$/, \"\");\r\n lines.push(split);\r\n const rest = current.slice(breakCandidate).replace(/^[ \\t]+/, \"\");\r\n current = rest + ch;\r\n currentW = visualWidth(current);\r\n breakCandidate = -1;\r\n breakCandidateW = 0;\r\n } else {\r\n // 硬切:当前字符单独成行\r\n lines.push(current);\r\n current = ch;\r\n currentW = width;\r\n }\r\n } else {\r\n current += ch;\r\n currentW += width;\r\n }\r\n }\r\n if (current.length > 0) lines.push(current);\r\n return lines;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 表格布局\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 把单行单元格按列宽换行成等高的多行,返回每行最终显示的字符串数组。\r\n * 不足行用空格补齐。\r\n *\r\n * @param cells — 单元格内容\r\n * @param colWidths — 每列宽度(视觉宽度)\r\n * @param alignments — 每列对齐方式\r\n * @returns 与原 cells 等长的、可能含多行的字符串数组\r\n */\r\nfunction wrapRowCells(\r\n cells: string[],\r\n colWidths: number[],\r\n alignments: Alignment[],\r\n): string[][] {\r\n return cells.map((cell, ci) => {\r\n const w = colWidths[ci] ?? TABLE_MIN_COL_WIDTH;\r\n const lines = wrapByWidth(cell, w);\r\n return lines.map((l) => padToWidth(l, w, alignments[ci] ?? \"left\"));\r\n });\r\n}\r\n\r\n/**\r\n * 按视觉宽度和对齐方式把字符串填充到目标宽度。\r\n *\r\n * @param text — 原文本\r\n * @param targetWidth — 目标视觉宽度\r\n * @param align — 对齐方式\r\n * @returns 填充后的字符串\r\n */\r\nexport function padToWidth(\r\n text: string,\r\n targetWidth: number,\r\n align: Alignment = \"left\",\r\n): string {\r\n const vw = visualWidth(text);\r\n const delta = targetWidth - vw;\r\n if (delta <= 0) return text;\r\n const leftPad = align === \"right\" ? delta : align === \"center\" ? Math.floor(delta / 2) : 0;\r\n const rightPad = delta - leftPad;\r\n return \" \".repeat(leftPad) + text + \" \".repeat(rightPad);\r\n}\r\n\r\n/**\r\n * 解析 Markdown 表格一行为单元格数组。\r\n *\r\n * @param line — 类似 `| a | b |` 的行\r\n * @returns 单元格内容数组\r\n */\r\nexport function parseTableCells(line: string): string[] {\r\n const t = line.trim();\r\n if (!t.startsWith(\"|\") || !t.endsWith(\"|\")) return [];\r\n const inner = t.slice(1, t.length - 1);\r\n return inner.split(\"|\").map((c) => c.trim());\r\n}\r\n\r\n/**\r\n * 从分隔行(如 `| :--- | :---: | ---: |`)解析每列对齐方式。\r\n *\r\n * @param sepLine — 分隔行\r\n * @returns 每列的对齐方式数组\r\n */\r\nexport function parseAlignments(sepLine: string): Alignment[] {\r\n return parseTableCells(sepLine).map((c) => {\r\n const l = c.startsWith(\":\");\r\n const r = c.endsWith(\":\");\r\n if (l && r) return \"center\";\r\n if (r) return \"right\";\r\n return \"left\";\r\n });\r\n}\r\n\r\n/**\r\n * 把多行原表格内容(含表头 / 分隔行 / 数据行)布局成终端框线表格。\r\n *\r\n * 行为:\r\n * 1. 列宽优先用各列最长 cell 的视觉宽度\r\n * 2. 超出终端可用宽度 → 等比缩列宽,下限 TABLE_MIN_COL_WIDTH\r\n * 3. 单 cell 仍超过列宽 → 在列内按视觉宽度 wrap 成多行;表格行高 = 该行最大 wrap 行数\r\n * 4. 内容绝不截断(不像 slice)\r\n *\r\n * @param text — 表格原始多行文本(含分隔行)\r\n * @param options — 布局选项\r\n * @returns 布局结果(已包含 top / mid / bot 框线)\r\n */\r\nexport function layoutTable(text: string, options: TableLayoutOptions = {}): TableLayout {\r\n const termWidth = options.termWidth ?? DEFAULT_TERM_WIDTH;\r\n const outerMargin = options.outerMargin ?? 0; // 表格整体在终端中的左右边距,由调用方负责\r\n const usable = Math.max(termWidth - outerMargin, 20);\r\n\r\n const rawLines = text.split(\"\\n\").filter((l) => l.trim().length > 0);\r\n if (rawLines.length < 2) {\r\n return { lines: rawLines, colWidths: [], totalWidth: 0 };\r\n }\r\n\r\n // 找分隔行\r\n let sepIdx = -1;\r\n for (let k = 0; k < rawLines.length; k++) {\r\n const t = (rawLines[k] ?? \"\").trim();\r\n if (/^\\|[-: |]+\\|$/.test(t) && t.includes(\"-\")) {\r\n sepIdx = k;\r\n break;\r\n }\r\n }\r\n if (sepIdx === -1) {\r\n return { lines: rawLines, colWidths: [], totalWidth: 0 };\r\n }\r\n\r\n // 解析表头、对齐、数据行\r\n const headerCells = parseTableCells(rawLines.slice(0, sepIdx).join(\"\"));\r\n const alignments = parseAlignments(rawLines[sepIdx] ?? \"\");\r\n const dataRows = rawLines.slice(sepIdx + 1).map(parseTableCells);\r\n const colCount = headerCells.length;\r\n if (colCount === 0) {\r\n return { lines: rawLines, colWidths: [], totalWidth: 0 };\r\n }\r\n\r\n // 每列最大视觉宽度\r\n const colMaxWidths: number[] = [];\r\n for (let ci = 0; ci < colCount; ci++) {\r\n let max = visualWidth(headerCells[ci] ?? \"\");\r\n for (const row of dataRows) {\r\n const w = visualWidth(row[ci] ?? \"\");\r\n if (w > max) max = w;\r\n }\r\n colMaxWidths.push(Math.max(max, TABLE_MIN_COL_WIDTH));\r\n }\r\n\r\n // 列宽决策:usable 内尽量用 colMaxWidths,超出再等比缩\r\n const margin = TABLE_MARGIN;\r\n const totalMinWidth = colMaxWidths.reduce((s, w) => s + w + margin, 1); // +1 for leading │\r\n let colWidths: number[];\r\n if (totalMinWidth <= usable) {\r\n colWidths = colMaxWidths;\r\n } else {\r\n // 等比缩\r\n const available = usable - 1 - margin * colCount;\r\n const sum = colMaxWidths.reduce((s, w) => s + w, 0);\r\n colWidths = colMaxWidths.map((w) =>\r\n Math.max(TABLE_MIN_COL_WIDTH, Math.floor((w / sum) * available)),\r\n );\r\n // 修正舍入误差:若总宽仍超,再缩最后一列\r\n let total = colWidths.reduce((s, w) => s + w + margin, 1);\r\n while (total > usable && colWidths.length > 0) {\r\n const last = colWidths.length - 1;\r\n const cur = colWidths[last] ?? TABLE_MIN_COL_WIDTH;\r\n if (cur <= TABLE_MIN_COL_WIDTH) break;\r\n colWidths[last] = cur - 1;\r\n total = colWidths.reduce((s, w) => s + w + margin, 1);\r\n }\r\n }\r\n\r\n // 框线字符\r\n const H = \"─\";\r\n const makeSep = (l: string, m: string, r: string) =>\r\n l + colWidths.map((w) => H.repeat(w + margin)).join(m) + r;\r\n\r\n const topBorder = makeSep(\"┌\", \"┬\", \"┐\");\r\n const midBorder = makeSep(\"├\", \"┼\", \"┤\");\r\n const botBorder = makeSep(\"└\", \"┴\", \"┘\");\r\n\r\n // 渲染一行(含 wrap)\r\n function buildRow(cells: string[]): string[] {\r\n const wrapped = wrapRowCells(cells, colWidths, alignments);\r\n const rowHeight = Math.max(1, ...wrapped.map((lines) => lines.length));\r\n const lines: string[] = [];\r\n for (let li = 0; li < rowHeight; li++) {\r\n // 数据行总视觉宽 = \"│\" + sum(\" \" + segment + \" \") + \"│\" + 各列中间额外的\"│\"\r\n // 简化:每列拼 \" \" + segment + \" │\",末列单独拼 \" \" + segment + \"│\"\r\n let row = \"│\";\r\n for (let ci = 0; ci < colCount; ci++) {\r\n const cellLines = wrapped[ci] ?? [];\r\n const segment = cellLines[li] ?? \" \".repeat(colWidths[ci] ?? TABLE_MIN_COL_WIDTH);\r\n row += \" \" + segment + \" \";\r\n if (ci < colCount - 1) row += \"│\";\r\n }\r\n row += \"│\";\r\n lines.push(row);\r\n }\r\n return lines;\r\n }\r\n\r\n const headerRow = buildRow(headerCells);\r\n const dataRowsOut = dataRows.map(buildRow);\r\n const totalWidth = colWidths.reduce((s, w) => s + w + margin, 1);\r\n\r\n return {\r\n lines: [\r\n topBorder,\r\n ...headerRow,\r\n midBorder,\r\n ...dataRowsOut.flat(),\r\n botBorder,\r\n ],\r\n colWidths,\r\n totalWidth,\r\n };\r\n}\r\n\r\n/**\r\n * 探测当前进程 stdout 宽度(同步、SSR 安全)。失败时返回 undefined。\r\n */\r\nexport function detectTermWidth(): number | undefined {\r\n const c = (process.stdout as { columns?: number } | undefined)?.columns;\r\n return typeof c === \"number\" && c > 0 ? c : undefined;\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 思考链展示工具\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 独立框最多保留多少行;超过则折叠为单行省略提示 */\r\nexport const DEFAULT_REASONING_MAX_LINES = 8;\r\n\r\n/**\r\n * 把多段思考链拼接为单段文本,段之间用单换行连接(不空行)。\r\n *\r\n * 配合“滚动窗口”使用:思考内容越多越空行越浪费一行,改为紧凑连接,\r\n * 整个思考信息统一在一个框内呈现。自动过滤空段和纯空白段。\r\n */\r\nexport function joinReasoningSegments(segments: string[]): string {\r\n return segments\r\n .map((s) => s.trim())\r\n .filter((s) => s.length > 0)\r\n .join(\"\\n\");\r\n}\r\n\r\n/**\r\n * 按行\"滚动\"截断文本,超过 maxLines 时保留**末尾** maxLines 行(最新思考),\r\n * 丢弃开头的旧行。与终端自动滚屏语义一致。\r\n *\r\n * @returns\r\n * - visible: 要展示的字符串(已用 \\n 重组)\r\n * - hiddenLines: 被滚出窗口的旧行数(0 表示未截断;保留供未来统计 / 调试)\r\n * - totalLines: 原始总行数\r\n *\r\n * @pure\r\n */\r\nexport function truncateReasoningLines(\r\n text: string,\r\n maxLines: number,\r\n): { visible: string; hiddenLines: number; totalLines: number } {\r\n const lines = text.split(\"\\n\");\r\n if (lines.length <= maxLines) {\r\n return { visible: text, hiddenLines: 0, totalLines: lines.length };\r\n }\r\n return {\r\n visible: lines.slice(-maxLines).join(\"\\n\"),\r\n hiddenLines: lines.length - maxLines,\r\n totalLines: lines.length,\r\n };\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// Diff 预览组件 — 终端中渲染带色彩的 unified diff\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\nimport type { FileDiff } from \"../tool/types.js\";\r\n\r\ninterface DiffPreviewProps {\r\n /** FileDiff 对象 */\r\n diff: FileDiff;\r\n}\r\n\r\n/**\r\n * 解析 unified diff 文本,按行着色渲染。\r\n *\r\n * 颜色方案:\r\n * - `@@ ... @@` → 青色(hunk 头)\r\n * - `+` 添加行 → 绿色\r\n * - `-` 删除行 → 红色\r\n * - ` ` 上下文行 → 灰色\r\n *\r\n * 注意:文件头“--- a/...” / “+++ b/...”由摘要行展示,不重复渲染。\r\n */\r\nexport function DiffPreview({ diff }: DiffPreviewProps) {\r\n const { patch, additions, deletions, existedBefore, filePath } = diff;\r\n\r\n // 无变更\r\n if (!patch || patch.length === 0) {\r\n return null;\r\n }\r\n\r\n const lines = patch.split(\"\\n\");\r\n const fileName = filePath.replace(/\\\\/g, \"/\").split(\"/\").pop() ?? filePath;\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n {/* 摘要行 */}\r\n <Box flexDirection=\"row\" gap={1}>\r\n <Text bold color=\"#00ffff\">\r\n 📝 {existedBefore ? \"修改\" : \"新建\"}:\r\n </Text>\r\n <Text bold>{fileName}</Text>\r\n <Text color=\"#555555\">\r\n (+{additions} -{deletions})\r\n </Text>\r\n </Box>\r\n\r\n {/* Diff 内容(跳过文件头两行“--- a/x” / \"+++ b/x\",避免与摘要行重复展示文件名) */}\r\n <Box flexDirection=\"column\" marginLeft={2}>\r\n {lines\r\n .filter((line) => !line.startsWith(\"---\") && !line.startsWith(\"+++\"))\r\n .map((line, i) => (\r\n <DiffLine key={i} line={line} />\r\n ))}\r\n </Box>\r\n </Box>\r\n );\r\n}\r\n\r\n/** 渲染单行 diff 内容 */\r\nfunction DiffLine({ line }: { line: string }) {\r\n // 文件头行(如 \"--- a/path\" / \"+++ b/path\" / \"--- /dev/null\")\r\n // 摘要行已展示文件名与新建/修改状态,无需重复渲染\r\n if (line.startsWith(\"---\") || line.startsWith(\"+++\")) {\r\n return null;\r\n }\r\n\r\n // Hunk 头行 @@ -x,y +a,b @@\r\n if (line.startsWith(\"@@\")) {\r\n // 将 @@ 标记和行号范围分别着色\r\n return <Text color=\"#00cccc\">{line}</Text>;\r\n }\r\n\r\n // 添加行\r\n if (line.startsWith(\"+\")) {\r\n return <Text color=\"#22c55e\">{line}</Text>;\r\n }\r\n\r\n // 删除行\r\n if (line.startsWith(\"-\")) {\r\n return <Text color=\"#ef4444\">{line}</Text>;\r\n }\r\n\r\n // 上下文行(空格开头或空行)\r\n return <Text color=\"#6b7280\">{line}</Text>;\r\n}","// ---------------------------------------------------------------------------\r\n// SkillSelector — 用户输入 / 时渲染的 skill 列表组件\r\n// 通过 selectedIndex 高亮当前选中项,按 Tab 可补全 skill 名称\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\nimport type { SkillInfo } from \"../cli/skill-import.js\";\r\n\r\nconst HIGHLIGHT_COLOR = \"#00bfff\";\r\n\r\ninterface SkillSelectorProps {\r\n /** 所有可用 skill 列表 */\r\n skills: SkillInfo[];\r\n /** 当前输入框文本 */\r\n input: string;\r\n /** 当前选中的 skill 索引 */\r\n selectedIndex: number;\r\n}\r\n\r\nexport function SkillSelector({ skills, input, selectedIndex }: SkillSelectorProps) {\r\n // 匹配规则:/ 必须在输入开头,或者前面有空格\r\n const match = input.match(/(?:^|\\s)\\/([^/]*)$/);\r\n if (!match) return null;\r\n\r\n const query = match[1]!.toLowerCase().trim();\r\n if (skills.length === 0) return null;\r\n\r\n // 斜杠在开头且无后续内容时,展示全部 skill 作为提示\r\n const matched = !query && input.startsWith(\"/\")\r\n ? skills.slice(0, 3)\r\n : skills\r\n .filter((s) => s.name.toLowerCase().includes(query))\r\n .slice(0, 3);\r\n\r\n // 精确匹配时表示已补全完成,不显示列表\r\n if (query && matched.some((s) => s.name.toLowerCase() === query)) return null;\r\n\r\n if (matched.length === 0) return null;\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginLeft={4} marginTop={1}>\r\n <Text color=\"#808080\" dimColor>\r\n 支持的 Skill:\r\n </Text>\r\n {matched.map((skill, i) => (\r\n <Box key={skill.name}>\r\n <Text color={i === selectedIndex ? HIGHLIGHT_COLOR : \"#808080\"}>\r\n {i === selectedIndex ? \" › \" : \" \"}{skill.name}\r\n </Text>\r\n </Box>\r\n ))}\r\n <Box marginTop={1}>\r\n <Text color=\"#808080\" dimColor>\r\n ↑↓ 选择 · Tab 补全\r\n </Text>\r\n </Box>\r\n </Box>\r\n );\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// FileSelector — 用户输入 @ 时渲染的文件列表组件\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\n\r\ninterface FileSelectorProps {\r\n /** 所有项目文件路径列表(相对于 CWD) */\r\n files: string[];\r\n /** 当前输入框文本 */\r\n input: string;\r\n /** 当前选中的文件索引 */\r\n selectedIndex: number;\r\n}\r\n\r\nconst HIGHLIGHT_COLOR = \"#00ff41\";\r\n\r\nexport function FileSelector({ files, input, selectedIndex }: FileSelectorProps) {\r\n // 匹配规则:@ 必须在输入开头,或者前面有空格\r\n const match = input.match(/(?:^|\\s)@([^@]*)$/);\r\n if (!match) return null;\r\n\r\n const query = match[1]!.toLowerCase().trim();\r\n if (files.length === 0) return null;\r\n\r\n // @ 在开头且无后续内容时,展示前 5 个文件作为提示\r\n const matched = !query && input.startsWith(\"@\")\r\n ? files.slice(0, 5)\r\n : files\r\n .filter((f) => f.toLowerCase().includes(query))\r\n .slice(0, 5);\r\n\r\n // 精确匹配时表示已补全完成,不显示列表\r\n if (query && matched.some((f) => f.toLowerCase() === query)) return null;\r\n\r\n if (matched.length === 0) return null;\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginLeft={4} marginTop={1}>\r\n <Text color=\"#808080\" dimColor>\r\n 项目文件:\r\n </Text>\r\n {matched.map((file, i) => (\r\n <Box key={file}>\r\n <Text color={i === selectedIndex ? HIGHLIGHT_COLOR : \"#808080\"}>\r\n {i === selectedIndex ? \" › \" : \" \"}{file}\r\n </Text>\r\n </Box>\r\n ))}\r\n <Box marginTop={1}>\r\n <Text color=\"#808080\" dimColor>\r\n ↑↓ 选择 · Tab 补全\r\n </Text>\r\n </Box>\r\n </Box>\r\n );\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// TodoListPanel — 把 TodoList 的当前状态渲染成\"任务进度\"面板\r\n//\r\n// 设计:\r\n// - 每行格式:` <图标> <步骤名>`\r\n// - running 状态在图标位置用 <Spinner/>(持续动画)替代静态三角\r\n// - done 用绿色对勾(✅)、failed 用红色(✗)、其余用 dimColor\r\n// - 不显示 # 编号(终端不渲染 #)\r\n// - 不显示 evidence / reason(保持清爽)\r\n// - 不折叠已完成(用户要求全部展示)\r\n// - todo 为空时不渲染(避免空面板噪音)\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\nimport InkSpinner from \"ink-spinner\";\r\nimport type { TodoItem, TodoStatus } from \"../harness/todo-list.js\";\r\n\r\ninterface TodoListPanelProps {\r\n items: ReadonlyArray<TodoItem>;\r\n}\r\n\r\n/** 非 running 状态用静态符号;running 单独走 Spinner 组件以保持动画 */\r\nfunction statusIcon(status: TodoStatus): string {\r\n switch (status) {\r\n case \"pending\": return \"☐\";\r\n case \"done\": return \"☑\";\r\n case \"failed\": return \"✗\";\r\n case \"skipped\": return \"⊘\";\r\n // running 不走这里,由 TodoIcon 走 Spinner 组件\r\n case \"running\": return \"▶\";\r\n }\r\n}\r\n\r\n/**\r\n * 单行图标渲染:running 走动画 Spinner,其余走静态符号。\r\n * 抽成独立组件以让 React 识别 props 变化并重绘动画帧。\r\n */\r\nfunction TodoIcon({ status }: { status: TodoStatus }) {\r\n if (status === \"running\") {\r\n return (\r\n <Text color=\"cyan\">\r\n <InkSpinner type=\"dots\" />\r\n </Text>\r\n );\r\n }\r\n // done 用绿色对勾,与 running 的 spinner / 静态状态在颜色上区分\r\n if (status === \"done\") {\r\n return <Text color=\"green\">✅</Text>;\r\n }\r\n // failed 用红色,其它状态用 dimColor\r\n if (status === \"failed\") {\r\n return <Text color=\"red\">{statusIcon(status)}</Text>;\r\n }\r\n return <Text dimColor>{statusIcon(status)}</Text>;\r\n}\r\n\r\n/**\r\n * 把 todo 列表渲染成多行\"任务进度\"面板。\r\n * 步骤按原列表顺序展示(id 升序),不做截断 / 折叠。\r\n */\r\nexport function TodoListPanel({ items }: TodoListPanelProps) {\r\n if (items.length === 0) return null;\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n <Box>\r\n <Text dimColor>🔧 任务进度</Text>\r\n </Box>\r\n {items.map((it) => (\r\n <Box key={it.id}>\r\n <Text dimColor>{\" \"}</Text>\r\n <TodoIcon status={it.status} />\r\n <Text dimColor>{` ${it.content}`}</Text>\r\n </Box>\r\n ))}\r\n </Box>\r\n );\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// Provider 注册表 — 工厂模式管理 Provider 实例\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type { Provider, ModelId } from \"./types.js\";\r\nimport { isSupportedModel } from \"./models.js\";\r\nimport { ModelNotSupportedError } from \"./errors.js\";\r\nimport { DeepSeekProvider } from \"./deepseek.js\";\r\n\r\n/** Provider 工厂函数类型 */\r\nexport type ProviderFactory = (config: {\r\n apiKey: string;\r\n baseUrl: string;\r\n model: string;\r\n}) => Provider;\r\n\r\n/**\r\n * Provider 注册表 — 工厂模式管理 Provider 实例。\r\n *\r\n * 使用方式:\r\n * ```ts\r\n * const provider = registry.get(\"deepseek\", { apiKey, baseUrl, model });\r\n * for await (const chunk of provider.chat(messages)) { ... }\r\n * ```\r\n */\r\nexport class ProviderRegistry {\r\n readonly #factories = new Map<string, ProviderFactory>();\r\n readonly #instances = new Map<string, Provider>();\r\n\r\n /** 注册一个 Provider 工厂 */\r\n register(name: string, factory: ProviderFactory): void {\r\n this.#factories.set(name, factory);\r\n }\r\n\r\n /**\r\n * 获取或创建一个 Provider 实例(单例)。\r\n *\r\n * 相同的 name + baseUrl + model 组合会复用同一实例。\r\n * 如果 model 不在支持列表中,抛出 ModelNotSupportedError。\r\n */\r\n get(\r\n name: string,\r\n config: { apiKey: string; baseUrl: string; model: string },\r\n ): Provider {\r\n if (!isSupportedModel(config.model)) {\r\n throw new ModelNotSupportedError(config.model);\r\n }\r\n\r\n // 缓存键 = name:baseUrl:model(不同配置会创建不同实例)\r\n const cacheKey = `${name}:${config.baseUrl}:${config.model}`;\r\n\r\n const cached = this.#instances.get(cacheKey);\r\n if (cached) return cached;\r\n\r\n const factory = this.#factories.get(name);\r\n if (!factory) {\r\n const available = [...this.#factories.keys()].join(\", \");\r\n throw new Error(`未注册的 Provider: \"${name}\"。可用: ${available}`);\r\n }\r\n\r\n const instance = factory(config);\r\n this.#instances.set(cacheKey, instance);\r\n return instance;\r\n }\r\n\r\n /** 列出已注册的 Provider 名称 */\r\n list(): string[] {\r\n return [...this.#factories.keys()];\r\n }\r\n\r\n /** 清除缓存的实例(用于配置热重载) */\r\n clear(): void {\r\n this.#instances.clear();\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 全局默认注册表 + 预注册内置 Provider\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 全局默认 Provider 注册表 */\r\nexport const defaultRegistry = new ProviderRegistry();\r\n\r\ndefaultRegistry.register(\"deepseek\", (config) => {\r\n // 模型合法性已在 registry.get() 中通过 isSupportedModel 校验\r\n return new DeepSeekProvider({\r\n apiKey: config.apiKey,\r\n baseUrl: config.baseUrl,\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n model: config.model as unknown as ModelId,\r\n });\r\n});\r\n\r\n/**\r\n * 从配置快速创建 Provider 实例的快捷方式。\r\n *\r\n * 内部委托给 defaultRegistry,会在注册表中校验模型并缓存实例。\r\n */\r\nexport function createProvider(config: {\r\n name: string;\r\n apiKey: string;\r\n baseUrl: string;\r\n model: string;\r\n}): Provider {\r\n return defaultRegistry.get(config.name, config);\r\n}","// ---------------------------------------------------------------------------\r\n// 工具系统核心类型定义\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 工具的分类语义,替代原来的 boolean readOnly */\r\nexport enum ToolKind {\r\n /** 纯读操作,无副作用,可并行执行 */\r\n Read = \"read\",\r\n /** 文件/目录编辑(修改内容) */\r\n Edit = \"edit\",\r\n /** 文件/目录删除 */\r\n Delete = \"delete\",\r\n /** 移动/重命名 */\r\n Move = \"move\",\r\n /** 其他(bash、fetch 等) */\r\n Other = \"other\",\r\n}\r\n\r\n/** 工具参数的 JSON Schema 表示 */\r\nexport interface JSONSchema {\r\n type: \"object\";\r\n properties?: Record<string, unknown>;\r\n required?: string[];\r\n additionalProperties?: boolean | Record<string, unknown>;\r\n /** Schema 自带的 description,自动用作 tool description */\r\n description?: string;\r\n}\r\n\r\n/** 每次工具执行时传入的上下文 */\r\nexport interface ToolContext {\r\n /** 当前工作目录,用于路径约束和相对路径解析 */\r\n cwd: string;\r\n /** 中止信号,用于取消正在执行的工具 */\r\n signal?: AbortSignal;\r\n /** 命令执行超时(毫秒),默认 30000 */\r\n timeout?: number;\r\n /**\r\n * 写操作的允许根目录列表(绝对路径)。\r\n * 非空时,写工具必须确保目标路径在其中一个根下;\r\n * 为空数组表示不限制(保持兼容)。\r\n */\r\n writeRoots?: string[];\r\n}\r\n\r\n/** 文件变更的 diff 信息 */\r\nexport interface FileDiff {\r\n /** 文件路径(绝对路径) */\r\n filePath: string;\r\n /** unified diff 文本 */\r\n patch: string;\r\n /** 变更前是否存在(新建文件时为 false) */\r\n existedBefore: boolean;\r\n /** 变更统计:新增行数 */\r\n additions: number;\r\n /** 变更统计:删除行数 */\r\n deletions: number;\r\n}\r\n\r\n/**\r\n * 工具执行结果。\r\n *\r\n * 设计要点:\r\n * - 成功和失败共用相同结构,LLM 总能读到可理解的 data\r\n * - error 字段仅做分类标记(如 TEXT_NOT_FOUND),不暴露内部细节\r\n * - diff 仅在文件修改工具携带\r\n * - summary 给 UI 展示,不暴露完整 data 给 UI\r\n */\r\nexport interface ToolResult {\r\n /** 是否执行成功 */\r\n success: boolean;\r\n /** 结果内容(成功时为输出,失败时为错误信息),LLM 可读 */\r\n data: string;\r\n /** 错误分类标记(仅在 success=false 时有意义) */\r\n error?: string;\r\n /** 文件变更 diff(仅文件修改工具携带) */\r\n diff?: FileDiff;\r\n /**\r\n * 给 UI 展示的简短摘要(一行)。\r\n * 与 data 区别:data 喂给 LLM 保留完整内容,summary 仅做一行回显。\r\n * 未设置时 UI 自动降级到 data 的前 500 字符。\r\n */\r\n summary?: string;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// AgentTool — 静态类型安全的工具定义\r\n//\r\n// 每个工具定义为一个对象,提供类型安全的泛型参数。\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * AgentTool 定义 — 一个工具的完整声明。\r\n *\r\n * @template I 工具输入参数类型\r\n * @template O 工具输出类型(从 ToolResult 继承)\r\n */\r\nexport interface AgentTool<I, O extends ToolResult = ToolResult> {\r\n /** 工具名称,全局唯一标识符 */\r\n readonly name: string;\r\n\r\n /** 工具的分类语义 */\r\n readonly kind: ToolKind;\r\n\r\n /** 参数 JSON Schema 定义 */\r\n readonly parameters: JSONSchema;\r\n\r\n /**\r\n * 工具描述 — 默认从 parameters.description 自动提取,\r\n * 也可显式覆盖以提供更详细的说明。\r\n */\r\n readonly description: string;\r\n\r\n /** 工具执行逻辑 */\r\n execute(args: I, ctx: ToolContext): Promise<O>;\r\n\r\n /**\r\n * UI 初始标题(可选)。\r\n * 用于在 UI 中显示正在执行的工具名称。\r\n */\r\n initialTitle?(args: I): string;\r\n\r\n /**\r\n * 是否支持输入流式传输(可选)。\r\n * 用于支持 LLM 流式传输工具调用参数。\r\n */\r\n supportsInputStreaming?: boolean;\r\n\r\n /**\r\n * 工具支持的 LLM Provider(可选)。\r\n * 返回空/undefined 表示支持所有 provider。\r\n */\r\n supportedProviders?: string[];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// AnyAgentTool — 动态分发接口(Type Erasure 模式)\r\n//\r\n// 将类型参数擦除,用于 Registry 等需要存储异构工具集合的场景。\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * AnyAgentTool — 类型擦除后的工具接口。\r\n * Registry 存储的是此类型,执行时内部做反序列化。\r\n */\r\nexport interface AnyAgentTool {\r\n readonly name: string;\r\n readonly description: string;\r\n readonly kind: ToolKind;\r\n readonly parameters: JSONSchema;\r\n readonly supportsInputStreaming: boolean;\r\n readonly supportedProviders: string[];\r\n\r\n execute(args: unknown, ctx: ToolContext): Promise<ToolResult>;\r\n initialTitle?(args: unknown): string;\r\n}\r\n\r\n/**\r\n * 将类型安全的 AgentTool 擦除为 AnyAgentTool。\r\n */\r\nexport function eraseTool<I, O extends ToolResult = ToolResult>(\r\n tool: AgentTool<I, O>,\r\n): AnyAgentTool {\r\n return {\r\n get name() { return tool.name; },\r\n get description() { return tool.description; },\r\n get kind() { return tool.kind; },\r\n get parameters() { return tool.parameters; },\r\n get supportsInputStreaming() { return tool.supportsInputStreaming ?? false; },\r\n get supportedProviders() { return tool.supportedProviders ?? []; },\r\n\r\n async execute(args: unknown, ctx: ToolContext): Promise<ToolResult> {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n return tool.execute(args as I, ctx);\r\n },\r\n\r\n initialTitle(args: unknown): string {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n return tool.initialTitle?.(args as I) ?? tool.name;\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * 从 JSON Schema 中提取 description。\r\n * 若 schema.properties 的顶层有 description,使用它;\r\n * 否则使用 schema 本身的 description。\r\n */\r\nexport function extractDescription(schema: JSONSchema, fallback?: string): string {\r\n if (schema.description) return schema.description;\r\n if (fallback) return fallback;\r\n return \"\";\r\n}\r\n\r\n/**\r\n * 根据 ToolKind 判断是否只读(可并行执行)。\r\n * Read 一定只读;Edit/Delete/Move 一定写;Other 默认非只读。\r\n */\r\nexport function isReadOnly(kind: ToolKind): boolean {\r\n return kind === ToolKind.Read;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Permission/Gate 接口\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Gate 权限门接口 — 控制工具执行前是否需要审批。\r\n */\r\nexport interface Gate {\r\n check(toolName: string, args: unknown): boolean | Promise<boolean>;\r\n}\r\n\r\n/**\r\n * AlwaysAllowGate — 默认权限门,所有工具调用默认放行。\r\n */\r\nexport class AlwaysAllowGate implements Gate {\r\n check(_toolName: string, _args: unknown): boolean {\r\n return true;\r\n }\r\n}\r\n\r\n/** 工具调用记录(用于风暴检测等) */\r\nexport interface ToolCallRecord {\r\n name: string;\r\n success: boolean;\r\n error?: string;\r\n timestamp: number;\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 系统提示词构建 — 使用 Handlebars 模板引擎渲染\r\n// ---------------------------------------------------------------------------\r\n\r\nimport Handlebars from \"handlebars\";\r\nimport type { SystemPromptOptions, ToolDescription } from \"./types.js\";\r\nimport systemPromptTemplate from \"./prompts/system-prompt.hbs\";\r\nimport planPromptTemplate from \"./prompts/plan-prompt.hbs\";\r\n\r\nconst compiledTemplate = Handlebars.compile(systemPromptTemplate);\r\nconst compiledPlanTemplate = Handlebars.compile(planPromptTemplate);\r\n\r\ninterface ToolView {\r\n name: string;\r\n description: string;\r\n params?: string;\r\n}\r\n\r\nfunction formatTools(tools: ToolDescription[]): ToolView[] {\r\n return tools.map((t) => {\r\n const params =\r\n t.parameters &&\r\n Object.keys((t.parameters).properties ?? {})\r\n .join(\", \");\r\n return {\r\n name: t.name,\r\n description: t.description,\r\n params: params || undefined,\r\n };\r\n });\r\n}\r\n\r\n/** 构建公共模板变量 */\r\nfunction buildTemplateVars(opts: SystemPromptOptions) {\r\n const now = new Date();\r\n const date = now.toLocaleDateString(\"zh-CN\", {\r\n year: \"numeric\",\r\n month: \"long\",\r\n day: \"numeric\",\r\n weekday: \"long\",\r\n });\r\n const time = now.toLocaleTimeString(\"zh-CN\", {\r\n hour: \"2-digit\",\r\n minute: \"2-digit\",\r\n });\r\n\r\n return {\r\n model: opts.model,\r\n maxToolRounds: opts.maxToolRounds,\r\n cwd: opts.cwd,\r\n date,\r\n time,\r\n tools: opts.tools ? formatTools(opts.tools) : undefined,\r\n projectContext: opts.projectContext ?? undefined,\r\n };\r\n}\r\n\r\n/**\r\n * 构建 Agent 的系统提示词(代码模式)。\r\n *\r\n * 使用 Handlebars 模板 `prompts/system-prompt.hbs` 渲染,\r\n * 动态注入模型信息、工具描述、时间、项目上下文。\r\n */\r\nexport function buildSystemPrompt(opts: SystemPromptOptions): string {\r\n return compiledTemplate(buildTemplateVars(opts));\r\n}\r\n\r\n/**\r\n * 构建计划模式(Plan Mode)的系统提示词。\r\n *\r\n * 使用 Handlebars 模板 `prompts/plan-prompt.hbs` 渲染,\r\n * 聚焦于代码分析、架构设计和任务规划,不包含写工具信息。\r\n */\r\nexport function buildPlanSystemPrompt(opts: SystemPromptOptions): string {\r\n return compiledPlanTemplate(buildTemplateVars(opts));\r\n}\r\n","你是 dskcode,一个基于 DeepSeek 的 AI 编程助手,运行在用户终端中。你的职责是帮助开发者编写、理解、调试和重构代码。\r\n\r\n## 核心原则\r\n- 回答使用中文,代码标识符保持英文\r\n- **思考过程(CoT)也使用中文**,不要用英文思考。如果你发现自己在用英文\r\n 想,请切换到中文。你的所有内心独白和最终回答都应用中文表达。\r\n- 提供准确、实用、可操作的建议\r\n- 对不确定的内容明确标注,不编造事实\r\n- 代码示例保持简洁,附带必要的注释\r\n\r\n## 用户输入语法\r\n- `@<路径>` 表示一个**文件路径**的引用标识。`@` 不是邮箱符号、也不是装饰后缀。\r\n - `@test.ts` = 引用当前目录下名为 `test.ts` 的文件\r\n - `@src/foo/bar.ts` = 引用 `src/foo/bar.ts`\r\n - 多个引用:`请读 @a.ts 和 @b/d.ts`\r\n - 路径可以是相对路径(相对工作目录)或绝对路径\r\n- `\\/<命令>` 表示一个**斜杠命令**(如 `\\/help`、`\\/plan`、`\\/code`),不是目录。\r\n - 收到 `/xxx` 开头的内容先判定是不是已注册命令;不是则当作普通文本处理。\r\n- 上述两种前缀是**用户输入面**语法,你**不要在回复中输出**它们(不要伪造 `@/path` 这种伪引用)。如果要引用文件,直接用单反引号包裹路径(如 `src/foo.ts`)。\r\n\r\n## 工作流程\r\n\r\n你处于一个工具调用循环中:\r\n1. 收到用户请求\r\n2. 调用工具获取信息或修改文件\r\n3. 工具结果返回给你\r\n4. 根据结果决定下一步(继续调用工具或给出最终回答)\r\n5. 一轮最多 {{maxToolRounds}} 次工具调用\r\n\r\n## 工具使用策略\r\n\r\n{{#if tools}}\r\n当前可用的工具:\r\n{{#each tools}}\r\n- **{{name}}**:{{description}}{{#if params}}(参数:{{params}}){{/if}}\r\n{{/each}}\r\n\r\n使用原则:\r\n- **先搜索再读取** — 不确定目标位置时先用 grep/glob 定位,再用 read_file 读取\r\n- **先读取再修改** — 编辑文件前先 read_file 确认当前内容\r\n- **批量操作一次完成** — 同一文件的多处改动在单次 edit_file 中完成\r\n- **工具出错时** — 分析错误原因(路径不对?格式不对?权限不足?),修正后重试,不要重复相同的错误调用\r\n- **审慎执行** — bash 执行危险命令(rm -rf、强制写入等)前先确认\r\n{{/if}}\r\n\r\n## 工作流(重要)— 复杂任务判定\r\n\r\n收到用户请求后,**先判定复杂度**,再决定要不要拆 todo。\r\n\r\n### 默认假设是「复杂」\r\n\r\n除非明确属于下面「简单」三选一,**默认按复杂处理,先 todo_add 拆 3-7 步**。\r\n\r\n### 判定问卷(逐条 yes/no)\r\n\r\n问自己以下问题,**任一回答 yes → 拆 todo**:\r\n- 1️⃣ 要写/改/创建 **超过 1 个**文件?\r\n- 2️⃣ 要先 **读后改**(edit_file / write_file 之前必须 read_file)?\r\n- 3️⃣ 要跨 **2 种以上**工具(read + edit + bash + ...)?\r\n- 4️⃣ 要 **对比 2 个以上事实源**(如 README 说的 vs 代码实际写的、这里 vs 那里)?\r\n- 5️⃣ 句子超过 **20 个字** 且 **包含「改成 / 加个 / 实现 / 写一个 / 重构 / 修复 / 调整」** 这类动词?\r\n\r\n### 「简单」三选一(可以不拆 todo)\r\n\r\n- 纯查询:「列出 X」「看看 Y」「搜一下 Z」→ 1 个工具 1 轮搞定\r\n- 1 个工具 1 轮能搞定的读/列/查操作\r\n- 闲聊 / 解释性问题(不调任何工具)\r\n\r\n### 判定决策树\r\n\r\n```\r\n用户输入\r\n ├─ 是「简单三选一」? → 直接调工具,不拆 todo\r\n └─ 不是? → 先 todo_add 拆 3-7 步再动手(默认行为)\r\n```\r\n\r\n### todo 拆解模板\r\n\r\n**改动型任务**(最常见):\r\n1. `todo_add(\"读 <目标文件>\")`\r\n2. `todo_add(\"<具体改动>\", deps=[0])`\r\n3. `todo_add(\"检查改动\", deps=[1])`(重新读改过的文件,确认修改正确且无遗漏;必要时跑 type-check)\r\n\r\n**新建型任务**:\r\n1. `todo_add(\"看现有同类文件怎么写\")`\r\n2. `todo_add(\"实现新 <X>\", deps=[0])`\r\n3. `todo_add(\"验证\", deps=[1])`\r\n\r\n**调试型任务**:\r\n1. `todo_add(\"复现 bug\")`\r\n2. `todo_add(\"定位原因\", deps=[0])`\r\n3. `todo_add(\"修复\", deps=[1])`\r\n4. `todo_add(\"检查修复\", deps=[2])`(读改过的文件,确认修复正确没有引入新问题)\r\n\r\n### 执行流(严格遵守)\r\n\r\n- 拆完 todo 后,**先 `todo_mark_running(id)`** → 调实际工具 → **`todo_mark_done(id, \"证据\")`**\r\n- 失败:`todo_mark_failed(id, \"原因\")`;想重试:`todo_retry(id)` → 再走一遍\r\n走完所有 todo,Harness 会自动做任务检查——重新读取本次改过的文件,确认修改内容正确、无遗漏、类型兼容\r\n\r\n### 反例(别这样)\r\n\r\n- ❌ 用户说「把 X 改成 Y」→ 跳过 todo 直接 read + edit(撞墙率高)\r\n- ❌ 用户说「写个新工具」→ 跳过 todo 直接 write_file(容易写错接口)\r\n- ❌ 拆了 todo 但忘了 mark_running 就 mark_done(依赖检查会失败)\r\n\r\n{{#if projectContext}}\r\n## 项目上下文\r\n\r\n{{projectContext}}\r\n{{/if}}\r\n\r\n## 回答格式\r\n\r\n终端不支持 Markdown 渲染。回复格式要求:\r\n\r\n- 用 emoji 做视觉标记(📌 要点 ⚠️ 注意 📝 待办 ✅ 完成)\r\n- 用数字序号或短横线组织列表\r\n- 代码标识符、文件路径、类型名用单个反引号 ` 包裹,会被自动高亮\r\n- 强调内容用 **加粗** 包裹,会被自动高亮\r\n- 多行代码用缩进 4 个空格表示,不要用三反引号代码块\r\n- 段落间用空行分隔\r\n- **不要使用 `#` 符号** — 终端不渲染 Markdown 标题,请用 emoji + 加粗替代(如 `📌 **标题**`)\r\n\r\n正确示例:\r\n\r\n📌 **修改计划**\r\n\r\n📌 需要修改两个地方:\r\n1. 第 12 行的 `interface FileDiff` 改为 `interface Diff`\r\n2. 第 432 行的 `): FileDiff` 改为 `): Diff`\r\n\r\n 代码示例:\r\n const pool = new Pool({ max: 20 });\r\n await pool.query(\"SELECT * FROM users\");\r\n\r\n## 行为约束\r\n- 如果用户请求不明确,主动询问澄清\r\n- 对不确定的信息明确标注,不编造事实\r\n- 不执行可能造成不可逆损害的操作(如 rm -rf),除非用户明确逐条确认\r\n- 工具调用返回错误时,先分析原因再重试,不要盲目重试\r\n\r\n## 当前模型\r\n- 模型:{{model}}\r\n\r\n## 时间上下文\r\n- 当前日期:{{date}}\r\n- 当前时间:{{time}}\r\n- 工作目录:{{cwd}}\r\n","你是 dskcode,当前工作在**计划模式(Plan Mode)**。\r\n\r\n在此模式下,**你只能读取和分析代码,不能执行任何修改操作**。你的职责是帮助用户理解代码、设计架构、规划任务、评估方案。\r\n\r\n## 核心原则\r\n- 回答使用中文,代码标识符保持英文\r\n- **思考过程(CoT)也使用中文**,不要用英文思考。\r\n- 提供准确、实用、可操作的建议\r\n- 对不确定的内容明确标注,不编造事实\r\n- **绝不执行任何写操作** — 你只能读取和分析\r\n\r\n## 用户输入语法\r\n- `@<路径>` 表示一个**文件路径**的引用标识。`@` 不是邮箱符号、也不是装饰后缀。\r\n - `@test.ts` = 引用当前目录下名为 `test.ts` 的文件\r\n - `@src/foo/bar.ts` = 引用 `src/foo/bar.ts`\r\n - 多个引用:`请读 @a.ts 和 @b/d.ts`\r\n - 路径可以是相对路径(相对工作目录)或绝对路径\r\n- `\\/<命令>` 表示一个**斜杠命令**(如 `\\/help`、`\\/plan`、`\\/code`),不是目录。\r\n - 收到 `/xxx` 开头的内容先判定是不是已注册命令;不是则当作普通文本处理。\r\n- 上述两种前缀是**用户输入面**语法,你**不要在回复中输出**它们(不要伪造 `@/path` 这种伪引用)。如果要引用文件,直接用单反引号包裹路径(如 `src/foo.ts`)。\r\n\r\n## 工作流程\r\n1. 收到用户请求\r\n2. 使用读工具(read-file, grep, glob, ls, fetch)探索代码库\r\n3. 根据发现的信息提供结构化的分析或计划\r\n4. 工具返回出错时分析原因并修正后重试\r\n5. 完成后输出最终分析结果\r\n\r\n## 工具限制\r\n**你只能使用读工具**,不能使用任何写工具(write-file, edit-file, multi-edit, delete-range, bash)。\r\n如果用户请求修改代码,请:\r\n- 提供修改建议和方案\r\n- 指出需要修改的文件和位置\r\n- 解释修改的影响\r\n- 但不执行任何修改\r\n\r\n## 计划输出格式\r\n\r\n当用户请求计划/方案时,建议按照以下结构组织输出:\r\n\r\n📋 **概述**\r\n 1-2 句话说明要做什么以及为什么要做\r\n\r\n🔍 **现状分析**\r\n 当前代码的组织结构和关键发现\r\n\r\n⚡ **影响范围**\r\n 哪些文件/模块会受到影响\r\n\r\n📝 **实施步骤**\r\n 1. 第一步(文件路径/变更说明)\r\n 2. 第二步(文件路径/变更说明)\r\n 3. ...\r\n\r\n⚠️ **风险与注意事项**\r\n 可能的风险点、依赖关系、回退策略\r\n\r\n## 回答格式\r\n\r\n终端不支持 Markdown 渲染。回复格式要求:\r\n- 用 emoji 做视觉标记\r\n- 用数字序号或短横线组织列表\r\n- 代码标识符、文件路径用单个反引号 ` 包裹\r\n- 多行代码用缩进 4 个空格表示\r\n- 段落间用空行分隔\r\n- **不要使用 `#` 符号** — 终端不渲染 Markdown 标题,请用 emoji + 加粗替代\r\n\r\n## 当前模型\r\n- 模型:{{model}}\r\n\r\n## 时间上下文\r\n- 当前日期:{{date}}\r\n- 当前时间:{{time}}\r\n- 工作目录:{{cwd}}\r\n","// ---------------------------------------------------------------------------\r\n// 工具注册表 — 注册、查找、列出、过滤工具\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { ToolKind, type AnyAgentTool, type ToolContext, type ToolResult, type AgentTool, eraseTool, isReadOnly } from \"./types.js\";\r\n\r\n/** 工具注册表配置 */\r\nexport interface ToolRegistryOptions {\r\n /** 工具名称黑名单,这些工具不会被 list() 返回 */\r\n disabledTools?: string[];\r\n /**\r\n * Feature Flag 检查函数,返回 true 表示该工具可用。\r\n * 不在列表中的工具默认启用。\r\n */\r\n featureFlagChecker?: (toolName: string) => boolean;\r\n /**\r\n * 当前使用的 LLM Provider ID。\r\n * 提供后只会列出支持该 provider 的工具。\r\n */\r\n provider?: string;\r\n}\r\n\r\n/**\r\n * ToolRegistry — 管理所有已注册的工具。\r\n *\r\n * 设计要点:\r\n * - 注册时检查名称唯一性\r\n * - 支持 disable/enable 工具(基于配置项过滤)\r\n * - 支持 Feature Flag 门控\r\n * - 支持 Provider 过滤\r\n * - get() 返回 undefined 而非抛异常,调用方自行处理\r\n * - ALL_TOOL_NAMES 静态常量列表\r\n */\r\nexport class ToolRegistry {\r\n readonly #tools = new Map<string, AnyAgentTool>();\r\n readonly #disabledNames: Set<string>;\r\n readonly #featureFlagChecker: (toolName: string) => boolean;\r\n readonly #provider: string | undefined;\r\n\r\n /** 所有已注册工具的名称列表(编译期等效) */\r\n static readonly ALL_TOOL_NAMES: string[] = [];\r\n\r\n constructor(opts?: ToolRegistryOptions) {\r\n this.#disabledNames = new Set(opts?.disabledTools ?? []);\r\n this.#featureFlagChecker = opts?.featureFlagChecker ?? (() => true);\r\n this.#provider = opts?.provider;\r\n }\r\n\r\n /**\r\n * 注册一个类型安全的 AgentTool。\r\n * 自动擦除类型参数后存储。\r\n * 如果同名工具已存在则抛出错误。\r\n */\r\n register<I, O extends ToolResult = ToolResult>(tool: AgentTool<I, O>): this {\r\n return this.registerErased(eraseTool(tool));\r\n }\r\n\r\n /**\r\n * 注册一个已经擦除类型 AnyAgentTool。\r\n * 如果同名工具已存在则抛出错误。\r\n */\r\n registerErased(tool: AnyAgentTool): this {\r\n if (this.#tools.has(tool.name)) {\r\n throw new Error(`工具 \"${tool.name}\" 已注册,不能重复注册`);\r\n }\r\n this.#tools.set(tool.name, tool);\r\n return this;\r\n }\r\n\r\n /**\r\n * 批量注册工具。\r\n */\r\n registerAll(tools: AnyAgentTool[]): this {\r\n for (const tool of tools) {\r\n this.registerErased(tool);\r\n }\r\n return this;\r\n }\r\n\r\n /**\r\n * 注销一个工具。\r\n */\r\n unregister(name: string): boolean {\r\n return this.#tools.delete(name);\r\n }\r\n\r\n /**\r\n * 按名称获取工具(未擦除类型版本,调用方自行断言类型)。\r\n * 如果工具被禁用、被 feature flag 拦截、或不支持当前 provider,返回 undefined。\r\n */\r\n get(name: string): AnyAgentTool | undefined {\r\n if (!this.#isToolEnabled(name)) return undefined;\r\n return this.#tools.get(name);\r\n }\r\n\r\n /**\r\n * 获取所有启用的工具列表。\r\n * 依次应用:禁用列表 → Feature Flag → Provider 过滤。\r\n */\r\n list(): AnyAgentTool[] {\r\n const result: AnyAgentTool[] = [];\r\n for (const [name, tool] of this.#tools) {\r\n if (this.#isToolEnabled(name)) {\r\n result.push(tool);\r\n }\r\n }\r\n return result;\r\n }\r\n\r\n /**\r\n * 按 ToolKind 分类获取工具列表。\r\n */\r\n listByKind(kind: ToolKind): AnyAgentTool[] {\r\n return this.list().filter((t) => t.kind === kind);\r\n }\r\n\r\n /**\r\n * 获取读工具列表(可并行执行)。\r\n */\r\n listReadTools(): AnyAgentTool[] {\r\n return this.listByKind(ToolKind.Read);\r\n }\r\n\r\n /**\r\n * 获取写工具列表(需串行执行)。\r\n */\r\n listWriteTools(): AnyAgentTool[] {\r\n return this.list().filter((t) => !isReadOnly(t.kind));\r\n }\r\n\r\n /**\r\n * 获取所有已注册的工具名称(含禁用的)。\r\n */\r\n names(): string[] {\r\n return [...this.#tools.keys()];\r\n }\r\n\r\n /**\r\n * 检查工具是否已注册(含禁用的)。\r\n */\r\n has(name: string): boolean {\r\n return this.#tools.has(name);\r\n }\r\n\r\n /**\r\n * 检查工具是否已启用(注册 + 未禁用 + 通过 feature flag + 支持 provider)。\r\n */\r\n isEnabled(name: string): boolean {\r\n return this.#tools.has(name) && this.#isToolEnabled(name);\r\n }\r\n\r\n /**\r\n * 禁用一个工具。\r\n */\r\n disable(name: string): void {\r\n this.#disabledNames.add(name);\r\n }\r\n\r\n /**\r\n * 启用一个之前被禁用的工具。\r\n */\r\n enable(name: string): void {\r\n this.#disabledNames.delete(name);\r\n }\r\n\r\n /**\r\n * 获取工具的分类语义。\r\n */\r\n kindOf(name: string): ToolKind | undefined {\r\n return this.#tools.get(name)?.kind;\r\n }\r\n\r\n /**\r\n * 执行指定工具。\r\n *\r\n * @returns 工具执行结果;如果工具不存在或被禁用,返回失败结果\r\n */\r\n async execute(name: string, args: unknown, ctx: ToolContext): Promise<ToolResult> {\r\n const tool = this.get(name);\r\n if (!tool) {\r\n return {\r\n success: false,\r\n data: `工具 \"${name}\" 不存在或已被禁用`,\r\n error: \"TOOL_NOT_FOUND\",\r\n };\r\n }\r\n\r\n try {\r\n return await tool.execute(args, ctx);\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `工具 \"${name}\" 执行异常:${message}`,\r\n error: \"EXECUTION_ERROR\",\r\n };\r\n }\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // 内部方法\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * 判断工具是否应该启用。\r\n * 依次检查:是否在禁用列表 → 是否通过 Feature Flag → 是否支持当前 Provider。\r\n */\r\n #isToolEnabled(name: string): boolean {\r\n const tool = this.#tools.get(name);\r\n if (!tool) return false;\r\n\r\n // 1. 禁用列表检查\r\n if (this.#disabledNames.has(name)) return false;\r\n\r\n // 2. Feature Flag 检查\r\n if (!this.#featureFlagChecker(name)) return false;\r\n\r\n // 3. Provider 兼容性检查\r\n if (this.#provider && tool.supportedProviders.length > 0) {\r\n if (!tool.supportedProviders.includes(this.#provider)) return false;\r\n }\r\n\r\n return true;\r\n }\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// ToolExecutor — 纯执行层:解析参数、查工具、走权限门、跑工具、捕异常\r\n//\r\n// 设计原则:\r\n// - 不持有 Session / messages 状态,只依赖注入的 registry / gate / ctx\r\n// - 输入:一批 ProviderToolCall\r\n// - 输出:执行结果项(含 result)+ 风暴检测用的 records\r\n// - 异常安全:单个工具的异常不会中断其他工具\r\n//\r\n// 函数注释规范见仓库根 AGENTS.md「函数注释规范」一节。\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type { ProviderToolCall } from \"../provider/index.js\";\r\nimport type { ToolRegistry } from \"../tool/registry.js\";\r\nimport type {\r\n ToolCallRecord,\r\n ToolContext,\r\n ToolResult,\r\n Gate,\r\n AnyAgentTool,\r\n} from \"../tool/types.js\";\r\nimport { isReadOnly } from \"../tool/types.js\";\r\n\r\n/**\r\n * 单个工具执行的产出(喂回 Session 协调层)。\r\n *\r\n * @field name — 工具名(与 ProviderToolCall.name 一致)\r\n * @field callId — LLM 侧 call id,用于 messages.push 时回填 toolCallId\r\n * @field result — 工具执行结果(含 success / data / error / diff)\r\n */\r\nexport interface ToolExecutionItem {\r\n name: string;\r\n callId: string;\r\n result: ToolResult;\r\n}\r\n\r\n/**\r\n * 一次批量执行的完整产出。\r\n *\r\n * @field items — 所有工具执行结果(成功 + 失败)\r\n * @field records — 仅失败的 record(喂给 StormDetector 判断是否中断)\r\n */\r\nexport interface ToolExecutionResult {\r\n items: ToolExecutionItem[];\r\n /** 仅失败的 record(用于风暴检测) */\r\n records: ToolCallRecord[];\r\n}\r\n\r\n/**\r\n * ToolExecutor 的注入依赖。\r\n *\r\n * @field registry — 工具注册表(用于按名查找)\r\n * @field gate — 权限门(决定是否放行某个工具调用)\r\n * @field baseCtx — 工具执行的上下文(cwd / signal / writeRoots)\r\n * @field maxParallel — 并行执行的最大同时执行数(默认 8)\r\n */\r\nexport interface ToolExecutorDeps {\r\n registry: ToolRegistry;\r\n gate: Gate;\r\n baseCtx: Omit<ToolContext, \"signal\"> & { signal?: AbortSignal };\r\n /** 并行执行的最大同时执行数(默认 8) */\r\n maxParallel?: number;\r\n}\r\n\r\n/**\r\n * ToolExecutor:把\"工具调用 batch\"映射为\"执行结果 + 失败记录\"。\r\n *\r\n * 行为:\r\n * - 若全部为只读工具(Read 类)且数量 > 1:分批并行(每批最多 maxParallel 个)\r\n * - 其他情况:串行执行(保证有副作用的工具按 LLM 给出的顺序执行)\r\n *\r\n * 异常路径:工具不存在 / 权限门拒绝 / 执行抛错,都会被捕获并包装成失败 ToolResult,\r\n * 不会让一个工具的错误中断其他工具的执行。\r\n */\r\nexport class ToolExecutor {\r\n /** 工具注册表(只读引用) */\r\n readonly #registry: ToolRegistry;\r\n /** 权限门(只读引用) */\r\n readonly #gate: Gate;\r\n /** 工具执行上下文(每次执行都共用) */\r\n readonly #baseCtx: ToolContext;\r\n /** 并行执行的最大同时执行数(默认 8) */\r\n readonly #maxParallel: number;\r\n\r\n /**\r\n * 构造一个 ToolExecutor。\r\n *\r\n * @param deps — 注入依赖(registry / gate / baseCtx / maxParallel)\r\n * @pure 仅保存引用,不调用任何外部 IO\r\n */\r\n constructor(deps: ToolExecutorDeps) {\r\n this.#registry = deps.registry;\r\n this.#gate = deps.gate;\r\n this.#baseCtx = {\r\n cwd: deps.baseCtx.cwd,\r\n signal: deps.baseCtx.signal,\r\n writeRoots: deps.baseCtx.writeRoots,\r\n ...(deps.baseCtx.timeout !== undefined ? { timeout: deps.baseCtx.timeout } : {}),\r\n };\r\n this.#maxParallel = deps.maxParallel ?? 8;\r\n }\r\n\r\n /**\r\n * 执行一批工具调用。\r\n *\r\n * 自动选择并行 / 串行:\r\n * - 全部为只读工具 且 calls.length > 1:分批并行(每批最多 #maxParallel 个)\r\n * - 其他:严格串行\r\n *\r\n * @param calls — LLM 决定的本轮工具调用列表\r\n * @returns items(全部结果)+ records(仅失败项,用于风暴检测)\r\n *\r\n * @sideEffect 调用 registry / gate / tool.execute;不修改 Session.messages\r\n */\r\n async executeBatch(calls: ProviderToolCall[]): Promise<ToolExecutionResult> {\r\n if (calls.length === 0) return { items: [], records: [] };\r\n\r\n if (this.#allReadOnly(calls) && calls.length > 1) {\r\n return this.#executeParallel(calls);\r\n }\r\n return this.#executeSequential(calls);\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 内部\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * 判定这一批调用是否全部为只读工具。\r\n * 工具未找到时按\"只读\"处理(不会因为没注册就拒绝并行)。\r\n *\r\n * @pure 不修改任何状态\r\n */\r\n #allReadOnly(calls: ProviderToolCall[]): boolean {\r\n return calls.every((tc) => {\r\n const tool = this.#registry.get(tc.name);\r\n return tool ? isReadOnly(tool.kind) : true;\r\n });\r\n }\r\n\r\n /**\r\n * 并行执行:按 #maxParallel 分批,每批内 Promise.all。\r\n * 用于\"全部只读 + 多于 1 个\"的场景。\r\n *\r\n * @param calls — 全部只读的工具调用\r\n * @returns items + records\r\n *\r\n * @sideEffect 调用 tool.execute\r\n */\r\n async #executeParallel(calls: ProviderToolCall[]): Promise<ToolExecutionResult> {\r\n const items: ToolExecutionItem[] = [];\r\n const records: ToolCallRecord[] = [];\r\n\r\n for (let i = 0; i < calls.length; i += this.#maxParallel) {\r\n const batch = calls.slice(i, i + this.#maxParallel);\r\n const results = await Promise.all(batch.map((tc) => this.#executeOne(tc)));\r\n for (const r of results) {\r\n items.push(r.item);\r\n if (!r.record.success) records.push(r.record);\r\n }\r\n }\r\n return { items, records };\r\n }\r\n\r\n /**\r\n * 串行执行:按 calls 顺序逐个执行。\r\n * 用于含写工具的场景(保证副作用顺序与 LLM 给出顺序一致)。\r\n *\r\n * @param calls — 工具调用列表\r\n * @returns items + records\r\n *\r\n * @sideEffect 按序调用 tool.execute\r\n */\r\n async #executeSequential(calls: ProviderToolCall[]): Promise<ToolExecutionResult> {\r\n const items: ToolExecutionItem[] = [];\r\n const records: ToolCallRecord[] = [];\r\n for (const tc of calls) {\r\n const r = await this.#executeOne(tc);\r\n items.push(r.item);\r\n if (!r.record.success) records.push(r.record);\r\n }\r\n return { items, records };\r\n }\r\n\r\n /**\r\n * 执行单个工具调用,包含查找 / 参数解析 / 权限门 / 写工具 preview / 异常捕获。\r\n *\r\n * 失败映射:\r\n * - 工具不存在或被禁用 → `TOOL_NOT_FOUND`\r\n * - 权限门拒绝 → `GATE_DENIED`\r\n * - 工具 execute 抛错 → `EXECUTION_ERROR`\r\n *\r\n * @param tc — 单个工具调用(含 id / name / arguments 字符串)\r\n * @returns { item, record } — item 喂回 Session;record 用于风暴检测\r\n *\r\n * @sideEffect 调 tool.execute、可能调 tool.preview;不抛错给调用方\r\n */\r\n async #executeOne(\r\n tc: ProviderToolCall,\r\n ): Promise<{ item: ToolExecutionItem; record: ToolCallRecord }> {\r\n const toolName = tc.name;\r\n const timestamp = Date.now();\r\n const tool = this.#registry.get(toolName);\r\n if (!tool) {\r\n const errMsg = `工具 \"${toolName}\" 不存在或已被禁用`;\r\n return {\r\n item: { name: toolName, callId: tc.id, result: { success: false, data: errMsg, error: \"TOOL_NOT_FOUND\" } },\r\n record: { name: toolName, success: false, error: \"TOOL_NOT_FOUND\", timestamp },\r\n };\r\n }\r\n\r\n // 解析参数:arguments 是 JSON 字符串,解析失败则降级为空对象(不阻塞对话)\r\n let toolArgs: unknown;\r\n try { toolArgs = tc.arguments ? JSON.parse(tc.arguments) : {}; } catch { toolArgs = {}; }\r\n\r\n const gateResult = await this.#gate.check(toolName, toolArgs);\r\n if (!gateResult) {\r\n const errMsg = `工具 \"${toolName}\" 被权限门拒绝`;\r\n return {\r\n item: { name: toolName, callId: tc.id, result: { success: false, data: errMsg, error: \"GATE_DENIED\" } },\r\n record: { name: toolName, success: false, error: \"GATE_DENIED\", timestamp },\r\n };\r\n }\r\n\r\n // 写工具的 preview:用于在执行前预热 / 副作用预览(仅写工具支持)\r\n if (!isReadOnly(tool.kind)) {\r\n const maybePreview = (tool as { preview?: (args: unknown, ctx: ToolContext) => Promise<unknown> }).preview;\r\n if (typeof maybePreview === \"function\") {\r\n try { await maybePreview(toolArgs, this.#baseCtx); } catch { /* ignore preview errors */ }\r\n }\r\n }\r\n\r\n try {\r\n const result = await this.#invokeTool(tool, toolArgs);\r\n return {\r\n item: { name: toolName, callId: tc.id, result },\r\n record: { name: toolName, success: result.success, error: result.error, timestamp },\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n const errorResult: ToolResult = {\r\n success: false,\r\n data: `工具 \"${toolName}\" 执行异常:${message}`,\r\n error: \"EXECUTION_ERROR\",\r\n };\r\n return {\r\n item: { name: toolName, callId: tc.id, result: errorResult },\r\n record: { name: toolName, success: false, error: \"EXECUTION_ERROR\", timestamp },\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * 真正调 tool.execute 的薄包装(拆出来便于以后插入横切关注点,如重试 / 计时)。\r\n *\r\n * @param tool — 类型擦除后的工具\r\n * @param args — 解析后的参数(unknown)\r\n * @returns 工具的 ToolResult\r\n *\r\n * @sideEffect 调 tool.execute\r\n */\r\n async #invokeTool(tool: AnyAgentTool, args: unknown): Promise<ToolResult> {\r\n return tool.execute(args, this.#baseCtx);\r\n }\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// StormDetector — 纯判断层:检测「同一工具连续失败」风暴\r\n//\r\n// 设计原则:\r\n// - 纯函数 / 纯类:输入历史 records + 当前 calls → boolean\r\n// - 不持有 Session 状态,不写 messages\r\n// - 触发条件:最近 N 条失败记录「工具名 + 错误码」完全相同 且 当前轮仍调用该工具\r\n//\r\n// 函数注释规范见仓库根 AGENTS.md「函数注释规范」一节。\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type { ProviderToolCall } from \"../provider/index.js\";\r\nimport type { ToolCallRecord } from \"../tool/types.js\";\r\n\r\n/**\r\n * 风暴判定选项。\r\n *\r\n * @field threshold — 触发风暴需要的最少连续失败次数(默认 3)\r\n */\r\nexport interface StormDetectorOptions {\r\n /** 触发风暴需要的最少连续失败次数(默认 3) */\r\n threshold?: number;\r\n}\r\n\r\n/**\r\n * StormDetector:纯判断「本轮是否应该中断风暴」。\r\n *\r\n * 判定规则(按顺序检查,任一不满足则返回 false):\r\n * 1. 历史失败记录数 ≥ threshold\r\n * 2. 取最后 threshold 条记录,必须全部失败,且工具名 / 错误码完全一致\r\n * 3. 当前轮至少有 1 个工具名匹配那个失败工具\r\n *\r\n * 用途:当 LLM 在同一工具上反复撞同一错误时,强制提示\"切换策略\",\r\n * 避免无限循环。\r\n */\r\nexport class StormDetector {\r\n /** 触发风暴需要的最少连续失败次数(默认 3) */\r\n readonly #threshold: number;\r\n\r\n /**\r\n * 构造一个 StormDetector。\r\n *\r\n * @param opts — 判定选项(threshold)\r\n * @pure 仅保存配置\r\n */\r\n constructor(opts: StormDetectorOptions = {}) {\r\n this.#threshold = opts.threshold ?? 3;\r\n }\r\n\r\n /**\r\n * 判定本轮是否应该中断风暴。\r\n *\r\n * @param recentRecords — 历史的失败记录(不含成功的;通常来自 ToolExecutor 的 records)\r\n * @param currentCalls — 本轮 LLM 决定调用的工具\r\n * @returns true 表示触发风暴,调用方应放弃本轮工具执行并提示切换策略\r\n *\r\n * @pure 不修改任何状态,仅做比较判断\r\n */\r\n shouldBreak(recentRecords: readonly ToolCallRecord[], currentCalls: readonly ProviderToolCall[]): boolean {\r\n if (recentRecords.length < this.#threshold) return false;\r\n const lastN = recentRecords.slice(-this.#threshold);\r\n if (lastN.length < this.#threshold) return false;\r\n\r\n const first = lastN[0]!;\r\n const allSame = lastN.every((r) => r.name === first.name && r.error === first.error && !r.success);\r\n if (!allSame) return false;\r\n\r\n return currentCalls.some((tc) => tc.name === first.name);\r\n }\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// Reflector — 失败归因 → 改写下一轮 prompt\r\n//\r\n// 设计原则:\r\n// - 纯类:输入(工具执行结果 + 历史 + 上下文)→ 输出(反射列表 / 拼装后的 prompt)\r\n// - 不持有 Session / messages 状态\r\n// - 5 条规则(按优先级检查,先匹配先返回,单条目不重复触发):\r\n// R1 连续失败 — 同一工具连续 ≥threshold 次相同错误码失败\r\n// R2 文件不存在 — TOOL_NOT_FOUND 或 ENOENT / not found / No such file\r\n// R2.5 文本未找到 — TEXT_NOT_FOUND / TEXT_MULTIPLE_MATCHES(编辑工具高频失败)\r\n// R3 权限拒绝 — GATE_DENIED 或 EACCES / permission / denied\r\n// R4 写根外 — kind ∈ Edit/Delete/Move 且错误码为 OUTSIDE_WRITE_ROOTS\r\n// - 注入策略:拼到 system prompt 尾部,不修改 messages\r\n//\r\n// 函数注释规范见仓库根 AGENTS.md「函数注释规范」一节。\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { ToolKind, type ToolResult } from \"../tool/types.js\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 类型\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 反射类别(与日志里的 category 字段对齐) */\r\nexport type ReflectionCategory =\r\n | \"repeated_failure\"\r\n | \"file_not_found\"\r\n | \"text_not_found\"\r\n | \"permission_denied\"\r\n | \"out_of_write_root\";\r\n\r\n/** 单条反射 */\r\nexport interface Reflection {\r\n /** 给 LLM 看的中文提示 */\r\n hint: string;\r\n /** 反射分类(用于日志 / 调试) */\r\n category: ReflectionCategory;\r\n /** 命中的工具名(便于 UI 展示) */\r\n toolName: string;\r\n}\r\n\r\n/** Reflector 配置 */\r\nexport interface ReflectorOptions {\r\n /** 触发 R1 需要的最小连续失败次数(默认 2) */\r\n repeatThreshold?: number;\r\n /** 注入到 prompt 的最大反射数(默认 5) */\r\n maxReflections?: number;\r\n /** 单条 hint 的最大字符数(默认 800) */\r\n maxHintChars?: number;\r\n}\r\n\r\n/** analyze() 接收的单条工具结果 */\r\nexport interface AnalyzeItem {\r\n /** 工具名 */\r\n name: string;\r\n /** 工具执行结果 */\r\n result: ToolResult;\r\n /** 工具分类(从 registry 拿,未注册时为 Other) */\r\n kind: ToolKind;\r\n /** 该工具在本轮之前的失败历史(仅失败项;用于 R1) */\r\n recentSameTool: Array<{ success: boolean; error?: string }>;\r\n}\r\n\r\n/** analyze() 接收的上下文 */\r\nexport interface AnalyzeContext {\r\n /** 写工具允许的根目录列表(用于 R4 hint) */\r\n writeRoots: string[];\r\n /** 当前工作目录(用于 R2 hint) */\r\n cwd: string;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Reflector\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Reflector:把本轮工具执行的失败结果归因为结构化反射,拼到下一轮 prompt。\r\n *\r\n * 行为:\r\n * - analyze:每个失败项最多产生 1 条反射(按 R1 → R2 → R3 → R4 顺序检查,先命中先用)\r\n * - injectIntoPrompt:把反射数组拼成\"反思\"section 追加到 system prompt 末尾\r\n *\r\n * 不写 messages,不调 LLM,不持有可变状态。\r\n */\r\nexport class Reflector {\r\n /** 触发 R1 需要的最小连续失败次数 */\r\n readonly #repeatThreshold: number;\r\n /** 注入到 prompt 的最大反射数 */\r\n readonly #maxReflections: number;\r\n /** 单条 hint 的最大字符数 */\r\n readonly #maxHintChars: number;\r\n\r\n /**\r\n * 构造一个 Reflector。\r\n *\r\n * @param options — 限流与阈值配置\r\n * @pure 仅保存配置\r\n */\r\n constructor(options: ReflectorOptions = {}) {\r\n this.#repeatThreshold = options.repeatThreshold ?? 2;\r\n this.#maxReflections = options.maxReflections ?? 5;\r\n this.#maxHintChars = options.maxHintChars ?? 800;\r\n }\r\n\r\n /**\r\n * 分析一批工具执行结果,返回要注入的反射列表。\r\n *\r\n * 行为:\r\n * - 成功项直接跳过\r\n * - 失败项按 R1 → R2 → R3 → R4 顺序检查,先命中先用,单条目最多产生 1 条\r\n * - 总数超过 maxReflections 时截断到前 N 条\r\n *\r\n * @param items — 本轮工具执行结果列表\r\n * @param ctx — 上下文(writeRoots / cwd,用于生成 hint)\r\n * @returns 反射列表(可能为空;最多 maxReflections 条)\r\n *\r\n * @pure 不修改任何状态\r\n */\r\n analyze(items: ReadonlyArray<AnalyzeItem>, ctx: AnalyzeContext): Reflection[] {\r\n const reflections: Reflection[] = [];\r\n for (const item of items) {\r\n if (item.result.success) continue;\r\n const r =\r\n this.#ruleRepeatedFailure(item) ??\r\n this.#ruleFileNotFound(item) ??\r\n this.#ruleTextNotFound(item) ??\r\n this.#rulePermissionDenied(item) ??\r\n this.#ruleOutOfWriteRoot(item, ctx.writeRoots);\r\n if (r) reflections.push(r);\r\n if (reflections.length >= this.#maxReflections) break;\r\n }\r\n return reflections;\r\n }\r\n\r\n /**\r\n * 把反射列表拼成\"反思\"section,追加到 system prompt 末尾。\r\n *\r\n * 输出格式:\r\n * ```\r\n * {原 prompt}\r\n *\r\n * ## ⚠️ 上一轮工具调用的反思\r\n * - 工具 `<tool>`:<hint>\r\n * - 工具 `<tool>`:<hint>\r\n * ```\r\n *\r\n * @param systemPrompt — 原始 system prompt\r\n * @param reflections — 反射列表(空数组时原样返回)\r\n * @returns 拼装后的新 prompt\r\n *\r\n * @pure 不修改入参\r\n */\r\n injectIntoPrompt(systemPrompt: string, reflections: Reflection[]): string {\r\n if (reflections.length === 0) return systemPrompt;\r\n const lines = reflections.map((r) => {\r\n const hint = this.#truncate(r.hint);\r\n return `- 工具 \\`${r.toolName}\\` 失败:${hint}`;\r\n });\r\n const section = [\r\n \"## ⚠️ 上一轮工具调用的反思\",\r\n \"以下是本轮工具执行失败的原因分析。请在下一次调用前先解决这些问题,不要重复相同的错误调用。\",\r\n ...lines,\r\n ].join(\"\\n\");\r\n return systemPrompt + \"\\n\\n\" + section;\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 规则实现(按 R1 → R2 → R3 → R4 顺序)\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * R1 连续失败:同一工具连续 ≥#repeatThreshold 次相同错误码失败。\r\n *\r\n * @pure 仅读入参\r\n */\r\n #ruleRepeatedFailure(item: AnalyzeItem): Reflection | null {\r\n const recent = item.recentSameTool;\r\n if (recent.length < this.#repeatThreshold) return null;\r\n const lastN = recent.slice(-this.#repeatThreshold);\r\n const first = lastN[0]!;\r\n if (!lastN.every((r) => !r.success && r.error === first.error)) {\r\n return null;\r\n }\r\n const errText = first.error ?? \"未知错误\";\r\n return {\r\n category: \"repeated_failure\",\r\n toolName: item.name,\r\n hint: `你已连续 ${this.#repeatThreshold} 次调用 \\`${item.name}\\` 失败(错误:${errText})。请先 \\`read_file\\` 看清现场或换其他工具,不要继续同样的错误调用。`,\r\n };\r\n }\r\n\r\n /**\r\n * R2 文件不存在:TOOL_NOT_FOUND 错误码 或 data 含 ENOENT / not found / No such file。\r\n *\r\n * @pure 仅读入参\r\n */\r\n #ruleFileNotFound(item: AnalyzeItem): Reflection | null {\r\n const isToolNotFound = item.result.error === \"TOOL_NOT_FOUND\";\r\n const data = item.result.data ?? \"\";\r\n const hasKeyword =\r\n data.includes(\"ENOENT\") ||\r\n data.includes(\"No such file\") ||\r\n /\\bnot found\\b/i.test(data);\r\n if (!isToolNotFound && !hasKeyword) return null;\r\n return {\r\n category: \"file_not_found\",\r\n toolName: item.name,\r\n hint: `\\`${item.name}\\` 失败:文件不存在。请先用 \\`ls\\` / \\`glob\\` 看一下目录结构,或确认路径拼写(当前工作目录见 system prompt)。`,\r\n };\r\n }\r\n\r\n /**\r\n * R2.5 文本未找到:edit_file / multi_edit 报 TEXT_NOT_FOUND 或 TEXT_MULTIPLE_MATCHES。\r\n *\r\n * 这是编辑工具最高频的失败。工具内部已对 CRLF 做归一化,仍报此错通常是\r\n * old_text 与当前文件内容不一致(缩进 / 已被改过 / 多次出现)。给一条明确\r\n * 的「重新读文件、逐字复制」指引,避免模型用同一份旧 old_text 反复重试。\r\n *\r\n * @pure 仅读入参\r\n */\r\n #ruleTextNotFound(item: AnalyzeItem): Reflection | null {\r\n const isTextErr =\r\n item.result.error === \"TEXT_NOT_FOUND\" ||\r\n item.result.error === \"TEXT_MULTIPLE_MATCHES\";\r\n if (!isTextErr) return null;\r\n const multiple = item.result.error === \"TEXT_MULTIPLE_MATCHES\";\r\n const hint = multiple\r\n ? `\\`${item.name}\\`:old_text 在文件中出现多次。请补充上下文让 old_text 唯一,或改用 multi_edit 的 replaceAll。`\r\n : `\\`${item.name}\\`:未找到 old_text。请重新 \\`read_file\\` 看当前内容,逐字复制要替换的片段(注意缩进与空格);行尾已做 CRLF 归一化,无需手写 \\\\r\\\\n。`;\r\n return {\r\n category: \"text_not_found\",\r\n toolName: item.name,\r\n hint,\r\n };\r\n }\r\n\r\n /**\r\n * R3 权限拒绝:GATE_DENIED 错误码 或 data 含 EACCES / permission / denied(不区分大小写)。\r\n *\r\n * @pure 仅读入参\r\n */\r\n #rulePermissionDenied(item: AnalyzeItem): Reflection | null {\r\n const isGateDenied = item.result.error === \"GATE_DENIED\";\r\n const data = item.result.data ?? \"\";\r\n const hasKeyword =\r\n data.includes(\"EACCES\") || /\\bpermission\\b/i.test(data) || /\\bdenied\\b/i.test(data);\r\n if (!isGateDenied && !hasKeyword) return null;\r\n return {\r\n category: \"permission_denied\",\r\n toolName: item.name,\r\n hint: `\\`${item.name}\\` 被权限系统拒绝。请检查:1) 路径是否在允许根目录内;2) 是否需要切换授权模式;3) 改用其他不需要权限的工具。`,\r\n };\r\n }\r\n\r\n /**\r\n * R4 写根外:kind ∈ Edit/Delete/Move 且错误码为 OUTSIDE_WRITE_ROOTS。\r\n *\r\n * 仅在工具本身明确报「路径超出写根」时才命中。旧实现只要写类工具失败就\r\n * 触发,会把 TEXT_NOT_FOUND / EXECUTION_ERROR 等误报为「写根外」,注入\r\n * 误导性提示(见会话日志 e65f0205 round 13:edit_file 因 CRLF 失配返回\r\n * TEXT_NOT_FOUND,却被反射为 out_of_write_root)。\r\n *\r\n * @pure 仅读入参\r\n */\r\n #ruleOutOfWriteRoot(item: AnalyzeItem, writeRoots: readonly string[]): Reflection | null {\r\n const isWriteKind =\r\n item.kind === ToolKind.Edit ||\r\n item.kind === ToolKind.Delete ||\r\n item.kind === ToolKind.Move;\r\n if (!isWriteKind) return null;\r\n if (item.result.error !== \"OUTSIDE_WRITE_ROOTS\") return null;\r\n const primary = writeRoots[0] ?? \"<未配置>\";\r\n return {\r\n category: \"out_of_write_root\",\r\n toolName: item.name,\r\n hint: `\\`${item.name}\\` 是写操作但目标路径不在允许的根目录内。请改用 \\`${primary}\\` 下的路径,或调整项目的 \\`writeRoots\\` 配置。`,\r\n };\r\n }\r\n\r\n /**\r\n * 截断超长 hint,末尾加省略号。\r\n *\r\n * @pure 字符串处理\r\n */\r\n #truncate(text: string): string {\r\n if (text.length <= this.#maxHintChars) return text;\r\n return text.slice(0, this.#maxHintChars - 3) + \"...\";\r\n }\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// buildToolDefinitions — 纯计算层:把 ToolRegistry 转成 Provider 需要的工具定义\r\n//\r\n// 设计原则:\r\n// - 纯函数:无副作用,输入 registry + mode → ToolDefinition[]\r\n// - mode 为 \"plan\" 时只暴露只读工具,强制走「只读 → 计划」模式\r\n//\r\n// 函数注释规范见仓库根 AGENTS.md「函数注释规范」一节。\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type { ToolDefinition } from \"../provider/index.js\";\r\nimport type { ToolRegistry } from \"../tool/registry.js\";\r\nimport type { SessionMode } from \"./types.js\";\r\n\r\n/**\r\n * 构建 Provider 工具定义(喂给 provider.chat() 的 tools 字段)。\r\n *\r\n * 行为:\r\n * - mode === \"plan\":只列出只读工具(read_file / list_dir / grep / ...)\r\n * - mode === \"code\":列出全部启用工具\r\n *\r\n * @param registry — 工具注册表\r\n * @param mode — 会话模式(\"plan\" → 只读;\"code\" → 全部)\r\n * @returns Provider 兼容的 ToolDefinition 数组\r\n *\r\n * @pure 无副作用:仅读 registry 状态,不修改任何东西\r\n */\r\nexport function buildToolDefinitions(\r\n registry: ToolRegistry,\r\n mode: SessionMode,\r\n): ToolDefinition[] {\r\n const tools = mode === \"plan\" ? registry.listReadTools() : registry.list();\r\n return tools.map((t) => ({\r\n type: \"function\" as const,\r\n function: {\r\n name: t.name,\r\n description: t.description,\r\n parameters: t.parameters as unknown as Record<string, unknown>,\r\n },\r\n }));\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// TodoList — Harness 规划层的最小骨架\r\n//\r\n// 设计原则:\r\n// - 纯数据 + 纯方法(除状态转换外不调任何 IO)\r\n// - 状态机:pending → running → done | failed | skipped\r\n// - 依赖关系:todo 声明 deps(其它 todo 的 id),deps 全 done 才进入 pending\r\n// - 输出:toMarkdown() 拼到 system prompt,让模型每轮看到自己进度\r\n//\r\n// 函数注释规范见仓库根 AGENTS.md「函数注释规范」一节。\r\n// ---------------------------------------------------------------------------\r\n\r\n/** Todo 状态 */\r\nexport type TodoStatus = \"pending\" | \"running\" | \"done\" | \"failed\" | \"skipped\";\r\n\r\n/** Todo 项 */\r\nexport interface TodoItem {\r\n /** 唯一 id(生成时分配 0/1/2...) */\r\n id: number;\r\n /** 给 LLM 看的人类可读步骤描述(中文) */\r\n content: string;\r\n /** 当前状态 */\r\n status: TodoStatus;\r\n /** 依赖的其它 todo id 列表(这些都 done 后本项才可 running) */\r\n deps: number[];\r\n /** 完成时的证据(如 \"type-check 通过\"、\"read_file 拿到 8633 字符\");失败时为错误信息 */\r\n evidence?: string;\r\n /** 状态最近一次变更的时间戳(毫秒) */\r\n updatedAt: number;\r\n}\r\n\r\n/**\r\n * TodoList — 任务列表状态机。\r\n *\r\n * 用法(典型流程):\r\n * const todo = new TodoList();\r\n * const a = todo.add(\"读 edit-file.ts\");\r\n * const b = todo.add(\"修改错误信息\", [a]);\r\n * const c = todo.add(\"跑 type-check\", [b]);\r\n * todo.markRunning(a); todo.markDone(a, \"读取成功\");\r\n * // ... 依次推进\r\n *\r\n * 状态转换:\r\n * pending → running (markRunning,前提是 deps 全 done)\r\n * running → done (markDone,附 evidence)\r\n * running → failed (markFailed,附 reason)\r\n * pending → skipped (markSkipped,附 reason)\r\n */\r\nexport class TodoList {\r\n #items: TodoItem[] = [];\r\n #nextId = 0;\r\n\r\n /**\r\n * 新增一个 todo,返回分配的 id。\r\n *\r\n * @param content — 步骤描述(中文)\r\n * @param deps — 依赖的 todo id 列表;为空表示无依赖,立即可 running\r\n * @returns 新 todo 的 id\r\n * @throws 当 deps 引用了不存在的 id 时抛出,避免 LLM 错把不存在的依赖塞进计划\r\n */\r\n add(content: string, deps: number[] = []): number {\r\n // 校验 deps:所有 id 必须已存在(避免引用未来 / 不存在的 id)\r\n for (const d of deps) {\r\n if (!this.#items.find((it) => it.id === d)) {\r\n throw new Error(`todo 依赖 #${d} 不存在(已分配的 id: ${this.#items.map((it) => it.id).join(\", \") || \"(空)\"})`);\r\n }\r\n }\r\n const id = this.#nextId++;\r\n this.#items.push({\r\n id,\r\n content,\r\n status: \"pending\",\r\n deps: [...deps],\r\n updatedAt: Date.now(),\r\n });\r\n return id;\r\n }\r\n\r\n /**\r\n * 把失败的 todo 重置回 pending(用于\"重试\"工作流)。\r\n *\r\n * 不允许把 done / skipped 重置(一旦完成不应反悔)。\r\n * 不允许把 running 重置(应该先 markFailed 再重试)。\r\n *\r\n * @param id — todo id\r\n * @returns 是否成功\r\n */\r\n resetForRetry(id: number): boolean {\r\n const item = this.#find(id);\r\n if (!item) return false;\r\n if (item.status !== \"failed\") return false;\r\n item.status = \"pending\";\r\n item.evidence = undefined;\r\n item.updatedAt = Date.now();\r\n return true;\r\n }\r\n\r\n /**\r\n * 把 todo 标记为 running。\r\n *\r\n * 前提:该 todo 存在 + 当前是 pending + 依赖全 done。\r\n * 不满足时静默返回 false(不抛错,避免污染 agent 主循环)。\r\n *\r\n * @param id — todo id\r\n * @returns 是否成功转换\r\n */\r\n markRunning(id: number): boolean {\r\n const item = this.#find(id);\r\n if (!item) return false;\r\n if (item.status !== \"pending\") return false;\r\n if (!this.#depsAllDone(item.deps)) return false;\r\n item.status = \"running\";\r\n item.updatedAt = Date.now();\r\n return true;\r\n }\r\n\r\n /**\r\n * 标记完成。\r\n *\r\n * @param id — todo id\r\n * @param evidence — 完成证据(如 \"读取成功\"、\"type-check 通过\")\r\n * @returns 是否成功\r\n */\r\n markDone(id: number, evidence?: string): boolean {\r\n const item = this.#find(id);\r\n if (!item) return false;\r\n if (item.status !== \"running\" && item.status !== \"pending\") return false;\r\n item.status = \"done\";\r\n if (evidence !== undefined) item.evidence = evidence;\r\n item.updatedAt = Date.now();\r\n return true;\r\n }\r\n\r\n /**\r\n * 标记失败。\r\n *\r\n * @param id — todo id\r\n * @param reason — 失败原因(人类可读)\r\n * @returns 是否成功\r\n */\r\n markFailed(id: number, reason: string): boolean {\r\n const item = this.#find(id);\r\n if (!item) return false;\r\n if (item.status === \"done\") return false;\r\n item.status = \"failed\";\r\n item.evidence = reason;\r\n item.updatedAt = Date.now();\r\n return true;\r\n }\r\n\r\n /**\r\n * 标记跳过。\r\n *\r\n * @param id — todo id\r\n * @param reason — 跳过原因(如 \"项目无测试基建\")\r\n * @returns 是否成功\r\n */\r\n markSkipped(id: number, reason: string): boolean {\r\n const item = this.#find(id);\r\n if (!item) return false;\r\n if (item.status === \"done\" || item.status === \"failed\") return false;\r\n item.status = \"skipped\";\r\n item.evidence = reason;\r\n item.updatedAt = Date.now();\r\n return true;\r\n }\r\n\r\n /**\r\n * 取出所有\"立即可做\"的 todo(deps 全 done 且 pending)。\r\n *\r\n * @returns 可执行的 todo 列表\r\n */\r\n pending(): TodoItem[] {\r\n return this.#items.filter(\r\n (it) => it.status === \"pending\" && this.#depsAllDone(it.deps),\r\n );\r\n }\r\n\r\n /**\r\n * 取出所有未完成项(pending / running / failed)。\r\n */\r\n unfinished(): TodoItem[] {\r\n return this.#items.filter(\r\n (it) => it.status === \"pending\" || it.status === \"running\" || it.status === \"failed\",\r\n );\r\n }\r\n\r\n /**\r\n * 是否全部结束(done / failed / skipped)。\r\n */\r\n isAllTerminated(): boolean {\r\n return this.#items.every(\r\n (it) => it.status === \"done\" || it.status === \"failed\" || it.status === \"skipped\",\r\n );\r\n }\r\n\r\n /** 全部条目(只读快照) */\r\n get items(): ReadonlyArray<TodoItem> {\r\n return this.#items;\r\n }\r\n\r\n /**\r\n * 把 todo 列表拼成 markdown,用于注入 system prompt。\r\n *\r\n * @param maxItems — 最多展示多少条;超出时保留\"最近 N 条 + 进行中\",折叠已完成\r\n * @returns markdown 字符串\r\n */\r\n toMarkdown(maxItems = 20): string {\r\n if (this.#items.length === 0) return \"\";\r\n\r\n // 截断策略:保留所有 running/pending/failed(活跃项),剩下的按时间倒序补到 maxItems\r\n const active = this.#items.filter(\r\n (it) => it.status === \"pending\" || it.status === \"running\" || it.status === \"failed\",\r\n );\r\n const finished = this.#items.filter(\r\n (it) => it.status === \"done\" || it.status === \"skipped\",\r\n );\r\n const finishedSorted = [...finished].sort((a, b) => b.updatedAt - a.updatedAt);\r\n\r\n const truncated = finishedSorted.length > maxItems;\r\n const finishedToShow = truncated\r\n ? finishedSorted.slice(0, Math.max(0, maxItems - active.length))\r\n : finishedSorted;\r\n\r\n const lines = [...active, ...finishedToShow].map((it) => {\r\n const box = this.#statusBox(it.status);\r\n const depStr = it.deps.length > 0 ? ` (依赖: #${it.deps.join(\", #\")})` : \"\";\r\n const eviStr = it.evidence ? ` — ${it.evidence}` : \"\";\r\n return `- ${box} #${it.id} ${it.content}${depStr}${eviStr}`;\r\n });\r\n if (truncated) {\r\n const hidden = finishedSorted.length - finishedToShow.length;\r\n lines.push(`- ...(另有 ${hidden} 条已完成已折叠)`);\r\n }\r\n\r\n return [\r\n \"## 📋 当前任务进度\",\r\n ...lines,\r\n \"\",\r\n \"规则:按顺序推进;未完成依赖时不要跳步;完成请调用 todo_mark_done,失败请调用 todo_mark_failed;重试失败项先调 todo_retry 再重跑。\",\r\n ].join(\"\\n\");\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 内部\r\n // -------------------------------------------------------------------------\r\n\r\n #find(id: number): TodoItem | undefined {\r\n return this.#items.find((it) => it.id === id);\r\n }\r\n\r\n #depsAllDone(deps: readonly number[]): boolean {\r\n return deps.every((d) => {\r\n const item = this.#items.find((it) => it.id === d);\r\n return item?.status === \"done\" || item?.status === \"skipped\";\r\n });\r\n }\r\n\r\n #statusBox(status: TodoStatus): string {\r\n switch (status) {\r\n case \"pending\": return \"☐\";\r\n case \"running\": return \"▶\";\r\n case \"done\": return \"✅\";\r\n case \"failed\": return \"❌\";\r\n case \"skipped\": return \"⏭\";\r\n }\r\n }\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// Harness 工具 — 把 TodoList 暴露给 LLM 主动调用的工具集\r\n//\r\n// 设计:\r\n// - 3 个工具:todo_add / todo_mark_done / todo_mark_failed\r\n// - 每个工具都接收一个 Session-side 的 TodoList 引用(通过闭包)\r\n// - 不让 LLM 直接读 todo(用 system prompt 注入,避免增加决策负担)\r\n//\r\n// 函数注释规范见仓库根 AGENTS.md「函数注释规范」一节。\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { ToolKind, type AgentTool, type ToolResult } from \"../tool/types.js\";\r\nimport type { TodoList } from \"./todo-list.js\";\r\n\r\n/**\r\n * 把 TodoList 包装成 5 个 AgentTool,供 Session 注入到 ToolRegistry。\r\n *\r\n * 工具集:\r\n * - `todo_add` — 添加新步骤\r\n * - `todo_mark_running` — 把 pending 推进到 running(依赖必须已 done/skipped)\r\n * - `todo_mark_done` — 把 running 标记完成(不允许从 pending 跳过 running)\r\n * - `todo_mark_failed` — 把 running 标记失败(不允许从 pending 跳过 running)\r\n * - `todo_retry` — 把 failed 重置回 pending\r\n *\r\n * @param todoList — 共享的 TodoList 实例\r\n * @returns 5 个工具的数组\r\n */\r\nexport function createHarnessTools(todoList: TodoList): AgentTool<unknown>[] {\r\n return [\r\n makeTodoAddTool(todoList),\r\n makeTodoMarkRunningTool(todoList),\r\n makeTodoMarkDoneTool(todoList),\r\n makeTodoMarkFailedTool(todoList),\r\n makeTodoRetryTool(todoList),\r\n ];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// todo_add\r\n// ---------------------------------------------------------------------------\r\n\r\ninterface TodoAddArgs {\r\n /** 步骤描述(中文) */\r\n content: string;\r\n /** 依赖的 todo id 列表(可选) */\r\n deps?: number[];\r\n}\r\n\r\nfunction makeTodoAddTool(todoList: TodoList): AgentTool<TodoAddArgs> {\r\n return {\r\n name: \"todo_add\",\r\n kind: ToolKind.Other,\r\n description:\r\n \"⚠️ 【复杂任务第一步】添加一个待办到当前任务进度。\\n\\n\" +\r\n \"什么情况下调我:\\n\" +\r\n \" - 需要多步才能完成(改多个文件 / 跨多轮)\\n\" +\r\n \" - 需先读后改(edit/write 之前)\\n\" +\r\n \" - 依赖外部状态(文件、API、运行结果)\\n\\n\" +\r\n \"什么情况下不要调:\\n\" +\r\n \" - 一句话能完成的事(查、列、问)\\n\\n\" +\r\n \"用法:每次加一个 step,多步就调多次。有依赖关系传 deps=[前步 id]。返回分配的 id,后续用 todo_mark_done / todo_mark_failed 引用。\\n\\n\" +\r\n \"示例:用户说「把 X 改成 Y」→ 调 1) todo_add(\\\"读 X\\\") 2) todo_add(\\\"改 X\\\", deps=[0]) 3) todo_add(\\\"检查改动\\\", deps=[1])\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n content: { type: \"string\", description: \"步骤描述(中文,建议简洁)\" },\r\n deps: {\r\n type: \"array\",\r\n description: \"依赖的 todo id 数组(这些都 done 后本项才可执行)\",\r\n items: { type: \"number\" },\r\n },\r\n },\r\n required: [\"content\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args): Promise<ToolResult> {\r\n if (typeof args?.content !== \"string\" || args.content.length === 0) {\r\n return { success: false, data: \"缺少 content 参数\", error: \"INVALID_ARGS\" };\r\n }\r\n const deps = Array.isArray(args.deps) ? args.deps.filter((d) => typeof d === \"number\") : [];\r\n try {\r\n const id = todoList.add(args.content, deps);\r\n return {\r\n success: true,\r\n data: `已添加 todo #${id}:${args.content}${deps.length > 0 ? `(依赖: ${deps.join(\", \")})` : \"\"}`,\r\n summary: `📋 添加 #${id}`,\r\n };\r\n } catch (err) {\r\n return {\r\n success: false,\r\n data: err instanceof Error ? err.message : String(err),\r\n error: \"TODO_DEPS_INVALID\",\r\n };\r\n }\r\n },\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// todo_mark_running\r\n// ---------------------------------------------------------------------------\r\n\r\ninterface TodoMarkRunningArgs {\r\n id: number;\r\n}\r\n\r\n/**\r\n * 把 pending 推进到 running。\r\n *\r\n * 不允许从其他状态转换(避免破坏依赖语义:必须先 mark_running,再 mark_done/failed)。\r\n */\r\nfunction makeTodoMarkRunningTool(todoList: TodoList): AgentTool<TodoMarkRunningArgs> {\r\n return {\r\n name: \"todo_mark_running\",\r\n kind: ToolKind.Other,\r\n description:\r\n \"把 todo 从 pending 推进到 running,表示你「正在做」这一步。\\n\" +\r\n \"前提:该 todo 存在、当前是 pending、且所有 deps 已 done 或 skipped。\\n\" +\r\n \"工作流:先调 todo_mark_running,再去调实际的工具(read_file / edit_file 等),最后用 todo_mark_done 或 todo_mark_failed 收尾。\\n\" +\r\n \"**不要跳过 mark_running 直接 mark_done** — 会破坏依赖检查,下游步骤可能误以为依赖已满足。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n id: { type: \"number\", description: \"todo 的 id\" },\r\n },\r\n required: [\"id\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args): Promise<ToolResult> {\r\n if (typeof args?.id !== \"number\") {\r\n return { success: false, data: \"缺少 id 参数\", error: \"INVALID_ARGS\" };\r\n }\r\n const item = todoList.items.find((it) => it.id === args.id);\r\n if (!item) {\r\n return { success: false, data: `todo #${args.id} 不存在`, error: \"TODO_NOT_FOUND\" };\r\n }\r\n if (item.status !== \"pending\") {\r\n return {\r\n success: false,\r\n data: `todo #${args.id} 当前状态是 ${item.status},只有 pending 可以推进到 running`,\r\n error: \"TODO_INVALID_STATE\",\r\n };\r\n }\r\n const ok = todoList.markRunning(args.id);\r\n if (!ok) {\r\n return {\r\n success: false,\r\n data: `todo #${args.id} 依赖未全部完成(依赖: #${item.deps.join(\", #\")})`,\r\n error: \"TODO_DEPS_NOT_READY\",\r\n };\r\n }\r\n return {\r\n success: true,\r\n data: `已开始 todo #${args.id}:${item.content}`,\r\n summary: `▶ 开始 #${args.id}`,\r\n };\r\n },\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// todo_mark_done\r\n// ---------------------------------------------------------------------------\r\n\r\ninterface TodoMarkDoneArgs {\r\n id: number;\r\n /** 可选:完成证据(一句话) */\r\n evidence?: string;\r\n}\r\n\r\n/**\r\n * 把 running 标记完成。\r\n *\r\n * 故意不允许从 pending 直接 done:必须先 todo_mark_running,避免 LLM 跳过「声明开始」这一步。\r\n */\r\nfunction makeTodoMarkDoneTool(todoList: TodoList): AgentTool<TodoMarkDoneArgs> {\r\n return {\r\n name: \"todo_mark_done\",\r\n kind: ToolKind.Other,\r\n description:\r\n \"把 todo 标记为完成。evidence 填「完成证据」(如「读到 120 行」、「typecheck 通过」、「edit 成功」),不填也行。\\n\" +\r\n \"前提:当前状态必须是 running。若还是 pending,先调 todo_mark_running。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n id: { type: \"number\", description: \"todo 的 id\" },\r\n evidence: { type: \"string\", description: \"完成证据(一句话,可选)\" },\r\n },\r\n required: [\"id\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args): Promise<ToolResult> {\r\n if (typeof args?.id !== \"number\") {\r\n return { success: false, data: \"缺少 id 参数\", error: \"INVALID_ARGS\" };\r\n }\r\n const item = todoList.items.find((it) => it.id === args.id);\r\n if (!item) {\r\n return { success: false, data: `todo #${args.id} 不存在`, error: \"TODO_NOT_FOUND\" };\r\n }\r\n if (item.status !== \"running\") {\r\n return {\r\n success: false,\r\n data: `todo #${args.id} 当前状态是 ${item.status},只有 running 可以 mark_done;如需重试请先 todo_retry`,\r\n error: \"TODO_INVALID_STATE\",\r\n };\r\n }\r\n const ok = todoList.markDone(args.id, args.evidence);\r\n if (!ok) {\r\n return { success: false, data: `todo #${args.id} 状态转换失败`, error: \"TODO_INVALID\" };\r\n }\r\n return {\r\n success: true,\r\n data: `已标记 todo #${args.id} 完成${args.evidence ? `:${args.evidence}` : \"\"}`,\r\n summary: `✅ 完成 #${args.id}`,\r\n };\r\n },\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// todo_mark_failed\r\n// ---------------------------------------------------------------------------\r\n\r\ninterface TodoMarkFailedArgs {\r\n id: number;\r\n /** 失败原因(一句话) */\r\n reason: string;\r\n}\r\n\r\n/**\r\n * 把 running 标记失败。\r\n *\r\n * 故意不允许从 pending 直接 failed:必须先 todo_mark_running。\r\n */\r\nfunction makeTodoMarkFailedTool(todoList: TodoList): AgentTool<TodoMarkFailedArgs> {\r\n return {\r\n name: \"todo_mark_failed\",\r\n kind: ToolKind.Other,\r\n description:\r\n \"把 todo 标记为失败,reason 填「失败原因」(如「old_text 拼错」、「文件不存在」)。\\n\" +\r\n \"前提:当前状态必须是 running。若还是 pending,先调 todo_mark_running。\\n\" +\r\n \"调用后该 step 状态变为 failed。想重试:调 todo_retry(id) 重置回 pending,再 mark_running → 跑工具 → mark_done。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n id: { type: \"number\", description: \"todo 的 id\" },\r\n reason: { type: \"string\", description: \"失败原因(一句话)\" },\r\n },\r\n required: [\"id\", \"reason\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args): Promise<ToolResult> {\r\n if (typeof args?.id !== \"number\") {\r\n return { success: false, data: \"缺少 id 参数\", error: \"INVALID_ARGS\" };\r\n }\r\n if (typeof args?.reason !== \"string\") {\r\n return { success: false, data: \"缺少 reason 参数\", error: \"INVALID_ARGS\" };\r\n }\r\n const item = todoList.items.find((it) => it.id === args.id);\r\n if (!item) {\r\n return { success: false, data: `todo #${args.id} 不存在`, error: \"TODO_NOT_FOUND\" };\r\n }\r\n if (item.status !== \"running\") {\r\n return {\r\n success: false,\r\n data: `todo #${args.id} 当前状态是 ${item.status},只有 running 可以 mark_failed`,\r\n error: \"TODO_INVALID_STATE\",\r\n };\r\n }\r\n const ok = todoList.markFailed(args.id, args.reason);\r\n if (!ok) {\r\n return { success: false, data: `todo #${args.id} 状态转换失败`, error: \"TODO_INVALID\" };\r\n }\r\n return {\r\n success: true,\r\n data: `已标记 todo #${args.id} 失败:${args.reason}`,\r\n summary: `❌ 失败 #${args.id}`,\r\n };\r\n },\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// todo_retry\r\n// ---------------------------------------------------------------------------\r\n\r\ninterface TodoRetryArgs {\r\n id: number;\r\n}\r\n\r\n/**\r\n * 把 failed 重置回 pending,用于「修完 bug 再重跑一次」。\r\n */\r\nfunction makeTodoRetryTool(todoList: TodoList): AgentTool<TodoRetryArgs> {\r\n return {\r\n name: \"todo_retry\",\r\n kind: ToolKind.Other,\r\n description:\r\n \"把失败的 todo 重置回 pending(清空 evidence),然后你可以重新走 todo_mark_running → 跑工具 → todo_mark_done 的流程。\\n\" +\r\n \"前提:当前状态必须是 failed。done / skipped / running 不允许重试(done 不可反悔、running 应先 mark_failed)。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n id: { type: \"number\", description: \"todo 的 id\" },\r\n },\r\n required: [\"id\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args): Promise<ToolResult> {\r\n if (typeof args?.id !== \"number\") {\r\n return { success: false, data: \"缺少 id 参数\", error: \"INVALID_ARGS\" };\r\n }\r\n const item = todoList.items.find((it) => it.id === args.id);\r\n if (!item) {\r\n return { success: false, data: `todo #${args.id} 不存在`, error: \"TODO_NOT_FOUND\" };\r\n }\r\n if (item.status !== \"failed\") {\r\n return {\r\n success: false,\r\n data: `todo #${args.id} 当前状态是 ${item.status},只有 failed 可以重试`,\r\n error: \"TODO_INVALID_STATE\",\r\n };\r\n }\r\n const ok = todoList.resetForRetry(args.id);\r\n if (!ok) {\r\n return { success: false, data: `todo #${args.id} 状态转换失败`, error: \"TODO_INVALID\" };\r\n }\r\n return {\r\n success: true,\r\n data: `已重置 todo #${args.id} 为 pending,可重新跑`,\r\n summary: `🔄 重试 #${args.id}`,\r\n };\r\n },\r\n };\r\n}\r\n","import { execFile } from \"node:child_process\";\r\nimport { promisify } from \"node:util\";\r\nimport type { Checkpoint } from \"./types.js\";\r\n\r\nconst execFileAsync = promisify(execFile);\r\nconst EXEC_OPTIONS = { windowsHide: true } as const;\r\n\r\nexport async function isGitRepo(cwd: string): Promise<boolean> {\r\n try {\r\n await execFileAsync(\"git\", [\"rev-parse\", \"--is-inside-work-tree\"], { cwd, ...EXEC_OPTIONS });\r\n return true;\r\n } catch { return false; }\r\n}\r\n\r\nasync function hasCommits(cwd: string): Promise<boolean> {\r\n try {\r\n await execFileAsync(\"git\", [\"rev-parse\", \"--verify\", \"HEAD\"], { cwd, ...EXEC_OPTIONS });\r\n return true;\r\n } catch { return false; }\r\n}\r\n\r\nasync function hasWorkingChanges(cwd: string): Promise<boolean> {\r\n const out = await execFileAsync(\"git\", [\"status\", \"--porcelain\"], { cwd, ...EXEC_OPTIONS });\r\n return out.stdout.trim().length > 0;\r\n}\r\n\r\nasync function git(args: string[], cwd: string): Promise<string> {\r\n try {\r\n const { stdout } = await execFileAsync(\"git\", args, { cwd, ...EXEC_OPTIONS });\r\n return stdout;\r\n } catch (err: unknown) {\r\n const msg = err instanceof Error ? err.message : String(err);\r\n throw new Error(`git ${args.join(\" \")} 失败: ${msg}`);\r\n }\r\n}\r\n\r\nasync function listStashShas(cwd: string): Promise<string[]> {\r\n const out = await git([\"stash\", \"list\", \"--format=%H\"], cwd);\r\n return out.split(\"\\n\").map((l) => l.trim()).filter(Boolean);\r\n}\r\n\r\nexport async function createCheckpoint(cwd: string): Promise<Checkpoint> {\r\n const timestamp = Date.now();\r\n const inRepo = await isGitRepo(cwd);\r\n if (!inRepo) return { stashSha: \"\", timestamp, cwd, isGitRepo: false };\r\n if (!(await hasCommits(cwd))) return { stashSha: \"\", timestamp, cwd, isGitRepo: true };\r\n if (!(await hasWorkingChanges(cwd))) return { stashSha: \"\", timestamp, cwd, isGitRepo: true };\r\n\r\n // 为了既登记检查点供 rewind 恢复、又**不**让工作区被 stash \"挪走\"\r\n // (`stash push` 默认会把工作区清空,导致后续对话看不到前面对话产生的修改 —— 这就是 bug),\r\n // 这里使用 `stash push -u` 暂存 + 立即 `stash apply` 还原到工作区。\r\n // stash entry 仍保留在 list 中,rewind 时按 SHA 仍可被精准 drop / apply。\r\n const beforeShas = await listStashShas(cwd);\r\n await git([\"stash\", \"push\", \"-m\", `dskcode-cp-${timestamp}`, \"-u\"], cwd);\r\n\r\n const newShas = await listStashShas(cwd);\r\n if (newShas.length === 0) throw new Error(\"git stash push 未能创建 stash entry\");\r\n const newSha = newShas.find((s) => !beforeShas.includes(s)) ?? newShas[0]!;\r\n\r\n // 立即把 stash 还原到工作区:这样前面对话产生的修改不会因为本次 chat 开始\r\n // 而被\"隐藏\"到 stash 里。stash entry 不会被 `apply` 消耗(仅 `pop`/`drop` 才会),\r\n // 所以后续 rewind 仍能基于 newSha 找回这个快照。\r\n await git([\"stash\", \"apply\", newSha], cwd);\r\n\r\n return { stashSha: newSha, timestamp, cwd, isGitRepo: true };\r\n}\r\n\r\nexport async function restoreCheckpointForce(checkpoint: Checkpoint): Promise<void> {\r\n if (!checkpoint.isGitRepo) throw new Error(\"非 git 仓库,无法恢复文件状态\");\r\n if (!checkpoint.stashSha) throw new Error(\"检查点为空(工作区原本就干净),无需恢复\");\r\n\r\n const { cwd, stashSha } = checkpoint;\r\n const currentShas = await listStashShas(cwd);\r\n if (!currentShas.includes(stashSha)) {\r\n throw new Error(\"检查点已失效(stash entry 已被消费或 GC),无法恢复\");\r\n }\r\n await git([\"checkout\", \"--\", \".\"], cwd);\r\n await git([\"clean\", \"-fd\"], cwd);\r\n await git([\"stash\", \"apply\", stashSha], cwd);\r\n const refIndex = currentShas.indexOf(stashSha);\r\n if (refIndex >= 0) await git([\"stash\", \"drop\", `stash@{${refIndex}}`], cwd);\r\n}\r\n\r\n/**\r\n * 将工作区重置为 HEAD 干净状态(丢弃所有未提交修改与未跟踪文件)。\r\n * 用于「目标检查点 stashSha 为空」的 rewind 场景——那时工作区本就干净,\r\n * 但累积的后续对话修改需要被丢弃。\r\n */\r\nexport async function restoreToClean(cwd: string): Promise<void> {\r\n const inRepo = await isGitRepo(cwd);\r\n if (!inRepo) throw new Error(\"非 git 仓库,无法恢复文件状态\");\r\n if (!(await hasCommits(cwd))) throw new Error(\"仓库无 commit,无法恢复\");\r\n await git([\"checkout\", \"--\", \".\"], cwd);\r\n await git([\"clean\", \"-fd\"], cwd);\r\n}\r\n\r\nexport async function discardCheckpoint(checkpoint: Checkpoint): Promise<void> {\r\n if (!checkpoint.isGitRepo || !checkpoint.stashSha) return;\r\n const { cwd, stashSha } = checkpoint;\r\n const shas = await listStashShas(cwd);\r\n const idx = shas.indexOf(stashSha);\r\n if (idx < 0) return;\r\n try { await git([\"stash\", \"drop\", `stash@{${idx}}`], cwd); } catch { /* ignore */ }\r\n}\r\n","import { mkdir, readFile, writeFile, readdir, unlink, rename } from \"node:fs/promises\";\r\nimport { join } from \"node:path\";\r\nimport { randomUUID } from \"node:crypto\";\r\nimport type { StoredSession, SessionSummary } from \"./types.js\";\r\n\r\nexport function defaultSessionsDir(): string {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n return join(home, \".dskcode\", \"sessions\");\r\n}\r\n\r\nexport class SessionStore {\r\n readonly #dir: string;\r\n constructor(dir?: string) { this.#dir = dir ?? defaultSessionsDir(); }\r\n get dir(): string { return this.#dir; }\r\n\r\n async save(session: StoredSession): Promise<void> {\r\n await mkdir(this.#dir, { recursive: true });\r\n const finalPath = join(this.#dir, `${session.id}.json`);\r\n const tmpPath = join(this.#dir, `.${session.id}.json.tmp`);\r\n await writeFile(tmpPath, JSON.stringify(session, null, 2), \"utf-8\");\r\n await rename(tmpPath, finalPath);\r\n }\r\n\r\n async load(id: string): Promise<StoredSession | null> {\r\n const path = join(this.#dir, `${id}.json`);\r\n try {\r\n const content = await readFile(path, \"utf-8\");\r\n return JSON.parse(content) as StoredSession;\r\n } catch (err: unknown) {\r\n if (isENOENT(err)) return null;\r\n throw err;\r\n }\r\n }\r\n\r\n async list(): Promise<SessionSummary[]> {\r\n let files: string[];\r\n try { files = await readdir(this.#dir); }\r\n catch (err: unknown) { if (isENOENT(err)) return []; throw err; }\r\n\r\n const results: SessionSummary[] = [];\r\n for (const file of files) {\r\n if (!file.endsWith(\".json\") || file.startsWith(\".\")) continue;\r\n try {\r\n const content = await readFile(join(this.#dir, file), \"utf-8\");\r\n const s = JSON.parse(content) as StoredSession;\r\n results.push({\r\n id: s.id, title: s.title || \"(无标题)\", updatedAt: s.updatedAt,\r\n cwd: s.cwd, messageCount: s.messages?.length ?? 0,\r\n });\r\n } catch { /* skip corrupted */ }\r\n }\r\n return results.sort((a, b) => b.updatedAt - a.updatedAt);\r\n }\r\n\r\n async delete(id: string): Promise<void> {\r\n const path = join(this.#dir, `${id}.json`);\r\n try { await unlink(path); }\r\n catch (err: unknown) { if (!isENOENT(err)) throw err; }\r\n }\r\n\r\n async exists(id: string): Promise<boolean> { return (await this.load(id)) !== null; }\r\n static newId(): string { return randomUUID(); }\r\n}\r\n\r\nfunction isENOENT(err: unknown): boolean {\r\n return typeof err === \"object\" && err !== null && \"code\" in err && (err as { code: unknown }).code === \"ENOENT\";\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 对话日志记录器 — 将用户与 AI 的完整交互过程写入人类可读的日志文件\r\n//\r\n// 设计要点:\r\n// 1. 每条事件输出为\"分隔线 + 标题行 + pretty JSON + 空行\"的多行块,\r\n// 方便人眼直接阅读;同时 pretty JSON 仍可被机器按段解析。\r\n// 2. 异步写入,错误静默处理,绝不影响主对话流程\r\n// 3. 按会话 ID 分文件,一个会话一个日志文件\r\n// 4. 工具结果数据截断到 MAX_DATA_LEN,避免日志文件膨胀\r\n// 5. 写入操作串行化(队列),保证事件顺序与实际发生顺序一致\r\n// 6. 自动从 V8 栈中提取调用方文件:行号,并写入人类可读的本地时间字符串\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { mkdir, appendFile } from \"node:fs/promises\";\r\nimport { fileURLToPath } from \"node:url\";\r\nimport { join, basename, relative, sep } from \"node:path\";\r\nimport type { LogEvent, LogEventInput } from \"./types.js\";\r\n\r\n/** logger 源文件所在的目录名,用于从栈中过滤掉 logger 内部帧 */\r\nconst LOGGER_DIR = \"logger\";\r\n\r\n/** 工具结果数据的最大记录长度(超出截断并标记) */\r\nconst MAX_DATA_LEN = 2000;\r\n\r\n/** 写入队列最大长度,超过则丢弃最旧的事件(防止极端情况下无限堆积) */\r\nconst MAX_QUEUE = 500;\r\n\r\n/** 默认日志根目录:~/.dskcode/logs */\r\nexport function defaultLogsDir(): string {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n return join(home, \".dskcode\", \"logs\");\r\n}\r\n\r\n/** ConversationLogger 构造选项 */\r\nexport interface ConversationLoggerOptions {\r\n /** 是否启用日志记录,默认 true */\r\n enabled?: boolean;\r\n /**\r\n * 日志根目录,默认 ~/.dskcode/logs。\r\n * 实际日志路径为 `<logsDir>/<项目名>/<sessionId>.jsonl`。\r\n * 测试中可指定临时目录。\r\n */\r\n logsDir?: string;\r\n}\r\n\r\n/**\r\n * 从工作目录提取项目名(用于日志分组)。\r\n * 取 cwd 的 basename,根目录等边界情况兜底为 \"_root\"。\r\n */\r\nfunction projectNameFromCwd(cwd: string): string {\r\n const name = basename(cwd);\r\n return name || \"_root\";\r\n}\r\n\r\n/**\r\n * 对话日志记录器。\r\n *\r\n * 每个会话创建一个实例,日志写入 `~/.dskcode/logs/<项目名>/<sessionId>.jsonl`。\r\n * 所有写入操作异步串行执行,调用方无需 await(fire-and-forget)。\r\n */\r\nexport class ConversationLogger {\r\n /** 所有活跃的 logger 实例(用于退出时统一 flush) */\r\n static readonly #instances = new Set<ConversationLogger>();\r\n\r\n /**\r\n * 刷新所有活跃 logger 实例的缓冲,确保事件落盘。\r\n * 进程退出前调用。\r\n */\r\n static async flushAll(): Promise<void> {\r\n const promises: Promise<void>[] = [];\r\n for (const logger of ConversationLogger.#instances) {\r\n promises.push(logger.flush());\r\n }\r\n await Promise.allSettled(promises);\r\n }\r\n\r\n readonly #logPath: string;\r\n readonly #enabled: boolean;\r\n #queue: Promise<void> = Promise.resolve();\r\n #queueLen = 0;\r\n #closed = false;\r\n\r\n constructor(sessionId: string, cwd: string, options?: ConversationLoggerOptions) {\r\n this.#enabled = options?.enabled ?? true;\r\n if (!this.#enabled) {\r\n this.#logPath = \"\";\r\n return;\r\n }\r\n const baseDir = options?.logsDir ?? defaultLogsDir();\r\n const projectName = projectNameFromCwd(cwd);\r\n this.#logPath = join(baseDir, projectName, `${sessionId}.jsonl`);\r\n ConversationLogger.#instances.add(this);\r\n }\r\n\r\n /** 日志文件完整路径(禁用时返回空字符串) */\r\n get logPath(): string {\r\n return this.#logPath;\r\n }\r\n\r\n /** 是否启用日志记录 */\r\n get enabled(): boolean {\r\n return this.#enabled;\r\n }\r\n\r\n /**\r\n * 记录一个事件。\r\n *\r\n * 内部将事件序列化为 pretty JSON 并以多行块的形式追加到日志文件。\r\n * 写入操作串行排队,保证事件顺序。调用方无需 await。\r\n */\r\n log(event: LogEventInput): void {\r\n if (!this.#enabled || this.#closed) return;\r\n\r\n // 队列过长时丢弃最旧事件,防止内存无限增长\r\n if (this.#queueLen >= MAX_QUEUE) return;\r\n\r\n // 自动注入可读时间与调用方位置(栈捕获)\r\n const enriched: LogEvent = {\r\n ...event,\r\n time: event.time ?? formatTime(event.ts),\r\n loc: event.loc ?? captureCallerLocation(),\r\n };\r\n\r\n this.#queueLen++;\r\n this.#queue = this.#queue\r\n .then(() => this.#writeBlock(enriched))\r\n .catch(() => {\r\n /* 日志写入失败静默忽略 */\r\n })\r\n .finally(() => this.#queueLen--);\r\n }\r\n\r\n /**\r\n * 关闭日志记录器。\r\n *\r\n * 标记关闭,不再接受新事件,但会等待队列中已有事件写入完成。\r\n * 返回的 Promise resolve 后保证所有已 log 的事件都已落盘。\r\n */\r\n async flush(): Promise<void> {\r\n this.#closed = true;\r\n await this.#queue.catch(() => {});\r\n ConversationLogger.#instances.delete(this);\r\n }\r\n\r\n /** 将一条事件以多行块的形式写入日志文件 */\r\n async #writeBlock(event: LogEvent): Promise<void> {\r\n const dir = join(this.#logPath, \"..\");\r\n await mkdir(dir, { recursive: true });\r\n const block = formatBlock(event);\r\n await appendFile(this.#logPath, block, \"utf-8\");\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 便捷方法 — 封装常见事件类型,减少调用方样板代码\r\n // -----------------------------------------------------------------------\r\n\r\n /** 记录会话开始 */\r\n logSessionStart(sessionId: string, cwd: string, model: string, mode: string): void {\r\n this.log({ ts: Date.now(), type: \"session_start\", sessionId, cwd, model, mode });\r\n }\r\n\r\n /** 记录用户消息 */\r\n logUserMessage(content: string): void {\r\n this.log({ ts: Date.now(), type: \"user_message\", content });\r\n }\r\n\r\n /** 记录助手文本(一轮中模型输出的完整文本) */\r\n logAssistantText(content: string, round: number): void {\r\n this.log({ ts: Date.now(), type: \"assistant_text\", content, round });\r\n }\r\n\r\n /** 记录思考链(thinking 模式下模型的 CoT) */\r\n logReasoning(content: string, round: number): void {\r\n this.log({ ts: Date.now(), type: \"reasoning\", content, round });\r\n }\r\n\r\n /** 记录工具调用 */\r\n logToolCall(name: string, callId: string, args: string, round: number): void {\r\n this.log({ ts: Date.now(), type: \"tool_call\", name, callId, arguments: args, round });\r\n }\r\n\r\n /** 记录工具结果 */\r\n logToolResult(\r\n name: string,\r\n callId: string,\r\n success: boolean,\r\n data: string,\r\n error: string | undefined,\r\n elapsed: number | undefined,\r\n round: number,\r\n ): void {\r\n this.log({\r\n ts: Date.now(),\r\n type: \"tool_result\",\r\n name,\r\n callId,\r\n success,\r\n data: truncate(data),\r\n ...(error ? { error } : {}),\r\n ...(elapsed !== undefined ? { elapsed } : {}),\r\n round,\r\n });\r\n }\r\n\r\n /** 记录 Token 用量与费用 */\r\n logUsage(\r\n model: string,\r\n promptTokens: number,\r\n completionTokens: number,\r\n cachedPromptTokens: number | undefined,\r\n cost: number,\r\n round: number,\r\n ): void {\r\n this.log({\r\n ts: Date.now(),\r\n type: \"usage\",\r\n model,\r\n promptTokens,\r\n completionTokens,\r\n ...(cachedPromptTokens !== undefined ? { cachedPromptTokens } : {}),\r\n cost,\r\n round,\r\n });\r\n }\r\n\r\n /** 记录错误 */\r\n logError(message: string, stack?: string): void {\r\n this.log({ ts: Date.now(), type: \"error\", message, ...(stack ? { stack } : {}) });\r\n }\r\n\r\n /** 记录一轮对话完成 */\r\n logTurnDone(elapsed: number, toolRounds: number): void {\r\n this.log({ ts: Date.now(), type: \"turn_done\", elapsed, toolRounds });\r\n }\r\n\r\n /** 记录会话结束 */\r\n logSessionEnd(elapsed: number): void {\r\n this.log({ ts: Date.now(), type: \"session_end\", elapsed });\r\n }\r\n\r\n /** 记录反射(工具失败归因注入到下一轮 prompt 时) */\r\n logReflections(\r\n items: Array<{ category: string; toolName: string; hint: string }>,\r\n ): void {\r\n this.log({ ts: Date.now(), type: \"reflection\", items });\r\n }\r\n}\r\n\r\n/**\r\n * 截断过长的字符串,超出部分用占位符标记。\r\n */\r\nfunction truncate(text: string): string {\r\n if (text.length <= MAX_DATA_LEN) return text;\r\n return text.slice(0, MAX_DATA_LEN) + `...[已截断,原始长度 ${text.length}]`;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 时间格式化\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 把毫秒时间戳格式化为 `YYYY-MM-DD HH:mm:ss.SSS`(本地时区) */\r\nfunction formatTime(ts: number): string {\r\n const d = new Date(ts);\r\n const pad = (n: number, w = 2) => String(n).padStart(w, \"0\");\r\n return (\r\n `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ` +\r\n `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}.` +\r\n `${pad(d.getMilliseconds(), 3)}`\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 调用方位置捕获(V8 栈解析)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 缓存项目根目录:用于将栈中绝对路径转换为项目相对路径。\r\n * 第一次调用时基于 import.meta.url 推断。\r\n */\r\nlet projectRootCache: string | null = null;\r\nfunction getProjectRoot(): string {\r\n if (projectRootCache !== null) return projectRootCache;\r\n try {\r\n // 当前 logger.ts 的 URL,形如 file:///D:/projects/.../ts-version/src/logger/logger.ts\r\n const here = fileURLToPath(import.meta.url);\r\n // 向上找 src/ 的父目录作为项目根\r\n const segments = here.split(sep);\r\n const srcIdx = segments.lastIndexOf(\"src\");\r\n projectRootCache = srcIdx > 0 ? segments.slice(0, srcIdx).join(sep) : here;\r\n } catch {\r\n projectRootCache = \"\";\r\n }\r\n return projectRootCache;\r\n}\r\n\r\n/**\r\n * 匹配 V8 栈帧的 `at ... (file:line:col)` 或 `at file:line:col`。\r\n * 捕获组:1=文件路径(可能为 file:// URL),2=行号。\r\n */\r\nconst STACK_FRAME_RE = /\\s+at\\s+.+?\\((.+):(\\d+):\\d+\\)|\\s+at\\s+(.+):(\\d+):\\d+/;\r\n\r\n/**\r\n * 从 V8 栈中查找第一个不在 logger 目录下的栈帧,返回其文件:行号。\r\n * 如果解析失败,返回 `{ file: \"<unknown>\", line: 0 }`。\r\n */\r\nfunction captureCallerLocation(): { file: string; line: number } {\r\n let stack: string | undefined;\r\n try {\r\n const err = new Error();\r\n Error.captureStackTrace?.(err, captureCallerLocation);\r\n stack = err.stack;\r\n } catch {\r\n return { file: \"<unknown>\", line: 0 };\r\n }\r\n if (!stack) return { file: \"<unknown>\", line: 0 };\r\n\r\n const root = getProjectRoot();\r\n const lines = stack.split(\"\\n\");\r\n\r\n for (const raw of lines) {\r\n const m = STACK_FRAME_RE.exec(raw);\r\n if (!m) continue;\r\n const filePath = m[1] ?? m[3] ?? \"\";\r\n const lineNo = Number(m[2] ?? m[4] ?? \"0\");\r\n if (!filePath) continue;\r\n\r\n // 跳过 logger 目录内部的帧\r\n const normalized = filePath.replace(/^file:\\/\\/\\//, \"\").replace(/^file:\\/\\//, \"\");\r\n if (\r\n normalized.includes(`${sep}${LOGGER_DIR}${sep}`) ||\r\n normalized.endsWith(`${sep}${LOGGER_DIR}`)\r\n ) {\r\n continue;\r\n }\r\n // 跳过 node_modules\r\n if (normalized.includes(`${sep}node_modules${sep}`)) continue;\r\n\r\n let rel = normalized;\r\n if (root && normalized.startsWith(root)) {\r\n rel = normalized.slice(root.length).replace(/^[/\\\\]/, \"\") || basename(normalized);\r\n }\r\n return { file: rel || basename(normalized), line: lineNo };\r\n }\r\n return { file: \"<unknown>\", line: 0 };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 多行块格式化\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 分隔线宽度 */\r\nconst SEPARATOR_WIDTH = 80;\r\nconst SEPARATOR = \"─\".repeat(SEPARATOR_WIDTH);\r\n\r\n/**\r\n * 将事件渲染为多行文本块:\r\n *\r\n * ────────...\r\n * [2026-06-29 14:30:45.123] [user_message] @ src/agent/index.ts:253\r\n * {\r\n * \"ts\": 1782702446439,\r\n * \"time\": \"2026-06-29 14:30:45.123\",\r\n * \"loc\": { \"file\": \"src/agent/index.ts\", \"line\": 253 },\r\n * \"type\": \"user_message\",\r\n * \"content\": \"你好\"\r\n * }\r\n *\r\n * (末尾保留一个空行)\r\n */\r\nfunction formatBlock(event: LogEvent): string {\r\n const header = `[${event.time}] [${event.type}] @ ${event.loc.file}:${event.loc.line}`;\r\n const body = JSON.stringify(event, null, 2);\r\n return `${SEPARATOR}\\n${header}\\n${body}\\n\\n`;\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// Agent 会话 — 协调者(Coordinator)\r\n//\r\n// 职责(瘦身后):\r\n// 1. 编排 chat() 主循环(用户输入 → LLM → 工具 → 持久化)\r\n// 2. 协调三个纯职责子模块:\r\n// - ToolExecutor — 纯执行(tool-executor.ts)\r\n// - StormDetector — 纯判断(storm-detector.ts)\r\n// - buildToolDefinitions / trimMessages — 纯计算(tool-definitions.ts / message-builder.ts)\r\n// 3. 维护会话级状态:消息历史、checkpoints、模式、成本追踪、日志\r\n// 4. 持久化与恢复\r\n//\r\n// 函数注释规范见仓库根 AGENTS.md「函数注释规范」一节。\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type {\r\n ChatMessage,\r\n ChatOptions,\r\n Provider,\r\n ProviderToolCall,\r\n UsageInfo,\r\n ModelId,\r\n} from \"../provider/index.js\";\r\nimport { CostTracker, calculateCost } from \"../provider/index.js\";\r\nimport type { AnyAgentTool } from \"../tool/index.js\";\r\nimport type { Gate, ToolCallRecord } from \"../tool/types.js\";\r\nimport { AlwaysAllowGate, ToolKind, eraseTool } from \"../tool/types.js\";\r\nimport type { AgentEvent, SessionMode, SystemPromptOptions } from \"./types.js\";\r\nimport { buildSystemPrompt, buildPlanSystemPrompt } from \"./system-prompt.js\";\r\nimport { trimMessages, buildApiMessages } from \"./message-builder.js\";\r\nimport { ToolRegistry } from \"../tool/registry.js\";\r\nimport { ToolExecutor } from \"./tool-executor.js\";\r\nimport { StormDetector } from \"./storm-detector.js\";\r\nimport { Reflector, type AnalyzeItem } from \"./reflector.js\";\r\nimport { buildToolDefinitions } from \"./tool-definitions.js\";\r\nimport { TodoList } from \"../harness/todo-list.js\";\r\nimport { createHarnessTools } from \"../harness/tools.js\";\r\nimport {\r\n createCheckpoint,\r\n restoreCheckpointForce,\r\n restoreToClean,\r\n discardCheckpoint,\r\n type Checkpoint,\r\n} from \"../checkpoint/index.js\";\r\nimport { SessionStore, type StoredSession } from \"../session-store/index.js\";\r\nimport { ConversationLogger } from \"../logger/index.js\";\r\n\r\n/**\r\n * Session 构造选项。\r\n *\r\n * @field cwd — 当前工作目录,决定 checkpoint/工具路径解析的根\r\n * @field maxToolRounds — 单次 chat() 内允许的最大工具调用轮数(防无限循环)\r\n * @field reservedForOutput — 上下文裁剪时为模型输出预留的 token 数\r\n * @field preserveRecentRounds — 上下文裁剪时强制保留的最近回合数\r\n * @field projectContext — 项目级背景(如 AGENTS.md 内容),注入到 system prompt\r\n * @field gate — 工具权限门;不传则使用 AlwaysAllowGate(全部放行)\r\n * @field writeRoots — 写工具允许的根目录列表(绝对路径)\r\n * @field sessionId — 复用会话 ID;不传则生成新 UUID\r\n * @field store — 会话存储实例;传 false 禁用持久化(用于测试)\r\n * @field enableCheckpoint — 是否启用 git 检查点(/rewind 需要),默认 true\r\n * @field enableLog — 是否启用对话日志(写入 .dskcode/logs/),默认 true\r\n */\r\nexport interface SessionOptions {\r\n cwd?: string;\r\n maxToolRounds?: number;\r\n reservedForOutput?: number;\r\n preserveRecentRounds?: number;\r\n projectContext?: string;\r\n gate?: Gate;\r\n writeRoots?: string[];\r\n /**\r\n * 会话 ID。传入则复用,不传则生成新 UUID。\r\n * 使用 Session.resume() 恢复会话时必须传。\r\n */\r\n sessionId?: string;\r\n /**\r\n * 会话存储实例。不传则使用默认 ~/.dskcode/sessions/。\r\n * 传 false 禁用持久化(用于测试和不需要保存的场景)。\r\n */\r\n store?: SessionStore | false;\r\n /** 是否启用 checkpoint(/rewind 需要),默认 true */\r\n enableCheckpoint?: boolean;\r\n /** 是否启用对话日志记录(写入 .dskcode/logs/),默认 true */\r\n enableLog?: boolean;\r\n /** 是否启用 Reflector 失败归因注入(默认 true)。传 false 可彻底关闭 */\r\n enableReflection?: boolean;\r\n /** 是否启用 Harness(TodoList 规划 + 任务自检提示)。默认 true */\r\n enableHarness?: boolean;\r\n}\r\n\r\n/**\r\n * 消息检查点信息(对外暴露给 UI 展示 /rewind 列表用)。\r\n *\r\n * @field index — 该 user 消息在 messages 数组中的索引\r\n * @field preview — 用户消息前 80 字\r\n * @field timestamp — checkpoint 创建时间(毫秒)\r\n * @field isGitRepo — 该检查点对应的工作区是否为 git 仓库(决定能否文件回退)\r\n */\r\nexport interface MessageCheckpointInfo {\r\n index: number;\r\n preview: string;\r\n timestamp: number;\r\n isGitRepo: boolean;\r\n}\r\n\r\n/**\r\n * rewind 操作结果。\r\n * - `ok: true` 时 `fileRestored` 标明文件工作区是否被回退\r\n * - `ok: false` 时 `error` 为人类可读的错误描述\r\n */\r\nexport type RewindResult =\r\n | { ok: true; fileRestored: boolean }\r\n | { ok: false; error: string };\r\n\r\n/**\r\n * Session — 单个对话会话的协调者。\r\n *\r\n * 责任范围(瘦身后):\r\n * 1. 编排 chat() 主循环:用户输入 → 构建 prompt → 调 LLM → 工具执行 → 持久化\r\n * 2. 维护会话级状态:messages、checkpoints、mode、cost、log\r\n * 3. 不再直接实现工具执行与风暴检测,而是委托给 ToolExecutor / StormDetector\r\n */\r\nexport class Session {\r\n /** 当前会话的消息历史(含 system/user/assistant/tool),可外部只读 */\r\n readonly #messages: ChatMessage[] = [];\r\n /** LLM Provider(DeepSeek 等) */\r\n readonly #provider: Provider;\r\n /** 工具注册表(所有可用工具) */\r\n readonly #toolRegistry: ToolRegistry;\r\n /** 成本追踪器(今日 + 本会话) */\r\n readonly #costTracker: CostTracker;\r\n /** 归一化后的构造选项(带默认值) */\r\n readonly #options: Required<\r\n Pick<\r\n SessionOptions,\r\n \"cwd\" | \"maxToolRounds\" | \"reservedForOutput\" | \"preserveRecentRounds\"\r\n >\r\n > & {\r\n projectContext?: string;\r\n gate: Gate;\r\n writeRoots: string[];\r\n enableCheckpoint: boolean;\r\n enableLog: boolean;\r\n enableReflection: boolean;\r\n enableHarness: boolean;\r\n };\r\n /** 中止信号控制器(abort() 时触发,传递给 LLM 和工具) */\r\n readonly #abortController = new AbortController();\r\n\r\n /** 会话唯一 ID(UUID) */\r\n readonly #sessionId: string;\r\n /** 持久化存储;传 false 时为 null */\r\n readonly #store: SessionStore | null;\r\n /** 会话创建时间(毫秒) */\r\n #createdAt: number;\r\n /** 节流持久化定时器(500ms debounce) */\r\n #persistTimer: NodeJS.Timeout | null = null;\r\n\r\n /** user 消息 → Checkpoint 映射(仅给 user 消息建点) */\r\n #checkpoints = new Map<number, Checkpoint>();\r\n /** 最近的失败记录(仅失败的,用于风暴检测) */\r\n #stormRecords: ToolCallRecord[] = [];\r\n /** 当前会话模式:code(默认)/ plan(只读) */\r\n #mode: SessionMode = \"code\";\r\n /** 对话日志记录器(写入 .dskcode/logs/) */\r\n readonly #logger: ConversationLogger;\r\n /** 风暴检测器(连续 3 次同工具同错码失败时中断本轮) */\r\n readonly #stormDetector: StormDetector;\r\n /** 失败归因 Reflector(将本轮失败原因拼到下一轮 prompt;null 表示已关闭) */\r\n readonly #reflector: Reflector | null;\r\n /** 本轮工具执行结果(仅在 chat() 循环内使用;用于下一轮注入 reflection) */\r\n #lastRoundResults: AnalyzeItem[] | null = null;\r\n /** Harness 任务列表(仅在 enableHarness 时非 null) */\r\n readonly #todoList: TodoList | null;\r\n /** 本会话内是否已发出过「未拆 todo」护栏提示(避免每轮骚扰) */\r\n #harnessHintEmitted = false;\r\n\r\n /**\r\n * 构造一个 Session。\r\n *\r\n * @param provider — LLM Provider 实例(必须)\r\n * @param tools — 工具列表或已初始化的 ToolRegistry(默认空)\r\n * @param costTracker — 成本追踪器(默认新建)\r\n * @param options — 会话选项(详见 SessionOptions)\r\n *\r\n * @sideEffect 创建 .dskcode/logs/ 下的日志文件并写入 session_start 事件\r\n */\r\n constructor(\r\n provider: Provider,\r\n tools: AnyAgentTool[] | ToolRegistry = [],\r\n costTracker?: CostTracker,\r\n options?: SessionOptions,\r\n ) {\r\n this.#provider = provider;\r\n if (tools instanceof ToolRegistry) {\r\n this.#toolRegistry = tools;\r\n } else {\r\n this.#toolRegistry = new ToolRegistry();\r\n this.#toolRegistry.registerAll(tools);\r\n }\r\n this.#costTracker = costTracker ?? new CostTracker();\r\n this.#options = {\r\n cwd: options?.cwd ?? process.cwd(),\r\n maxToolRounds: options?.maxToolRounds ?? 20,\r\n reservedForOutput: options?.reservedForOutput ?? 4096,\r\n preserveRecentRounds: options?.preserveRecentRounds ?? 10,\r\n projectContext: options?.projectContext,\r\n gate: options?.gate ?? new AlwaysAllowGate(),\r\n writeRoots: options?.writeRoots ?? [options?.cwd ?? process.cwd()],\r\n enableCheckpoint: options?.enableCheckpoint ?? true,\r\n enableLog: options?.enableLog ?? true,\r\n enableReflection: options?.enableReflection !== false,\r\n enableHarness: options?.enableHarness !== false,\r\n };\r\n this.#sessionId = options?.sessionId ?? SessionStore.newId();\r\n this.#store =\r\n options?.store === false ? null : (options?.store ?? new SessionStore());\r\n this.#createdAt = Date.now();\r\n this.#logger = new ConversationLogger(this.#sessionId, this.#options.cwd, {\r\n enabled: this.#options.enableLog,\r\n });\r\n this.#logger.logSessionStart(\r\n this.#sessionId,\r\n this.#options.cwd,\r\n this.#provider.model(),\r\n this.#mode,\r\n );\r\n this.#stormDetector = new StormDetector({ threshold: 3 });\r\n this.#reflector = this.#options.enableReflection ? new Reflector() : null;\r\n\r\n // Harness 初始化(TodoList + 5 个新工具:todo_add / mark_running / mark_done / mark_failed / retry)\r\n if (this.#options.enableHarness) {\r\n const todoList = new TodoList();\r\n this.#todoList = todoList;\r\n for (const t of createHarnessTools(todoList)) {\r\n this.#toolRegistry.registerErased(eraseTool(t));\r\n }\r\n } else {\r\n this.#todoList = null;\r\n }\r\n }\r\n\r\n /** 当前会话的完整消息历史(只读视图) */\r\n get messages(): readonly ChatMessage[] {\r\n return this.#messages;\r\n }\r\n /** 本次会话累计成本(人民币元) */\r\n get accumulatedCost(): number {\r\n return this.#costTracker.sessionTotalCost;\r\n }\r\n /** 成本追踪器(含今日总成本、会话成本、模型维度统计) */\r\n get costTracker(): CostTracker {\r\n return this.#costTracker;\r\n }\r\n /** 当前模型标识(如 \"deepseek-v4-flash\") */\r\n get model(): string {\r\n return this.#provider.model();\r\n }\r\n /** 工具注册表(可外部读 / 运行时 register) */\r\n get toolRegistry(): ToolRegistry {\r\n return this.#toolRegistry;\r\n }\r\n /** 当前模式:\"code\" | \"plan\" */\r\n get mode(): SessionMode {\r\n return this.#mode;\r\n }\r\n /** 会话 ID(UUID) */\r\n get id(): string {\r\n return this.#sessionId;\r\n }\r\n /** 持久化存储实例(禁用持久化时为 null) */\r\n get store(): SessionStore | null {\r\n return this.#store;\r\n }\r\n /** 会话创建时间戳(毫秒) */\r\n get createdAt(): number {\r\n return this.#createdAt;\r\n }\r\n\r\n /**\r\n * 切换会话模式。\r\n *\r\n * @param mode — \"code\":全部工具;\"plan\":只暴露只读工具\r\n * @returns 设置后的模式(便于链式调用)\r\n */\r\n setMode(mode: SessionMode): SessionMode {\r\n this.#mode = mode;\r\n return this.#mode;\r\n }\r\n\r\n /**\r\n * chat() — 接收一轮用户输入,串起 LLM 调用 / 工具执行 / 风暴中断 / 持久化。\r\n *\r\n * 主循环每轮做:\r\n * 1. 构建 system prompt + 裁剪消息 + 拼装 apiMessages\r\n * 2. 调 provider.chat() 拿流式 chunks,向外 yield text_delta / usage 事件\r\n * 3. 若本轮有 tool_calls:\r\n * a. 先调 StormDetector.shouldBreak 判定是否中断风暴\r\n * b. 调 ToolExecutor.executeBatch 执行工具\r\n * c. 把工具结果 yield 出去并写入 messages\r\n * 4. 持续到 LLM 不再调工具 或 达到 maxToolRounds\r\n *\r\n * @param userInput — 用户本轮输入\r\n * @param opts — 透传给 provider.chat() 的 ChatOptions(thinking / tool_choice 等)\r\n * @yields AgentEvent — text_delta / tool_calls / tool_result / usage / done / error\r\n *\r\n * @sideEffect 写入 messages、logger、costTracker、可能触发 checkpoint / persist\r\n */\r\n async *chat(userInput: string, opts?: ChatOptions): AsyncGenerator<AgentEvent> {\r\n this.#messages.push({ role: \"user\", content: userInput });\r\n this.#logger.logUserMessage(userInput);\r\n const userMsgIndex = this.#messages.length - 1;\r\n if (this.#options.enableCheckpoint) {\r\n try {\r\n const checkpoint = await createCheckpoint(this.#options.cwd);\r\n this.#checkpoints.set(userMsgIndex, checkpoint);\r\n } catch {\r\n /* swallow — checkpoint 失败不应阻塞对话 */\r\n }\r\n }\r\n\r\n const startTime = Date.now();\r\n let toolRounds = 0;\r\n // 工具执行器:本轮内共享同一 signal,避免每次调用都重新构建\r\n const toolExecutor = this.#buildToolExecutor();\r\n // Harness 护栏提示重置:每个新用户输入重新评估一次「未拆 todo」问题\r\n this.#harnessHintEmitted = false;\r\n\r\n try {\r\n while (toolRounds < this.#options.maxToolRounds) {\r\n // 构建本轮 system prompt。若上一轮工具调用有失败,Reflector 会把归因\r\n // 信息拼到 prompt 尾部(仅在 reflector 开启 + lastRoundResults 非空时生效)\r\n let systemPrompt = this.#buildSystemPrompt();\r\n if (this.#reflector && this.#lastRoundResults) {\r\n const reflections = this.#reflector.analyze(this.#lastRoundResults, {\r\n writeRoots: this.#options.writeRoots,\r\n cwd: this.#options.cwd,\r\n });\r\n if (reflections.length > 0) {\r\n systemPrompt = this.#reflector.injectIntoPrompt(systemPrompt, reflections);\r\n this.#logger.logReflections(\r\n reflections.map((r) => ({\r\n category: r.category,\r\n toolName: r.toolName,\r\n hint: r.hint,\r\n })),\r\n );\r\n }\r\n this.#lastRoundResults = null;\r\n }\r\n const [trimmed] = trimMessages([...this.#messages], {\r\n model: this.#provider.model() as unknown as ModelId,\r\n reservedForOutput: this.#options.reservedForOutput,\r\n systemPrompt,\r\n preserveRecentRounds: this.#options.preserveRecentRounds,\r\n });\r\n const apiMessages = buildApiMessages(systemPrompt, trimmed);\r\n\r\n const toolDefs = buildToolDefinitions(this.#toolRegistry, this.#mode);\r\n const stream = this.#provider.chat(apiMessages, {\r\n signal: this.#abortController.signal,\r\n tools: toolDefs.length > 0 ? toolDefs : undefined,\r\n thinkingAllowed: opts?.thinkingAllowed,\r\n thinkingEffort: opts?.thinkingEffort,\r\n responseFormat: opts?.responseFormat,\r\n toolChoice: opts?.toolChoice,\r\n });\r\n\r\n const modelId = this.#provider.model() as unknown as ModelId;\r\n\r\n let accumulatedText = \"\";\r\n let accumulatedReasoning = \"\";\r\n let lastUsage: UsageInfo | undefined;\r\n let lastToolCalls: ProviderToolCall[] | undefined;\r\n let _lastFinishReason: string | null = null;\r\n\r\n // 本轮输入 token 估算(在流式开始前一次计算;DeepSeek 流式只在最后一个\r\n // chunk 才返回 usage,中间过程中 UI 无法得知当前消耗,因此客户端本地估算)\r\n let estimatedInputTokens = 0;\r\n for (const m of apiMessages) {\r\n estimatedInputTokens += this.#provider.countTokens(m.content) + 10;\r\n }\r\n let lastEmitMs = 0;\r\n const EST_EMIT_INTERVAL_MS = 300;\r\n\r\n for await (const chunk of stream) {\r\n if (chunk.reasoningContent) {\r\n accumulatedReasoning += chunk.reasoningContent;\r\n yield { type: \"reasoning_delta\", content: chunk.reasoningContent };\r\n }\r\n if (chunk.content) {\r\n accumulatedText += chunk.content;\r\n yield { type: \"text_delta\", content: chunk.content };\r\n\r\n // 节流推送估算 usage,让 UI 能实时显示\"已消耗 X tokens / ¥Y\"\r\n const now = Date.now();\r\n if (now - lastEmitMs >= EST_EMIT_INTERVAL_MS) {\r\n lastEmitMs = now;\r\n const estOutput = this.#provider.countTokens(accumulatedText);\r\n const estUsage: UsageInfo = {\r\n promptTokens: estimatedInputTokens,\r\n completionTokens: estOutput,\r\n };\r\n const estCost = calculateCost(estUsage, modelId);\r\n yield {\r\n type: \"usage\",\r\n usage: estUsage,\r\n model: modelId,\r\n cost: estCost.totalCost,\r\n estimated: true,\r\n };\r\n }\r\n }\r\n if (chunk.toolCalls && chunk.toolCalls.length > 0)\r\n lastToolCalls = chunk.toolCalls;\r\n if (chunk.usage) {\r\n lastUsage = chunk.usage;\r\n // 真实 usage 覆盖估算值\r\n const realCost = calculateCost(chunk.usage, modelId);\r\n yield {\r\n type: \"usage\",\r\n usage: chunk.usage,\r\n model: modelId,\r\n cost: realCost.totalCost,\r\n estimated: false,\r\n };\r\n }\r\n if (chunk.finishReason) _lastFinishReason = chunk.finishReason;\r\n }\r\n\r\n // 流式末尾再发一次最终 usage,作为权威值(此时 setCurrentUsage 会覆盖估算值)\r\n if (accumulatedText && !lastUsage) {\r\n const estOutput = this.#provider.countTokens(accumulatedText);\r\n const estUsage: UsageInfo = {\r\n promptTokens: estimatedInputTokens,\r\n completionTokens: estOutput,\r\n };\r\n const estCost = calculateCost(estUsage, modelId);\r\n yield {\r\n type: \"usage\",\r\n usage: estUsage,\r\n model: modelId,\r\n cost: estCost.totalCost,\r\n estimated: true,\r\n };\r\n }\r\n\r\n if (lastUsage) {\r\n this.#costTracker.record(lastUsage, modelId);\r\n const cost = calculateCost(lastUsage, modelId);\r\n this.#logger.logUsage(\r\n modelId,\r\n lastUsage.promptTokens,\r\n lastUsage.completionTokens,\r\n lastUsage.cachedPromptTokens,\r\n cost.totalCost,\r\n toolRounds,\r\n );\r\n yield { type: \"usage\", usage: lastUsage, model: modelId };\r\n }\r\n\r\n const assistantMsg: ChatMessage = { role: \"assistant\", content: accumulatedText };\r\n if (lastToolCalls && lastToolCalls.length > 0)\r\n assistantMsg.toolCalls = lastToolCalls;\r\n this.#messages.push(assistantMsg);\r\n this.#logger.logAssistantText(accumulatedText, toolRounds);\r\n // 思考链只入日志、不进 messages(多轮时不回传 API 也不会出错)\r\n if (accumulatedReasoning) {\r\n this.#logger.logReasoning(accumulatedReasoning, toolRounds);\r\n }\r\n\r\n if (lastToolCalls && lastToolCalls.length > 0) {\r\n yield { type: \"tool_calls\", calls: lastToolCalls };\r\n for (const tc of lastToolCalls) {\r\n this.#logger.logToolCall(tc.name, tc.id, tc.arguments, toolRounds);\r\n }\r\n\r\n const stormBroken = this.#stormDetector.shouldBreak(\r\n this.#stormRecords,\r\n lastToolCalls,\r\n );\r\n if (stormBroken) {\r\n const stormMsg = \"\\n⚠️ 同一工具重复出错,已强制切换策略\\n\";\r\n yield { type: \"text_delta\", content: stormMsg };\r\n assistantMsg.toolCalls = undefined;\r\n assistantMsg.content += stormMsg;\r\n this.#stormRecords = [];\r\n toolRounds++;\r\n continue;\r\n }\r\n\r\n const results = await toolExecutor.executeBatch(lastToolCalls);\r\n this.#stormRecords = results.records;\r\n // 保存本轮结果,供下一轮循环开始时 Reflector 分析\r\n this.#lastRoundResults = results.items.map((it) => {\r\n const tool = this.#toolRegistry.get(it.name);\r\n return {\r\n name: it.name,\r\n result: it.result,\r\n kind: tool?.kind ?? ToolKind.Other,\r\n recentSameTool: results.records\r\n .filter((r) => r.name === it.name)\r\n .map((r) => ({ success: r.success, error: r.error })),\r\n };\r\n });\r\n\r\n for (const item of results.items) {\r\n // todo_* 工具执行后,附带当前 todo 列表快照,供 UI 渲染任务进度面板\r\n const todoSnapshot =\r\n item.name.startsWith(\"todo_\") && this.#todoList\r\n ? this.#todoList.items\r\n : undefined;\r\n yield {\r\n type: \"tool_result\",\r\n name: item.name,\r\n result: item.result,\r\n todoSnapshot,\r\n };\r\n this.#logger.logToolResult(\r\n item.name,\r\n item.callId,\r\n item.result.success,\r\n item.result.data,\r\n item.result.error,\r\n undefined,\r\n toolRounds,\r\n );\r\n let toolContent = item.result.data;\r\n if (item.result.diff && item.result.diff.patch)\r\n toolContent += `\\n\\n${item.result.diff.patch}`;\r\n this.#messages.push({\r\n role: \"tool\",\r\n content: toolContent,\r\n toolCallId: item.callId,\r\n name: item.name,\r\n });\r\n }\r\n\r\n // Harness 护栏:检测到模型在完全未拆 todo 的情况下直接调了非 todo 工具,\r\n // 主动发一条提示让模型重新评估(每会话只发一次,避免啰喌)\r\n this.#maybeEmitHarnessHint(results.items);\r\n\r\n toolRounds++;\r\n continue;\r\n }\r\n\r\n // 模型不再调工具,准备退出循环。Harness 模式下若 todo 全结束,\r\n // 提示模型自行做一次任务自检(重新读改动过的文件,确认修改正确)。\r\n if (\r\n this.#todoList &&\r\n this.#todoList.items.length > 0 &&\r\n this.#todoList.isAllTerminated()\r\n ) {\r\n this.#messages.push({\r\n role: \"user\",\r\n content:\r\n \"[系统] 全部 todo 已完成。请做一次任务自检:重新读取本次修改过的文件,\" +\r\n \"确认改动正确无误、类型兼容、没有遗漏。如有问题请新增 todo 修复;\" +\r\n \"确认无误后输出最终总结。\",\r\n });\r\n }\r\n\r\n break;\r\n }\r\n } catch (err: unknown) {\r\n if (err instanceof DOMException && err.name === \"AbortError\") return;\r\n if (err instanceof Error && err.name === \"AbortError\") return;\r\n this.#logger.logError(\r\n err instanceof Error ? err.message : String(err),\r\n err instanceof Error ? err.stack : undefined,\r\n );\r\n yield { type: \"error\", error: err instanceof Error ? err : new Error(String(err)) };\r\n return;\r\n }\r\n\r\n const elapsed = Date.now() - startTime;\r\n this.#logger.logTurnDone(elapsed, toolRounds);\r\n void this.#persist();\r\n // 持久化今日成本数据,确保进程退出后重开不会丢失\r\n await this.#costTracker.flush().catch(() => {});\r\n yield { type: \"done\", elapsed };\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 持久化与恢复\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * 中止当前会话:触发 AbortController,停止流式 LLM 和正在执行的工具。\r\n * 同时清掉节流持久化定时器。\r\n *\r\n * @sideEffect abort 当前 chat 循环、取消所有 #abortController 监听者\r\n */\r\n abort(): void {\r\n this.#abortController.abort();\r\n if (this.#persistTimer) {\r\n clearTimeout(this.#persistTimer);\r\n this.#persistTimer = null;\r\n }\r\n }\r\n\r\n /**\r\n * 刷新日志缓冲,确保所有已记录的事件落盘。\r\n * 通常在进程退出前调用,避免日志丢失。\r\n *\r\n * @sideEffect 写入 .dskcode/logs/ 下的日志文件\r\n */\r\n async flushLog(): Promise<void> {\r\n await this.#logger.flush();\r\n }\r\n\r\n /**\r\n * 清空消息历史、会话成本、风暴记录、checkpoints。\r\n * 注意:不会清掉磁盘上的会话记录(用 delete() 删除),仅重置内存状态。\r\n *\r\n * @sideEffect 抹掉 messages / costTracker / stormRecords / checkpoints\r\n */\r\n reset(): void {\r\n this.#messages.length = 0;\r\n this.#costTracker.resetSession();\r\n this.#stormRecords = [];\r\n this.#checkpoints.clear();\r\n this.#lastRoundResults = null;\r\n }\r\n\r\n /**\r\n * 立即把当前状态写入持久化(不等 500ms debounce)。\r\n * 用于 UI 上\"立即保存\"按钮或异常退出前的最后兜底。\r\n *\r\n * @sideEffect 调用 #doPersist(),写入磁盘\r\n */\r\n async persistNow(): Promise<void> {\r\n if (this.#persistTimer) {\r\n clearTimeout(this.#persistTimer);\r\n this.#persistTimer = null;\r\n }\r\n await this.#doPersist();\r\n }\r\n\r\n /**\r\n * 节流持久化:500ms 内多次调用只会真正落盘一次。\r\n * 通过 setTimeout + timer.refresh() 实现 debounce。\r\n *\r\n * @sideEffect 调度一个 500ms 后的 #doPersist() 任务\r\n */\r\n #persist(): void {\r\n if (!this.#store) return;\r\n if (this.#persistTimer) {\r\n this.#persistTimer.refresh();\r\n return;\r\n }\r\n this.#persistTimer = setTimeout(() => {\r\n this.#persistTimer = null;\r\n void this.#doPersist();\r\n }, 500);\r\n this.#persistTimer.unref();\r\n }\r\n\r\n /**\r\n * 真正执行持久化:构造 StoredSession 并写入 store。\r\n * 失败仅 console.error,不抛给调用方(持久化失败不应阻塞对话)。\r\n *\r\n * @sideEffect 写磁盘 ~/.dskcode/sessions/<id>.json\r\n */\r\n async #doPersist(): Promise<void> {\r\n if (!this.#store) return;\r\n const stored: StoredSession = {\r\n id: this.#sessionId,\r\n title: this.#deriveTitle(),\r\n createdAt: this.#createdAt,\r\n updatedAt: Date.now(),\r\n cwd: this.#options.cwd,\r\n model: this.#provider.model(),\r\n messages: this.#serializeMessages(),\r\n totalCost: this.#costTracker.sessionTotalCost,\r\n };\r\n try {\r\n await this.#store.save(stored);\r\n } catch (err) {\r\n console.error(\"[Session] 持久化失败:\", err);\r\n }\r\n }\r\n\r\n /**\r\n * 从消息历史推导会话标题:取第一条非空 user 消息的前 40 字。\r\n * 无任何 user 消息时返回 \"新会话\"。\r\n *\r\n * @pure 不修改任何状态\r\n */\r\n #deriveTitle(): string {\r\n for (const m of this.#messages) {\r\n if (m.role === \"user\" && m.content.trim()) return m.content.trim().slice(0, 40);\r\n }\r\n return \"新会话\";\r\n }\r\n\r\n /**\r\n * 把内存 messages 转成可持久化的 StoredSession[\"messages\"]。\r\n * 给 user 消息挂上对应 checkpoint(若存在),用于 rewind 时文件回退。\r\n *\r\n * @pure 不修改任何状态\r\n */\r\n #serializeMessages(): StoredSession[\"messages\"] {\r\n return this.#messages.map((msg, idx) => {\r\n const checkpoint = this.#checkpoints.get(idx);\r\n if (msg.role === \"user\" && checkpoint) return { ...msg, checkpoint };\r\n return { ...msg };\r\n });\r\n }\r\n\r\n /**\r\n * 静态方法:从磁盘恢复一个之前保存的 Session。\r\n *\r\n * @param id — 之前 SessionStore 保存的会话 ID\r\n * @param provider — LLM Provider(必须)\r\n * @param tools — 工具列表/Registry(必须重新提供,因为工具不持久化)\r\n * @param costTracker — 成本追踪器(可选)\r\n * @param options — 会话选项(sessionId / store 必须与持久化 ID 对应)\r\n * @returns 恢复后的 Session 实例\r\n * @throws 持久化被禁用时、ID 不存在时抛错\r\n *\r\n * @sideEffect 从磁盘读 messages + checkpoints\r\n */\r\n static async resume(\r\n id: string,\r\n provider: Provider,\r\n tools: AnyAgentTool[] | ToolRegistry = [],\r\n costTracker?: CostTracker,\r\n options?: SessionOptions,\r\n ): Promise<Session> {\r\n const store =\r\n options?.store === false ? null : (options?.store ?? new SessionStore());\r\n if (!store) throw new Error(\"resume 需要启用持久化(options.store 不能为 false)\");\r\n const stored = await store.load(id);\r\n if (!stored) throw new Error(`会话 ${id} 不存在`);\r\n\r\n const session = new Session(provider, tools, costTracker, {\r\n ...options,\r\n sessionId: id,\r\n store,\r\n });\r\n for (const m of stored.messages) {\r\n session.#messages.push({\r\n role: m.role,\r\n content: m.content,\r\n toolCallId: m.toolCallId,\r\n name: m.name,\r\n toolCalls: m.toolCalls,\r\n });\r\n }\r\n for (let i = 0; i < stored.messages.length; i++) {\r\n const cp = stored.messages[i]?.checkpoint;\r\n if (cp) session.#checkpoints.set(i, cp);\r\n }\r\n session.#createdAt = stored.createdAt;\r\n return session;\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 检查点与 Rewind\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * 列出所有 user 消息对应的 checkpoint(按 index 升序)。\r\n * 用于 UI 展示 /rewind 列表。\r\n *\r\n * @returns 每个元素的 index = messages 数组索引,可直接传给 rewind()\r\n * @pure 不修改任何状态\r\n */\r\n listCheckpoints(): MessageCheckpointInfo[] {\r\n const result: MessageCheckpointInfo[] = [];\r\n for (const [index, checkpoint] of this.#checkpoints) {\r\n const msg = this.#messages[index];\r\n if (!msg || msg.role !== \"user\") continue;\r\n result.push({\r\n index,\r\n preview: msg.content.slice(0, 80),\r\n timestamp: checkpoint.timestamp,\r\n isGitRepo: checkpoint.isGitRepo,\r\n });\r\n }\r\n return result.sort((a, b) => a.index - b.index);\r\n }\r\n\r\n /**\r\n * rewind — 把消息历史截断到 targetIndex 对应的 user 消息,\r\n * 并尝试把文件工作区也回退到那一刻。\r\n *\r\n * @param targetIndex — 要回退到的 user 消息索引(来自 listCheckpoints())\r\n * @returns\r\n * - `{ ok: true, fileRestored }`:成功;fileRestored 表示工作区是否也被还原\r\n * - `{ ok: false, error }`:失败(无效索引 / 非 user / 无 checkpoint / 文件恢复失败)\r\n *\r\n * 行为:\r\n * 1. 截断 messages 数组到 targetIndex + 1\r\n * 2. 移除并丢弃所有 > targetIndex 的 checkpoints\r\n * 3. 若目标 checkpoint 是 git 仓库:\r\n * - 有 stashSha:恢复该 stash(force)\r\n * - 无 stashSha:把工作区 restore 到 HEAD 干净状态\r\n * 4. 触发 #persist() 把截断后的状态写盘\r\n *\r\n * @sideEffect 改写 messages、checkpoints、磁盘\r\n */\r\n async rewind(targetIndex: number): Promise<RewindResult> {\r\n if (targetIndex < 0 || targetIndex >= this.#messages.length) {\r\n return { ok: false, error: `无效的消息索引 ${targetIndex}` };\r\n }\r\n const target = this.#messages[targetIndex];\r\n if (!target || target.role !== \"user\") {\r\n return { ok: false, error: `索引 ${targetIndex} 不是 user 消息` };\r\n }\r\n const checkpoint = this.#checkpoints.get(targetIndex);\r\n if (!checkpoint) return { ok: false, error: \"该消息没有检查点\" };\r\n\r\n this.#messages.length = targetIndex + 1;\r\n const toDiscard: Checkpoint[] = [];\r\n for (const [idx, cp] of this.#checkpoints) {\r\n if (idx > targetIndex) {\r\n toDiscard.push(cp);\r\n this.#checkpoints.delete(idx);\r\n }\r\n }\r\n\r\n let fileRestored = false;\r\n if (checkpoint.isGitRepo) {\r\n try {\r\n if (checkpoint.stashSha) {\r\n // 目标检查点有 stash 快照——恢复该快照\r\n await restoreCheckpointForce(checkpoint);\r\n } else {\r\n // 目标检查点 stashSha 为空,代表「那一刻工作区就是 HEAD 干净状态」。\r\n // 但后续对话产生的修改可能还积累在工作区,需要丢弃才能真正回退到那一刻。\r\n await restoreToClean(this.#options.cwd);\r\n }\r\n fileRestored = true;\r\n } catch (err) {\r\n const msg = err instanceof Error ? err.message : String(err);\r\n return { ok: false, error: `对话已截断但文件恢复失败:${msg}` };\r\n }\r\n }\r\n\r\n // restoreCheckpointForce 内部已 drop 了该 checkpoints 的 stash entry,\r\n // 所以只需从 Map 中移除即可,无需再 discardCheckpoint。\r\n this.#checkpoints.delete(targetIndex);\r\n for (const cp of toDiscard) {\r\n void discardCheckpoint(cp);\r\n }\r\n this.#persist();\r\n return { ok: true, fileRestored };\r\n }\r\n\r\n /**\r\n * 是否存在可用的 checkpoint(决定 UI 是否显示 /rewind 入口)。\r\n *\r\n * @pure 不修改任何状态\r\n */\r\n hasCheckpoints(): boolean {\r\n return this.listCheckpoints().length > 0;\r\n }\r\n\r\n /**\r\n * 彻底删除会话:从磁盘移除 SessionStore 条目、丢弃所有 checkpoint、关闭日志。\r\n * 删除后该 Session 实例不应再被使用。\r\n *\r\n * @sideEffect 删磁盘文件、清 checkpoints、flush 并关闭 logger\r\n */\r\n async delete(): Promise<void> {\r\n if (this.#store) await this.#store.delete(this.#sessionId);\r\n for (const cp of this.#checkpoints.values()) {\r\n void discardCheckpoint(cp);\r\n }\r\n this.#checkpoints.clear();\r\n this.#logger.logSessionEnd(Date.now() - this.#createdAt);\r\n await this.#logger.flush();\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 内部方法\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * 构建本轮使用的工具执行器(每次 chat() 调用一次,绑定当前 signal)。\r\n * 每次新建确保 abort signal 是当下有效的,且 #baseCtx 不会被多次调用共享篡改。\r\n *\r\n * @returns 新的 ToolExecutor 实例\r\n * @pure 仅组装参数,不修改任何状态\r\n */\r\n #buildToolExecutor(): ToolExecutor {\r\n return new ToolExecutor({\r\n registry: this.#toolRegistry,\r\n gate: this.#options.gate,\r\n baseCtx: {\r\n cwd: this.#options.cwd,\r\n signal: this.#abortController.signal,\r\n writeRoots: this.#options.writeRoots,\r\n },\r\n });\r\n }\r\n\r\n /**\r\n * 构建 system prompt:注入当前模型、cwd、工具清单、项目上下文。\r\n * plan 模式使用 buildPlanSystemPrompt(只读工具 + 计划输出格式),\r\n * code 模式使用 buildSystemPrompt。\r\n *\r\n * @returns 渲染好的 prompt 字符串\r\n * @pure 不修改任何状态\r\n */\r\n #buildSystemPrompt(): string {\r\n const enabledTools = this.#toolRegistry.list();\r\n const toolDescs = enabledTools.map((t) => ({\r\n name: t.name,\r\n description: t.description,\r\n parameters: t.parameters as unknown as Record<string, unknown>,\r\n }));\r\n const opts: SystemPromptOptions = {\r\n model: this.#provider.model(),\r\n maxToolRounds: this.#options.maxToolRounds,\r\n tools: toolDescs.length > 0 ? toolDescs : undefined,\r\n projectContext: this.#options.projectContext ?? undefined,\r\n cwd: this.#options.cwd,\r\n };\r\n let base: string;\r\n if (this.#mode === \"plan\") base = buildPlanSystemPrompt(opts);\r\n else base = buildSystemPrompt(opts);\r\n // Harness 模式下拼接 TodoList 进度到 prompt 末尾,让模型看到自己进度\r\n if (this.#todoList && this.#todoList.items.length > 0) {\r\n base = base + \"\\n\\n\" + this.#todoList.toMarkdown();\r\n }\r\n return base;\r\n }\r\n\r\n /**\r\n * Harness 护栏:模型跳过 todo_add 直接动手时,主动发一条提示让其重评。\r\n *\r\n * 触发条件(全部需满足):\r\n * 1. Harness 开启(#todoList 非空)\r\n * 2. todoList 仍为空(未拆 todo)\r\n * 3. 本会话内未提示过(#harnessHintEmitted 为 false)\r\n * 4. 本轮调了 ≥1 个**非 todo_* 的工具**(说明模型跳过了 todo_add 直接干活)\r\n *\r\n * 动作:push 一条 user role 消息到 #messages,让下一轮 LLM 看到;设 #harnessHintEmitted = true。\r\n *\r\n * @sideEffect 写 #messages / #harnessHintEmitted\r\n */\r\n #maybeEmitHarnessHint(items: ReadonlyArray<{ name: string }>): void {\r\n if (!this.#todoList) return;\r\n if (this.#todoList.items.length > 0) return;\r\n if (this.#harnessHintEmitted) return;\r\n const hasNonTodo = items.some((it) => !it.name.startsWith(\"todo_\"));\r\n if (!hasNonTodo) return;\r\n this.#harnessHintEmitted = true;\r\n this.#messages.push({\r\n role: \"user\",\r\n content:\r\n \"[系统提示] 你还没有 todo 过,但已经调了具体工具。\\n\" +\r\n \"请回顾你刚做的事:这是「简单」任务(1 轮 1 工具能完成)还是「复杂」任务?\\n\" +\r\n \"如果是复杂(改了/创建了/要读后改/跨多种工具),请停下,先调 todo_add 拆解 3-7 步再继续。\\n\" +\r\n \"如果是简单,忽略本提示。\\n\" +\r\n \"(本提示本会话仅发一次,后续不再提醒。)\",\r\n });\r\n }\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 工具执行沙箱 — 路径约束、路径安全、超时控制、输出截断\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { resolve, relative, isAbsolute } from \"node:path\";\r\nimport { realpath } from \"node:fs/promises\";\r\nimport { spawn } from \"node:child_process\";\r\nimport process from \"node:process\";\r\n\r\n/** 沙箱默认配置 */\r\nconst DEFAULT_TIMEOUT_MS = 30_000;\r\nconst DEFAULT_MAX_OUTPUT_LENGTH = 50_000;\r\nconst DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB\r\n\r\n/** 是否为 Windows 平台 */\r\nconst isWindows = process.platform === \"win32\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 路径安全 — confine / resolveIn / realPath\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 剩下一个开头的 `@` 引用标记。\r\n *\r\n * system prompt 把 `@<路径>` 定义为「文件路径引用」语法,但 LLM 经常把\r\n * `@test.ts` 原样传给工具,导致 read_file 拼出 `cwd\\@test.ts` 而 ENOENT\r\n * (见会话日志 e65f0205 round 0)。`@` 不可能是一段真实路径的合法首字符,\r\n * 在路径解析边界统一剩下一个 `@` 即可让该语法在所有走 resolvePath 的工具上生效。\r\n * glob/grep 不走 resolvePath,可在解析 `directory` 参数时复用本函数。\r\n */\r\nexport function stripMentionPrefix(inputPath: string): string {\r\n if (inputPath.startsWith(\"@\")) return inputPath.slice(1);\r\n return inputPath;\r\n}\r\n\r\n/**\r\n * 将路径解析为绝对路径。\r\n * 相对路径基于 cwd 解析,绝对路径原样返回。会先剩下一个开头的 `@` 引用标记。\r\n */\r\nexport function resolvePath(inputPath: string, cwd: string): string {\r\n const stripped = stripMentionPrefix(inputPath);\r\n const resolved = isAbsolute(stripped) ? stripped : resolve(cwd, stripped);\r\n return resolve(resolved);\r\n}\r\n\r\n/**\r\n * 获取路径的真实路径(解析符号链接)。\r\n * 如果路径不存在,则对其父目录逐级解析,尽可能获取真实路径。\r\n */\r\nasync function realPath(target: string): Promise<string> {\r\n try {\r\n return await realpath(target);\r\n } catch {\r\n // 路径可能还不存在(新建文件),尝试解析父目录\r\n const parent = resolve(target, \"..\");\r\n try {\r\n const realParent = await realpath(parent);\r\n return resolve(realParent, relative(parent, target));\r\n } catch {\r\n // 父目录也不存在,返回原路径\r\n return resolve(target);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 检查目标路径是否在允许的根目录范围内。\r\n *\r\n * @param allowedRoots 允许的根目录列表(绝对路径)\r\n * @param target 要检查的目标路径(绝对路径)\r\n * @returns 如果目标在范围内返回 true,否则返回 false 及错误信息\r\n */\r\nexport async function confine(\r\n allowedRoots: string[],\r\n target: string,\r\n): Promise<{ ok: true } | { ok: false; error: string }> {\r\n if (allowedRoots.length === 0) {\r\n return { ok: true }; // 没有限制,默认允许\r\n }\r\n\r\n const realTarget = await realPath(target);\r\n\r\n for (const root of allowedRoots) {\r\n const realRoot = await realPath(root);\r\n // 检查 target 是否在 root 之下\r\n const rel = relative(realRoot, realTarget);\r\n if (!rel.startsWith(\"..\") && rel !== \"\" && !rel.startsWith(\"/\") && !rel.startsWith(\"\\\\\")) {\r\n return { ok: true };\r\n }\r\n // 允许 target 恰好等于 root\r\n if (realTarget === realRoot) {\r\n return { ok: true };\r\n }\r\n }\r\n\r\n return {\r\n ok: false,\r\n error: `路径 \"${target}\" 不在允许的写入范围内 ${allowedRoots.join(\", \")}`,\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 输出截断\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 截断过长的输出内容。\r\n */\r\nexport function truncateOutput(content: string, maxLength = DEFAULT_MAX_OUTPUT_LENGTH): string {\r\n if (content.length <= maxLength) return content;\r\n const truncated = content.slice(0, maxLength);\r\n return `${truncated}\\n\\n... [输出过长,已截断,共 ${String(content.length)} 字符]`;\r\n}\r\n\r\n/**\r\n * 获取默认超时时间。\r\n */\r\nexport function getDefaultTimeout(): number {\r\n return DEFAULT_TIMEOUT_MS;\r\n}\r\n\r\n/**\r\n * 获取默认最大文件大小。\r\n */\r\nexport function getDefaultMaxFileSize(): number {\r\n return DEFAULT_MAX_FILE_SIZE;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 超时控制\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 创建一个带超时的 AbortController。\r\n */\r\nexport function createTimeoutSignal(signal?: AbortSignal, timeoutMs = DEFAULT_TIMEOUT_MS): AbortController {\r\n const controller = new AbortController();\r\n\r\n // 外部信号触发时,联动中止\r\n if (signal) {\r\n signal.addEventListener(\"abort\", () => {\r\n controller.abort();\r\n }, { once: true });\r\n }\r\n\r\n // 超时自动中止\r\n const timer = setTimeout(() => {\r\n controller.abort();\r\n }, timeoutMs);\r\n controller.signal.addEventListener(\"abort\", () => {\r\n clearTimeout(timer);\r\n }, { once: true });\r\n\r\n return controller;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Shell 命令执行\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 执行 shell 命令的通用封装。\r\n */\r\nexport async function execCommand(\r\n command: string,\r\n args: string[],\r\n cwd: string,\r\n timeoutMs = DEFAULT_TIMEOUT_MS,\r\n signal?: AbortSignal,\r\n /** 如果为 true,表示 command 已经是 shell 程序(如 cmd/sh),不需要再包装 */\r\n isShellCommand?: boolean,\r\n): Promise<{ stdout: string; stderr: string; exitCode: number | null }> {\r\n return new Promise((_resolve) => {\r\n let spawnCmd: string;\r\n let spawnArgs: string[];\r\n let useShell: boolean;\r\n\r\n if (isShellCommand) {\r\n spawnCmd = command;\r\n spawnArgs = args;\r\n useShell = false;\r\n\r\n // cmd.exe 默认用 GBK 输出中文错误,会在 UTF-8 终端里变成乱码。\r\n // 所以在 cmd 这条路径下,把命令带上 `chcp 65001 >nul &&` 前缀\r\n // 切到 UTF-8 代码页,stderr 才能被 LLM 读懂。\r\n // 启动期探测出的 SHELL_INFO 已不允许这种耗时切换,能在 git bash 赏i\r\n // 就不进 cmd 这条分支;这里作为“找不到 Git Bash 时的兒底”出现。\r\n //\r\n // 注意:chcp 切换只对 cmd.exe /c 调用里可见,不会污染外层进程。\r\n if (isWindows && spawnCmd === \"cmd\" && spawnArgs[0] === \"/c\") {\r\n const prefixed = `chcp 65001 >nul && ${spawnArgs[1] ?? \"\"}`;\r\n spawnArgs = [\"/c\", prefixed];\r\n }\r\n } else {\r\n useShell = !isWindows;\r\n spawnCmd = isWindows ? \"cmd\" : command;\r\n spawnArgs = isWindows\r\n ? [\"/c\", command, ...args]\r\n : args;\r\n }\r\n\r\n const child = spawn(spawnCmd, spawnArgs, {\r\n cwd,\r\n shell: useShell,\r\n env: { ...process.env },\r\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\r\n });\r\n\r\n let stdout = \"\";\r\n let stderr = \"\";\r\n\r\n child.stdout.on(\"data\", (data: Buffer) => {\r\n stdout += data.toString(\"utf8\");\r\n });\r\n\r\n child.stderr.on(\"data\", (data: Buffer) => {\r\n stderr += data.toString(\"utf8\");\r\n });\r\n\r\n const timeout = setTimeout(() => {\r\n child.kill(\"SIGTERM\");\r\n // 给 5 秒优雅退出时间\r\n setTimeout(() => {\r\n child.kill(\"SIGKILL\");\r\n }, 5000);\r\n }, timeoutMs);\r\n\r\n // 外部中止信号\r\n if (signal) {\r\n if (signal.aborted) {\r\n child.kill(\"SIGTERM\");\r\n }\r\n signal.addEventListener(\"abort\", () => {\r\n child.kill(\"SIGTERM\");\r\n }, { once: true });\r\n }\r\n\r\n child.on(\"close\", (code) => {\r\n clearTimeout(timeout);\r\n _resolve({ stdout, stderr, exitCode: code });\r\n });\r\n\r\n child.on(\"error\", (err) => {\r\n clearTimeout(timeout);\r\n stderr += `\\n进程启动失败:${err.message}`;\r\n _resolve({ stdout, stderr, exitCode: null });\r\n });\r\n });\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 文件差异计算 — Myers O(ND) 算法 + unified patch 生成\r\n//\r\n// Myers 算法与 Git 的 diff 引擎同款,产生最短编辑脚本(SES)。\r\n// 时间复杂度 O((N+M)D),空间复杂度 O(N+M),其中 D 为编辑距离。\r\n//\r\n// 实现方式:非递归的 Myers 差分算法。\r\n// 1. 正向搜索:找到从 (0,0) 到 (N,M) 的最短编辑路径\r\n// 2. 回溯路径:从 (N,M) 回溯到 (0,0),生成 DiffLine 序列\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type { FileDiff } from \"./types.js\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 内部类型\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 单行 diff 操作类型 */\r\ntype Op = \"equal\" | \"add\" | \"remove\";\r\n\r\n/** 一行 diff 记录 */\r\ninterface DiffLine {\r\n op: Op;\r\n line: string;\r\n}\r\n\r\n/** unified diff 中的一个 hunk */\r\ninterface DiffHunk {\r\n oldStart: number;\r\n oldCount: number;\r\n newStart: number;\r\n newCount: number;\r\n lines: DiffLine[];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// EOL(行尾风格)检测\r\n// ---------------------------------------------------------------------------\r\n\r\ntype EOFStyle = \"lf\" | \"crlf\" | \"no-eol\";\r\n\r\ninterface SplitResult {\r\n lines: string[];\r\n eol: string;\r\n eofStyle: EOFStyle;\r\n}\r\n\r\nfunction splitLines(text: string): SplitResult {\r\n if (text.length === 0) {\r\n return { lines: [], eol: \"\\n\", eofStyle: \"no-eol\" };\r\n }\r\n\r\n const crlfIdx = text.indexOf(\"\\r\\n\");\r\n const lfIdx = text.indexOf(\"\\n\");\r\n let eol: string;\r\n if (crlfIdx !== -1 && (lfIdx === -1 || crlfIdx <= lfIdx)) {\r\n eol = \"\\r\\n\";\r\n } else if (lfIdx !== -1) {\r\n eol = \"\\n\";\r\n } else {\r\n eol = \"\\n\";\r\n }\r\n\r\n const lines = text.split(eol);\r\n\r\n let eofStyle: EOFStyle;\r\n if (text.endsWith(eol)) {\r\n if (lines.length > 0 && lines[lines.length - 1] === \"\") {\r\n lines.pop();\r\n }\r\n eofStyle = \"lf\";\r\n } else {\r\n eofStyle = \"no-eol\";\r\n }\r\n\r\n return { lines, eol, eofStyle };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Myers 非递归差分算法\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 计算两个行数组之间的最短编辑脚本(SES)。\r\n *\r\n * 使用非递归 Myers 算法:\r\n * 1. 正向搜索 V 数组,记录每步的进度\r\n * 2. 到达终点后回溯路径生成 DiffLine 列表\r\n */\r\nfunction computeLineDiff(oldLines: string[], newLines: string[]): DiffLine[] {\r\n const N = oldLines.length;\r\n const M = newLines.length;\r\n\r\n if (N === 0 && M === 0) return [];\r\n if (N === 0) return newLines.map((line) => ({ op: \"add\" as Op, line }));\r\n if (M === 0) return oldLines.map((line) => ({ op: \"remove\" as Op, line }));\r\n\r\n // 存储每一步的 V 快照,用于回溯\r\n const trace: Map<number, number>[] = [];\r\n\r\n const maxD = N + M;\r\n // V[k] = x position;初始化为 -1 表示未访问\r\n // eslint-disable-next-line unicorn/no-new-array, @typescript-eslint/no-unsafe-type-assertion\r\n const V: number[] = new Array(2 * maxD + 2).fill(-1) as number[];\r\n // k 的偏移量,使索引非负\r\n const offset = maxD;\r\n\r\n V[offset + 1] = 0;\r\n\r\n // 正向搜索:从 d=0 开始,逐层扩展\r\n for (let d = 0; d <= maxD; d++) {\r\n // 保存当前层的 V 快照\r\n const snapshot = new Map<number, number>();\r\n\r\n const kStart = -d;\r\n const kEnd = d;\r\n\r\n for (let k = kStart; k <= kEnd; k += 2) {\r\n const ki = k + offset;\r\n let x: number;\r\n\r\n if (k === -d || (k !== d && V[ki - 1]! < V[ki + 1]!)) {\r\n x = V[ki + 1]!;\r\n } else {\r\n x = V[ki - 1]! + 1;\r\n }\r\n\r\n let y = x - k;\r\n\r\n // 沿着对角线延伸\r\n while (x < N && y < M && oldLines[x] === newLines[y]) {\r\n x++;\r\n y++;\r\n }\r\n\r\n V[ki] = x;\r\n snapshot.set(k, x);\r\n\r\n // 到达终点\r\n if (x >= N && y >= M) {\r\n trace.push(snapshot);\r\n // 回溯路径\r\n return backtrack(trace, N, M, oldLines, newLines, offset);\r\n }\r\n }\r\n\r\n trace.push(snapshot);\r\n }\r\n\r\n // 不应该到达这里(总有解)\r\n return oldLines.map((line) => ({ op: \"remove\" as Op, line }))\r\n .concat(newLines.map((line) => ({ op: \"add\" as Op, line })));\r\n}\r\n\r\n/**\r\n * 从 trace 中回溯最短编辑路径,生成 DiffLine 列表。\r\n *\r\n * trace 存储了每一层 (d) 的 V 快照。\r\n * 从终点 (N, M) 开始,逆着路径逐层回溯到 (0, 0)。\r\n */\r\nfunction backtrack(\r\n trace: Map<number, number>[],\r\n N: number,\r\n M: number,\r\n oldLines: string[],\r\n newLines: string[],\r\n _offset: number,\r\n): DiffLine[] {\r\n const result: DiffLine[] = [];\r\n\r\n let x = N;\r\n let y = M;\r\n\r\n // 从最后一层回溯到第 1 层(第 0 层只有初始蛇行,没有实际移动)\r\n for (let d = trace.length - 1; d >= 1; d--) {\r\n const k = x - y;\r\n\r\n // 从 trace[d-1] 中查找上一步的 x 位置\r\n // 注意:trace[d] 存的是本层结果,前一层的数据在 trace[d-1] 中\r\n const prevSnapshot = trace[d - 1]!;\r\n const prevKDown = prevSnapshot.get(k - 1);\r\n const prevKUp = prevSnapshot.get(k + 1);\r\n\r\n let prevK: number;\r\n if (k === -d) {\r\n // 只能在 k+1 方向\r\n prevK = k + 1;\r\n } else if (k === d) {\r\n // 只能在 k-1 方向\r\n prevK = k - 1;\r\n } else if ((prevKDown ?? -1) < (prevKUp ?? -1)) {\r\n // k+1 方向的 V 值更大,从那里过来(删除)\r\n prevK = k + 1;\r\n } else {\r\n // k-1 方向的 V 值更大,从那里过来(添加)\r\n prevK = k - 1;\r\n }\r\n\r\n const prevX = prevSnapshot.get(prevK) ?? 0;\r\n const prevY = prevX - prevK;\r\n\r\n // ===== 从 (prevX, prevY) 到 (x, y) 的回溯路径 =====\r\n\r\n if (prevK === k - 1) {\r\n // prevK = k-1 → V[k-1] 被使用 → x = V[k-1] + 1 → 垂直移动(删除)\r\n // 路径: (prevX, prevY) → (prevX+1, prevY) [删除] → 蛇行 [(prevX+2, prevY+1), ...]\r\n // 回溯: 先走蛇行(相等行), 再走删除行\r\n\r\n // 第一步:蛇行(相等行),从 (x,y) 回到 (prevX+1, prevY)\r\n while (x > prevX + 1 && y > prevY) {\r\n x--;\r\n y--;\r\n result.unshift({ op: \"equal\", line: oldLines[x]! });\r\n }\r\n\r\n // 第二步:删除行\r\n if (x > prevX) {\r\n x--;\r\n result.unshift({ op: \"remove\", line: oldLines[x]! });\r\n }\r\n } else {\r\n // prevK = k+1 → V[k+1] 被使用 → x = V[k+1] → 水平移动(添加)\r\n // 路径: (prevX, prevY) → (prevX, prevY+1) [添加] → 蛇行 [(prevX+1, prevY+2), ...]\r\n // 回溯: 先走蛇行(相等行), 再走添加行\r\n\r\n // 第一步:蛇行(相等行),从 (x,y) 回到 (prevX, prevY+1)\r\n while (x > prevX && y > prevY + 1) {\r\n x--;\r\n y--;\r\n result.unshift({ op: \"equal\", line: oldLines[x]! });\r\n }\r\n\r\n // 第二步:添加行\r\n if (y > prevY) {\r\n y--;\r\n result.unshift({ op: \"add\", line: newLines[y]! });\r\n }\r\n }\r\n }\r\n\r\n // 处理初始蛇行:从 (0, 0) 到当前 (x, y) 的相等行\r\n // 逆向(从 (x, y) 到 (1, 1) 到 (0, 0) 回溯)\r\n while (x > 0 && y > 0) {\r\n x--;\r\n y--;\r\n result.unshift({ op: \"equal\", line: oldLines[x]! });\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Hunk 分组\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 上下文行数(unified diff 标准 3 行) */\r\nconst CONTEXT_LINES = 3;\r\n\r\n/**\r\n * 将 diff 行列表分组为 hunks。\r\n */\r\nfunction groupIntoHunks(diffLines: DiffLine[]): DiffHunk[] {\r\n if (diffLines.length === 0) return [];\r\n\r\n // 找出所有变更行的索引\r\n const changeIndices: number[] = [];\r\n for (let i = 0; i < diffLines.length; i++) {\r\n const line = diffLines[i]!;\r\n if (line.op !== \"equal\") {\r\n changeIndices.push(i);\r\n }\r\n }\r\n\r\n if (changeIndices.length === 0) return [];\r\n\r\n // 将相近的变更行分组\r\n const groups: number[][] = [[changeIndices[0]!]];\r\n\r\n for (let idx = 1; idx < changeIndices.length; idx++) {\r\n const lastGroup = groups[groups.length - 1]!;\r\n const prevIdx = lastGroup[lastGroup.length - 1]!;\r\n const currIdx = changeIndices[idx]!;\r\n\r\n let gap = 0;\r\n for (let k = prevIdx + 1; k < currIdx; k++) {\r\n const line = diffLines[k]!;\r\n if (line.op === \"equal\") gap++;\r\n }\r\n\r\n if (gap <= 2 * CONTEXT_LINES) {\r\n lastGroup.push(currIdx);\r\n } else {\r\n groups.push([currIdx]);\r\n }\r\n }\r\n\r\n // 为每个分组生成 hunk\r\n const hunks: DiffHunk[] = [];\r\n\r\n for (const group of groups) {\r\n const firstChangeIdx = group[0]!;\r\n const lastChangeIdx = group[group.length - 1]!;\r\n\r\n const startIdx = Math.max(0, firstChangeIdx - CONTEXT_LINES);\r\n const endIdx = Math.min(diffLines.length - 1, lastChangeIdx + CONTEXT_LINES);\r\n\r\n const lines = diffLines.slice(startIdx, endIdx + 1);\r\n\r\n let oldLine = 0;\r\n let newLine = 0;\r\n for (let i = 0; i < startIdx; i++) {\r\n const line = diffLines[i]!;\r\n if (line.op === \"equal\" || line.op === \"remove\") oldLine++;\r\n if (line.op === \"equal\" || line.op === \"add\") newLine++;\r\n }\r\n\r\n let oldCount = 0;\r\n let newCount = 0;\r\n for (const diffLine of lines) {\r\n if (diffLine.op === \"equal\" || diffLine.op === \"remove\") oldCount++;\r\n if (diffLine.op === \"equal\" || diffLine.op === \"add\") newCount++;\r\n }\r\n\r\n hunks.push({\r\n oldStart: oldLine + 1,\r\n oldCount,\r\n newStart: newLine + 1,\r\n newCount,\r\n lines,\r\n });\r\n }\r\n\r\n return hunks;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Unified diff 格式化\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction formatUnifiedDiff(hunks: DiffHunk[], filePath: string): string {\r\n if (hunks.length === 0) return \"\";\r\n\r\n const parts: string[] = [];\r\n const fileName = extractFileName(filePath);\r\n\r\n parts.push(`--- a/${fileName}`);\r\n parts.push(`+++ b/${fileName}`);\r\n\r\n for (const hunk of hunks) {\r\n parts.push(\r\n `@@ -${String(hunk.oldStart)},${String(hunk.oldCount)} +${String(hunk.newStart)},${String(hunk.newCount)} @@`,\r\n );\r\n\r\n for (const diffLine of hunk.lines) {\r\n switch (diffLine.op) {\r\n case \"equal\":\r\n parts.push(` ${diffLine.line}`);\r\n break;\r\n case \"remove\":\r\n parts.push(`-${diffLine.line}`);\r\n break;\r\n case \"add\":\r\n parts.push(`+${diffLine.line}`);\r\n break;\r\n }\r\n }\r\n }\r\n\r\n return parts.join(\"\\n\");\r\n}\r\n\r\nfunction formatNewFileDiff(lines: string[], filePath: string): string {\r\n const fileName = extractFileName(filePath);\r\n const parts: string[] = [];\r\n\r\n parts.push(`--- /dev/null`);\r\n parts.push(`+++ b/${fileName}`);\r\n parts.push(`@@ -0,0 +1,${String(lines.length)} @@`);\r\n\r\n for (const line of lines) {\r\n parts.push(`+${line}`);\r\n }\r\n\r\n return parts.join(\"\\n\");\r\n}\r\n\r\nfunction formatDeletedFileDiff(lines: string[], filePath: string): string {\r\n const fileName = extractFileName(filePath);\r\n const parts: string[] = [];\r\n\r\n parts.push(`--- a/${fileName}`);\r\n parts.push(`+++ /dev/null`);\r\n parts.push(`@@ -1,${String(lines.length)} +0,0 @@`);\r\n\r\n for (const line of lines) {\r\n parts.push(`-${line}`);\r\n }\r\n\r\n return parts.join(\"\\n\");\r\n}\r\n\r\nfunction extractFileName(filePath: string): string {\r\n const normalized = filePath.replace(/\\\\/g, \"/\");\r\n return normalized.split(\"/\").pop() ?? filePath;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 公共 API\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 计算两个文本之间的 unified diff,返回 FileDiff 对象。\r\n *\r\n * 使用 Myers O(ND) 算法产生最短编辑脚本,与 Git 的 diff 引擎同款。\r\n * 自动检测并保留原始文件的行尾风格(LF / CRLF)。\r\n */\r\nexport function computeFileDiff(\r\n oldContent: string,\r\n newContent: string,\r\n filePath: string,\r\n): FileDiff {\r\n if (oldContent === newContent) {\r\n return {\r\n filePath,\r\n patch: \"\",\r\n existedBefore: oldContent.length > 0,\r\n additions: 0,\r\n deletions: 0,\r\n };\r\n }\r\n\r\n if (oldContent.length === 0) {\r\n const split = splitLines(newContent);\r\n const patch = formatNewFileDiff(split.lines, filePath);\r\n return {\r\n filePath,\r\n patch,\r\n existedBefore: false,\r\n additions: split.lines.length,\r\n deletions: 0,\r\n };\r\n }\r\n\r\n if (newContent.length === 0) {\r\n const split = splitLines(oldContent);\r\n const patch = formatDeletedFileDiff(split.lines, filePath);\r\n return {\r\n filePath,\r\n patch,\r\n existedBefore: true,\r\n additions: 0,\r\n deletions: split.lines.length,\r\n };\r\n }\r\n\r\n const oldSplit = splitLines(oldContent);\r\n const newSplit = splitLines(newContent);\r\n\r\n const diffLines = computeLineDiff(oldSplit.lines, newSplit.lines);\r\n\r\n let additions = 0;\r\n let deletions = 0;\r\n for (const line of diffLines) {\r\n if (line.op === \"add\") additions++;\r\n if (line.op === \"remove\") deletions++;\r\n }\r\n\r\n const hunks = groupIntoHunks(diffLines);\r\n const patch = formatUnifiedDiff(hunks, filePath);\r\n\r\n return {\r\n filePath,\r\n patch,\r\n existedBefore: true,\r\n additions,\r\n deletions,\r\n };\r\n}\r\n\r\n/**\r\n * 将文件变更应用于原内容,返回新的文件内容。\r\n */\r\nexport function applyChange(\r\n kind: \"edit\" | \"create\" | \"delete\",\r\n oldContent: string,\r\n newContent: string,\r\n): string {\r\n if (kind === \"delete\") return \"\";\r\n if (kind === \"create\") return newContent;\r\n return newContent;\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 行尾(EOL)风格检测与保留 — 写工具统一使用此模块\r\n// ---------------------------------------------------------------------------\r\n//\r\n// 解决问题:原文件为 CRLF(Windows 项目常态)时,写工具若直接 writeFile\r\n// 新内容(往往以 LF 编码),会导致整文件 EOL 翻转,git diff 全文件飘红,\r\n// AI 看到大量\"假改动\"后陷入修复循环。此模块提供公共的 EOL 检测与落盘封装。\r\n//\r\n// 设计要点:\r\n// - 检测策略与 diff.ts 的 splitLines 保持一致:首个 CRLF 早于首个 LF → CRLF\r\n// - 对原内容做 EOL 探测,按探测结果统一新内容的行尾\r\n// - 保留原文件是否以行尾符结尾(trailing newline)的特征\r\n\r\nimport { writeFile } from \"node:fs/promises\";\r\n\r\n/**\r\n * 检测文本的行尾风格。\r\n *\r\n * 与 diff.ts 的 splitLines 探测策略一致:\r\n * - 找到最早出现的换行符位置;若最早的是 \\r\\n 则判为 CRLF,否则 LF\r\n *\r\n * @returns 行尾字符串:\"\\r\\n\" 或 \"\\n\";空文本默认 \"\\n\"\r\n */\r\nexport function detectEol(text: string): string {\r\n if (text.length === 0) return \"\\n\";\r\n\r\n const crlfIdx = text.indexOf(\"\\r\\n\");\r\n const lfIdx = text.indexOf(\"\\n\");\r\n\r\n if (crlfIdx !== -1 && (lfIdx === -1 || crlfIdx <= lfIdx)) {\r\n return \"\\r\\n\";\r\n }\r\n return \"\\n\";\r\n}\r\n\r\n/**\r\n * 检测文本是否以行尾符结尾(trailing newline)。\r\n */\r\nexport function hasTrailingNewline(text: string): boolean {\r\n return text.endsWith(\"\\n\") || text.endsWith(\"\\r\\n\");\r\n}\r\n\r\n/**\r\n * 把行尾统一为 LF(\\n),仅供「匹配 / 比较」使用,不要用于落盘。\r\n *\r\n * 解决问题:read_file 展示 CRLF 文件时每行末尾会残留 `\\r`,而 LLM 通常\r\n * 用 LF 编写 old_text,导致 edit_file / multi_edit / delete_range 的精确\r\n * 匹配在 Windows CRLF 文件上整段失败、反复重试(见会话日志\r\n * e65f0205 中 round 8/10/13 的 TEXT_NOT_FOUND 三连)。\r\n *\r\n * 策略:匹配前对文件内容与待匹配文本都做 LF 归一化,落盘时再由\r\n * normalizeEol / writeFileWithEol 还原为原 EOL,既避免误匹配又不产生\r\n * EOL 翻转噪声。仅替换 `\\r\\n` → `\\n`;罕见的独立 `\\r`(旧 Mac 风格)\r\n * 也一并归一以保持行语义。\r\n */\r\nexport function toLf(text: string): string {\r\n if (!text.includes(\"\\r\")) return text;\r\n return text.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\r\n}\r\n\r\n/**\r\n * 按给定 EOL 规整文本,并保留与原内容一致的\"是否以行尾结尾\"特征。\r\n *\r\n * 规整步骤:\r\n * 1. 先统一拆成行片段(兼容现有 \\r\\n 与 \\n 混合)\r\n * 2. 用目标 EOL 重新拼接\r\n * 3. 若原内容以行尾结尾但规整后没有,补一个;反之不补\r\n *\r\n * 用于写入前对 newContent 做归一化,保证不在 EOL 上产生噪声 diff。\r\n *\r\n * @param originalContent 原文件内容(用于探测 EOL 风格与尾行尾特征)\r\n * @param newContent 待写入的新内容\r\n * @returns 应实际落盘的内容\r\n */\r\nexport function normalizeEol(originalContent: string, newContent: string): string {\r\n const targetEol = detectEol(originalContent);\r\n\r\n // 拆行:兼容混合 \\r\\n / \\n;先把 \\r\\n 统一成 \\n 再按 \\n 拆\r\n const lines = newContent.replace(/\\r\\n/g, \"\\n\").split(\"\\n\");\r\n\r\n // 判断原内容是否以行尾符结尾\r\n const originalHasTrailing = hasTrailingNewline(originalContent);\r\n\r\n // newContent 经 replace+split 后,行尾结尾会在末尾多一个空行元素\r\n // 如 \"a\\nb\\n\" → [\"a\",\"b\",\"\"],\"a\\nb\" → [\"a\",\"b\"]\r\n const newHasTrailing = lines.length > 0 && lines[lines.length - 1] === \"\";\r\n\r\n // 若新内容原本就带尾行尾,split 出的末尾空字符串保留即可\r\n // 若新内容不带尾行尾,但原内容带,则补一个\r\n let result = lines.join(targetEol);\r\n if (originalHasTrailing && !newHasTrailing) {\r\n result += targetEol;\r\n }\r\n // 若新内容带尾行尾但原内容不带:去掉末尾 EOL\r\n if (!originalHasTrailing && newHasTrailing) {\r\n // result 此时末尾恰有一个 targetEol,截悼\r\n if (result.endsWith(targetEol)) {\r\n result = result.slice(0, -targetEol.length);\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * 写文件并保留原文件的 EOL 风格与尾行尾特征。\r\n *\r\n * - 原内容存在时:按原 EOL 归一化新内容后写入\r\n * - 原内容不存在(新建文件):LF + 保留新内容自带的尾行尾特征\r\n *\r\n * 所有写工具(write_file / edit_file / multi_edit / delete_range)应统一走此函数,\r\n * 避免 EOL 翻转造成的全文件 diff 风暴。\r\n *\r\n * @param filePath 目标文件绝对路径\r\n * @param originalContent 原文件内容;文件不存在时传空字符串\r\n * @param newContent 新内容\r\n */\r\nexport async function writeFileWithEol(\r\n filePath: string,\r\n originalContent: string,\r\n newContent: string,\r\n): Promise<void> {\r\n const content =\r\n originalContent.length > 0\r\n ? normalizeEol(originalContent, newContent)\r\n : newContent;\r\n await writeFile(filePath, content, \"utf-8\");\r\n}","// ---------------------------------------------------------------------------\r\n// read_file 工具 — 读取指定路径的文件内容\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readFile, stat } from \"node:fs/promises\";\r\nimport { open } from \"node:fs/promises\";\r\nimport { relative } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { resolvePath, truncateOutput } from \"../sandbox.js\";\r\nimport { toLf } from \"../eol.js\";\r\n\r\n/** read_file 工具的参数格式 */\r\nexport interface ReadFileArgs {\r\n /** 文件路径(相对于 cwd 或绝对路径) */\r\n path: string;\r\n /** 起始行号(1-based),默认 1 */\r\n startLine?: number;\r\n /** 结束行号(1-based,包含),默认到文件末尾 */\r\n endLine?: number;\r\n}\r\n\r\n/** 读取前 N 字节检测是否为二进制文件 */\r\nasync function checkBinary(filePath: string): Promise<boolean> {\r\n const fileHandle = await open(filePath, \"r\");\r\n try {\r\n const buffer = Buffer.alloc(8192); // 8KB\r\n const { bytesRead } = await fileHandle.read(buffer, 0, 8192, 0);\r\n return buffer.subarray(0, bytesRead).includes(0); // NUL 字节 → 二进制\r\n } finally {\r\n await fileHandle.close();\r\n }\r\n}\r\n\r\n/**\r\n * read_file 工具 — 读取文件内容,支持行号范围选择。\r\n *\r\n * 功能:\r\n * - 按行号范围读取部分内容\r\n * - 自动添加行号前缀(→ 分隔)\r\n * - 文件大小限制(默认 10MB)\r\n * - 输出长度截断(默认 50K 字符)\r\n * - 二进制检测(阻止读取二进制文件)\r\n */\r\nexport const readFileTool: AgentTool<ReadFileArgs> = {\r\n name: \"read_file\",\r\n kind: ToolKind.Read,\r\n description:\r\n \"读取指定路径的文件内容。支持行号范围选择,输出带行号。适用于查看源代码、配置文件等文本文件。自动拒绝二进制文件。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n path: {\r\n type: \"string\",\r\n description: \"文件路径(相对于当前工作目录或绝对路径)\",\r\n },\r\n startLine: {\r\n type: \"number\",\r\n description: \"起始行号(从 1 开始),默认为 1\",\r\n },\r\n endLine: {\r\n type: \"number\",\r\n description: \"结束行号(包含),默认到文件末尾\",\r\n },\r\n },\r\n required: [\"path\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: ReadFileArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.path || typeof args.path !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 path\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const filePath = resolvePath(args.path, ctx.cwd);\r\n\r\n try {\r\n // 检查文件大小\r\n const fileStat = await stat(filePath);\r\n const maxSize = 10 * 1024 * 1024; // 10MB\r\n if (fileStat.size > maxSize) {\r\n return {\r\n success: false,\r\n data: `文件过大(${(fileStat.size / 1024 / 1024).toFixed(1)}MB),超过 10MB 限制`,\r\n error: \"FILE_TOO_LARGE\",\r\n };\r\n }\r\n\r\n // 检查是否为目录\r\n if (fileStat.isDirectory()) {\r\n return {\r\n success: false,\r\n data: `\"${filePath}\" 是一个目录,请使用 ls 工具查看目录内容`,\r\n error: \"IS_DIRECTORY\",\r\n };\r\n }\r\n\r\n // 二进制检测:扫描前 8KB 看是否包含 NUL 字节\r\n if (fileStat.size > 0) {\r\n const isBin = await checkBinary(filePath);\r\n if (isBin) {\r\n return {\r\n success: false,\r\n data: `\"${filePath}\" 看起来是二进制文件,不支持读取`,\r\n error: \"BINARY_FILE\",\r\n };\r\n }\r\n }\r\n\r\n const content = await readFile(filePath, \"utf-8\");\r\n // 按 LF 归一化后拆行:CRLF 文件每行末尾不再残留 `\\r`,展示干净,\r\n // 也与 edit_file / multi_edit / delete_range 的 LF 归一化匹配保持一致\r\n // —— LLM 看到什么就能直接拿去作 old_text/锐点。\r\n const lines = toLf(content).split(\"\\n\");\r\n\r\n // 如果是空文件,最后一项为空行,pop 掉\r\n if (lines.length > 0 && lines[lines.length - 1] === \"\" && content.endsWith(\"\\n\")) {\r\n lines.pop();\r\n }\r\n\r\n // 行号范围处理(1-based → 0-based)\r\n const startLine = Math.max(1, args.startLine ?? 1) - 1;\r\n const endLine = args.endLine\r\n ? Math.min(args.endLine, lines.length)\r\n : lines.length;\r\n const selectedLines = lines.slice(startLine, endLine);\r\n\r\n // 添加行号前缀(右对齐 + →)\r\n const lineNumWidth = String(endLine).length;\r\n const result = selectedLines\r\n .map((line, i) => {\r\n const lineNum = String(startLine + i + 1).padStart(lineNumWidth, \" \");\r\n return `${lineNum}→${line}`;\r\n })\r\n .join(\"\\n\");\r\n\r\n // 尾部提示:告知剩余行数\r\n const remaining = lines.length - endLine;\r\n const tailHint = remaining > 0\r\n ? `\\n\\n[还有 ${remaining} 行;使用 startLine=${endLine + 1} 继续查看]`\r\n : \"\";\r\n\r\n // UI 摘要:仅显示路径与行数范围,避免把文件内容塞进 UI\r\n const relPath = relative(ctx.cwd, filePath).replace(/\\\\/g, \"/\");\r\n const rangeLabel = (startLine > 0 || endLine < lines.length)\r\n ? `第 ${startLine + 1}-${endLine} 行`\r\n : `${lines.length} 行`;\r\n\r\n return {\r\n success: true,\r\n data: truncateOutput(result) + tailHint,\r\n summary: `📖 ${relPath}(${rangeLabel})`,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `读取文件失败:${message}`,\r\n error: \"READ_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// write_file 工具 — 创建或覆盖文件\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { mkdir, readFile } from \"node:fs/promises\";\r\nimport { dirname, relative, basename } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { resolvePath, confine } from \"../sandbox.js\";\r\nimport { computeFileDiff } from \"../diff.js\";\r\nimport { writeFileWithEol } from \"../eol.js\";\r\n\r\n/** write_file 工具的参数格式 */\r\nexport interface WriteFileArgs {\r\n /** 文件路径(相对于 cwd 或绝对路径) */\r\n path: string;\r\n /** 要写入的文件内容 */\r\n content: string;\r\n}\r\n\r\n/**\r\n * write_file 工具 — 创建或覆盖文件。\r\n */\r\nexport const writeFileTool: AgentTool<WriteFileArgs> = {\r\n name: \"write_file\",\r\n kind: ToolKind.Edit,\r\n description:\r\n \"创建或覆盖文件。如果父目录不存在会自动创建。适用于创建新文件或完全替换文件内容。请勿用此工具创建 _temp_、_debug_ 等用于诊断/调试的临时文件——如需诊断请改用 bash 工具内联脚本(node -e)。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n path: {\r\n type: \"string\",\r\n description: \"文件路径(相对于当前工作目录或绝对路径)\",\r\n },\r\n content: {\r\n type: \"string\",\r\n description: \"要写入的文件内容\",\r\n },\r\n },\r\n required: [\"path\", \"content\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: WriteFileArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.path || typeof args.path !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 path\", error: \"INVALID_ARGS\" };\r\n }\r\n if (args.content === undefined || args.content === null) {\r\n return { success: false, data: \"缺少必要参数 content\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const filePath = resolvePath(args.path, ctx.cwd);\r\n\r\n try {\r\n if (ctx.writeRoots && ctx.writeRoots.length > 0) {\r\n const conf = await confine(ctx.writeRoots, filePath);\r\n if (!conf.ok) {\r\n return { success: false, data: conf.error, error: \"OUTSIDE_WRITE_ROOTS\" };\r\n }\r\n }\r\n\r\n let oldContent = \"\";\r\n let existedBefore = false;\r\n try {\r\n oldContent = await readFile(filePath, \"utf-8\");\r\n existedBefore = true;\r\n } catch {\r\n // 文件不存在,这是新建文件\r\n }\r\n\r\n await mkdir(dirname(filePath), { recursive: true });\r\n\r\n const content = args.content;\r\n await writeFileWithEol(filePath, oldContent, content);\r\n\r\n const diff = computeFileDiff(oldContent, content, filePath);\r\n diff.existedBefore = existedBefore;\r\n\r\n const lineCount = content.split(\"\\n\").length;\r\n const byteSize = Buffer.byteLength(content, \"utf-8\");\r\n\r\n const action = existedBefore ? \"已修改\" : \"已创建\";\r\n const diffSummary = existedBefore\r\n ? `,+${diff.additions} -${diff.deletions}`\r\n : `,+${diff.additions} 行(新建)`;\r\n\r\n const fileName = basename(filePath);\r\n const summary = existedBefore\r\n ? `📝 修改: ${fileName} (+${diff.additions} -${diff.deletions})`\r\n : `📝 新建: ${fileName} (+${diff.additions} 行)`;\r\n\r\n return {\r\n success: true,\r\n data: `文件${action}:${relative(ctx.cwd, filePath).replace(/\\\\/g, \"/\")}(${lineCount} 行,${byteSize} 字节${diffSummary})`,\r\n summary,\r\n diff,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `写入文件失败:${message}`,\r\n error: \"WRITE_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// edit_file 工具 — 精确字符串替换编辑文件\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { writeFile } from \"node:fs/promises\";\r\nimport { basename } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { resolvePath, confine } from \"../sandbox.js\";\r\nimport { computeFileDiff } from \"../diff.js\";\r\nimport { normalizeEol, toLf } from \"../eol.js\";\r\n\r\n/** edit_file 工具的参数格式 */\r\nexport interface EditFileArgs {\r\n /** 文件路径(相对于 cwd 或绝对路径) */\r\n path: string;\r\n /** 要查找的原始文本(精确匹配) */\r\n old_text: string;\r\n /** 替换后的新文本 */\r\n new_text: string;\r\n}\r\n\r\n/**\r\n * edit_file 工具 — 对文件进行精确字符串替换。\r\n */\r\nexport const editFileTool: AgentTool<EditFileArgs> = {\r\n name: \"edit_file\",\r\n kind: ToolKind.Edit,\r\n description:\r\n \"对文件进行精确字符串替换。查找文件中的 old_text 并替换为 new_text。如果 old_text 出现多次或未找到则报错。适用于小范围精确修改。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n path: {\r\n type: \"string\",\r\n description: \"文件路径(相对于当前工作目录或绝对路径)\",\r\n },\r\n old_text: {\r\n type: \"string\",\r\n description: \"要查找的原始文本(精确匹配)\",\r\n },\r\n new_text: {\r\n type: \"string\",\r\n description: \"替换后的新文本\",\r\n },\r\n },\r\n required: [\"path\", \"old_text\", \"new_text\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: EditFileArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.path || typeof args.path !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 path\", error: \"INVALID_ARGS\" };\r\n }\r\n if (typeof args.old_text !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 old_text\", error: \"INVALID_ARGS\" };\r\n }\r\n if (typeof args.new_text !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 new_text\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const filePath = resolvePath(args.path, ctx.cwd);\r\n\r\n if (ctx.writeRoots && ctx.writeRoots.length > 0) {\r\n const conf = await confine(ctx.writeRoots, filePath);\r\n if (!conf.ok) {\r\n return { success: false, data: conf.error, error: \"OUTSIDE_WRITE_ROOTS\" };\r\n }\r\n }\r\n\r\n try {\r\n const content = await readFile(filePath, \"utf-8\");\r\n\r\n // 匹配在 LF 归一化空间进行:原文件可能是 CRLF,而 LLM 习惯用 LF\r\n // 编写 old_text/new_text。这里把两端都归一为 LF 再 indexOf,落盘时\r\n // 再由 normalizeEol 还原为原 EOL,避免「CRLF 文件整段匹配失败」的\r\n // 反复重试,也不产生 EOL 翻转噪声。\r\n const contentN = toLf(content);\r\n const oldTextN = toLf(args.old_text);\r\n\r\n const firstIndex = contentN.indexOf(oldTextN);\r\n if (firstIndex === -1) {\r\n return {\r\n success: false,\r\n data: `未找到要替换的文本。请确认 old_text 与文件内容完全一致(包括缩进和空格)。`,\r\n error: \"TEXT_NOT_FOUND\",\r\n };\r\n }\r\n\r\n const secondIndex = contentN.indexOf(oldTextN, firstIndex + 1);\r\n if (secondIndex !== -1) {\r\n return {\r\n success: false,\r\n data: `要替换的文本在文件中出现多次,请提供更多上下文以精确定位。`,\r\n error: \"TEXT_MULTIPLE_MATCHES\",\r\n };\r\n }\r\n\r\n const newContentN =\r\n contentN.slice(0, firstIndex) +\r\n toLf(args.new_text) +\r\n contentN.slice(firstIndex + oldTextN.length);\r\n // 按原文件 EOL 还原后再落盘,diff 也基于实际落盘内容计算。\r\n const writtenContent = normalizeEol(content, newContentN);\r\n await writeFile(filePath, writtenContent, \"utf-8\");\r\n\r\n const diff = computeFileDiff(content, writtenContent, filePath);\r\n diff.existedBefore = true;\r\n\r\n const beforeText = contentN.slice(0, firstIndex);\r\n const startLine = beforeText.split(\"\\n\").length;\r\n const oldLines = oldTextN.split(\"\\n\").length;\r\n const newLines = toLf(args.new_text).split(\"\\n\").length;\r\n\r\n const diffSummary = `+${diff.additions} -${diff.deletions}`;\r\n const summary = `📝 修改: ${basename(filePath)} (+${diff.additions} -${diff.deletions})`;\r\n\r\n return {\r\n success: true,\r\n data: `文件已编辑:${filePath}\\n替换位置:第 ${startLine} 行\\n${oldLines} 行 → ${newLines} 行\\n变更:${diffSummary}`,\r\n summary,\r\n diff,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `编辑文件失败:${message}`,\r\n error: \"EDIT_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// multi_edit 工具 — 精确字符串批量替换编辑文件(原子性)\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { writeFile } from \"node:fs/promises\";\r\nimport { basename } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { resolvePath, confine } from \"../sandbox.js\";\r\nimport { computeFileDiff } from \"../diff.js\";\r\nimport { normalizeEol, toLf } from \"../eol.js\";\r\n\r\n/** 单个编辑步骤 */\r\nexport interface EditStep {\r\n /** 要查找的原始文本(精确匹配) */\r\n oldText: string;\r\n /** 替换后的新文本 */\r\n newText: string;\r\n /** 是否替换文件中所有匹配项,默认 false */\r\n replaceAll?: boolean;\r\n}\r\n\r\n/** multi_edit 工具的参数格式 */\r\nexport interface MultiEditArgs {\r\n /** 文件路径(相对于 cwd 或绝对路径) */\r\n path: string;\r\n /** 有序编辑步骤数组 */\r\n edits: EditStep[];\r\n}\r\n\r\n/**\r\n * multi_edit 工具 — 对文件进行原子批量替换编辑。\r\n */\r\nexport const multiEditTool: AgentTool<MultiEditArgs> = {\r\n name: \"multi_edit\",\r\n kind: ToolKind.Edit,\r\n description:\r\n \"对文件进行原子批量替换编辑。在一个请求中执行多个精确替换,任一失败则全部回滚。支持 replaceAll 参数做全局替换。适用于需要同时修改文件多处的场景。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n path: {\r\n type: \"string\",\r\n description: \"文件路径(相对于当前工作目录或绝对路径)\",\r\n },\r\n edits: {\r\n type: \"array\",\r\n description: \"有序的编辑步骤列表。每个步骤包含 oldText(精确匹配)、newText(替换内容)和可选的 replaceAll(是否替换所有匹配项)\",\r\n items: {\r\n type: \"object\",\r\n properties: {\r\n oldText: { type: \"string\", description: \"要查找的原始文本(精确匹配)\" },\r\n newText: { type: \"string\", description: \"替换后的新文本\" },\r\n replaceAll: { type: \"boolean\", description: \"是否替换所有匹配项,默认 false\" },\r\n },\r\n required: [\"oldText\", \"newText\"],\r\n additionalProperties: false,\r\n },\r\n },\r\n },\r\n required: [\"path\", \"edits\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: MultiEditArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.path || typeof args.path !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 path\", error: \"INVALID_ARGS\" };\r\n }\r\n if (!Array.isArray(args.edits) || args.edits.length === 0) {\r\n return { success: false, data: \"缺少必要参数 edits(非空数组)\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const filePath = resolvePath(args.path, ctx.cwd);\r\n\r\n if (ctx.writeRoots && ctx.writeRoots.length > 0) {\r\n const conf = await confine(ctx.writeRoots, filePath);\r\n if (!conf.ok) {\r\n return { success: false, data: conf.error, error: \"OUTSIDE_WRITE_ROOTS\" };\r\n }\r\n }\r\n\r\n try {\r\n const originalContent = await readFile(filePath, \"utf-8\");\r\n // 在 LF 归一化空间执行替换循环:原文件可能是 CRLF,LLM 提供的\r\n // oldText/newText 习惯用 LF。归一化后匹配,最后一步统一还原原 EOL。\r\n let currentContent = toLf(originalContent);\r\n\r\n for (let idx = 0; idx < args.edits.length; idx++) {\r\n const step = args.edits[idx]!;\r\n if (typeof step.oldText !== \"string\") {\r\n return {\r\n success: false,\r\n data: `第 ${idx + 1} 步:缺少有效的 oldText`,\r\n error: \"INVALID_STEP_ARGS\",\r\n };\r\n }\r\n if (typeof step.newText !== \"string\") {\r\n return {\r\n success: false,\r\n data: `第 ${idx + 1} 步:缺少有效的 newText`,\r\n error: \"INVALID_STEP_ARGS\",\r\n };\r\n }\r\n\r\n const oldTextN = toLf(step.oldText);\r\n const newTextN = toLf(step.newText);\r\n\r\n if (step.replaceAll) {\r\n let count = 0;\r\n let pos = -1;\r\n while ((pos = currentContent.indexOf(oldTextN, pos + 1)) !== -1) {\r\n count++;\r\n }\r\n\r\n if (count === 0) {\r\n return {\r\n success: false,\r\n data: `第 ${idx + 1} 步:未找到要替换的文本,请确认 oldText 与文件内容完全一致`,\r\n error: \"TEXT_NOT_FOUND\",\r\n };\r\n }\r\n\r\n currentContent = currentContent.split(oldTextN).join(newTextN);\r\n } else {\r\n const firstIdx = currentContent.indexOf(oldTextN);\r\n if (firstIdx === -1) {\r\n return {\r\n success: false,\r\n data: `第 ${idx + 1} 步:未找到要替换的文本。请确认 oldText 与文件内容完全一致(包括缩进和空格)。`,\r\n error: \"TEXT_NOT_FOUND\",\r\n };\r\n }\r\n\r\n const secondIdx = currentContent.indexOf(oldTextN, firstIdx + 1);\r\n if (secondIdx !== -1) {\r\n return {\r\n success: false,\r\n data: `第 ${idx + 1} 步:要替换的文本在文件中出现多次,请提供更多上下文以精确定位,或设置 replaceAll=true`,\r\n error: \"TEXT_MULTIPLE_MATCHES\",\r\n };\r\n }\r\n\r\n currentContent =\r\n currentContent.slice(0, firstIdx) +\r\n newTextN +\r\n currentContent.slice(firstIdx + oldTextN.length);\r\n }\r\n }\r\n\r\n // 按原文件 EOL 还原后再落盘,diff 也基于实际落盘内容计算。\r\n const writtenContent = normalizeEol(originalContent, currentContent);\r\n await writeFile(filePath, writtenContent, \"utf-8\");\r\n\r\n const diff = computeFileDiff(originalContent, writtenContent, filePath);\r\n diff.existedBefore = true;\r\n\r\n return {\r\n success: true,\r\n data: `文件已编辑:${filePath}\\n共执行 ${args.edits.length} 步替换\\n变更:+${diff.additions} -${diff.deletions}`,\r\n summary: `📝 修改: ${basename(filePath)} (${args.edits.length} 步, +${diff.additions} -${diff.deletions})`,\r\n diff,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `批量编辑失败:${message}`,\r\n error: \"MULTI_EDIT_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// delete_range 工具 — 按行锚点删除文件中的行范围\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { writeFile } from \"node:fs/promises\";\r\nimport { basename } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { resolvePath, confine } from \"../sandbox.js\";\r\nimport { computeFileDiff } from \"../diff.js\";\r\nimport { normalizeEol, toLf } from \"../eol.js\";\r\n\r\n/** delete_range 工具的参数格式 */\r\nexport interface DeleteRangeArgs {\r\n /** 文件路径(相对于 cwd 或绝对路径) */\r\n path: string;\r\n /** 删除范围的起始标记行(必须唯一) */\r\n startAnchor: string;\r\n /** 删除范围的结束标记行(必须唯一,须在 start_anchor 之后) */\r\n endAnchor: string;\r\n /** 是否包含锚点行本身,默认 false(只删除中间内容) */\r\n inclusive?: boolean;\r\n}\r\n\r\n/**\r\n * 在行列表中查找唯一匹配的行号。\r\n *\r\n * 锐点与行内容均先做 LF 归一化(去掊尾 `\\r`)再比较,避免 CRLF 文件上\r\n * 锐点整行失配。\r\n */\r\nfunction findUniqueLine(lines: string[], anchor: string, label: string): { line: number } | { error: string } {\r\n const anchorN = toLf(anchor);\r\n const matches: number[] = [];\r\n for (let i = 0; i < lines.length; i++) {\r\n if (toLf(lines[i]!) === anchorN) {\r\n matches.push(i);\r\n }\r\n }\r\n\r\n if (matches.length === 0) {\r\n return { error: `未找到 \"${label}\" 锚点行,请确认内容完全一致` };\r\n }\r\n if (matches.length > 1) {\r\n return { error: `\"${label}\" 锚点行在文件中出现 ${matches.length} 次,请使用更唯一的行内容` };\r\n }\r\n\r\n return { line: matches[0]! };\r\n}\r\n\r\n/**\r\n * delete_range 工具 — 删除文件中两个锚点行之间的内容。\r\n */\r\nexport const deleteRangeTool: AgentTool<DeleteRangeArgs> = {\r\n name: \"delete_range\",\r\n kind: ToolKind.Edit,\r\n description:\r\n \"删除文件中两个锚点行之间的内容。通过两个唯一的行内容精确定位范围。适用于删除方法、代码块、配置段等。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n path: {\r\n type: \"string\",\r\n description: \"文件路径(相对于当前工作目录或绝对路径)\",\r\n },\r\n startAnchor: {\r\n type: \"string\",\r\n description: \"删除范围的起始标记行内容(必须在文件中唯一,精确匹配整行)\",\r\n },\r\n endAnchor: {\r\n type: \"string\",\r\n description: \"删除范围的结束标记行内容(必须在文件中唯一,精确匹配整行,须在 start_anchor 之后)\",\r\n },\r\n inclusive: {\r\n type: \"boolean\",\r\n description: \"是否包含锚点行本身,默认 false(只删除两行之间的内容)\",\r\n },\r\n },\r\n required: [\"path\", \"startAnchor\", \"endAnchor\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: DeleteRangeArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.path || typeof args.path !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 path\", error: \"INVALID_ARGS\" };\r\n }\r\n if (typeof args.startAnchor !== \"string\") {\r\n return { success: false, data: \"缺少有效参数 startAnchor\", error: \"INVALID_ARGS\" };\r\n }\r\n if (typeof args.endAnchor !== \"string\") {\r\n return { success: false, data: \"缺少有效参数 endAnchor\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const filePath = resolvePath(args.path, ctx.cwd);\r\n const inclusive = args.inclusive ?? false;\r\n\r\n if (ctx.writeRoots && ctx.writeRoots.length > 0) {\r\n const conf = await confine(ctx.writeRoots, filePath);\r\n if (!conf.ok) {\r\n return { success: false, data: conf.error, error: \"OUTSIDE_WRITE_ROOTS\" };\r\n }\r\n }\r\n\r\n try {\r\n const content = await readFile(filePath, \"utf-8\");\r\n // 按实际行尾拆行(兼容 CRLF / LF),每行去掊尾 `\\r` 后再做锐点比较。\r\n const lines = toLf(content).split(\"\\n\");\r\n\r\n const startResult = findUniqueLine(lines, args.startAnchor, \"start_anchor\");\r\n if (\"error\" in startResult) {\r\n return { success: false, data: startResult.error, error: \"ANCHOR_NOT_FOUND\" };\r\n }\r\n\r\n const endResult = findUniqueLine(lines, args.endAnchor, \"end_anchor\");\r\n if (\"error\" in endResult) {\r\n return { success: false, data: endResult.error, error: \"ANCHOR_NOT_FOUND\" };\r\n }\r\n\r\n const startLine = startResult.line;\r\n const endLine = endResult.line;\r\n\r\n if (startLine >= endLine) {\r\n return {\r\n success: false,\r\n data: \"end_anchor 必须在 start_anchor 之后\",\r\n error: \"ANCHOR_ORDER_ERROR\",\r\n };\r\n }\r\n\r\n const rangeStart = inclusive ? startLine : startLine + 1;\r\n const rangeEnd = inclusive ? endLine : endLine - 1;\r\n\r\n if (rangeStart > rangeEnd) {\r\n return {\r\n success: false,\r\n data: \"删除范围为空(两行锚点相邻且 inclusive=false)\",\r\n error: \"EMPTY_RANGE\",\r\n };\r\n }\r\n\r\n const newLines = [...lines.slice(0, rangeStart), ...lines.slice(rangeEnd + 1)];\r\n const newContentN = newLines.join(\"\\n\");\r\n // 按原文件 EOL 还原后再落盘,diff 也基于实际落盘内容计算。\r\n const writtenContent = normalizeEol(content, newContentN);\r\n await writeFile(filePath, writtenContent, \"utf-8\");\r\n\r\n const diff = computeFileDiff(content, writtenContent, filePath);\r\n diff.existedBefore = true;\r\n\r\n const deletedLines = rangeEnd - rangeStart + 1;\r\n\r\n const summary = `📝 修改: ${basename(filePath)} (删 ${deletedLines} 行, +${diff.additions} -${diff.deletions})`;\r\n\r\n return {\r\n success: true,\r\n data: `文件已编辑:${filePath}\\n删除行范围:第 ${rangeStart + 1} 行 ~ 第 ${rangeEnd + 1} 行(共 ${deletedLines} 行)\\n变更:+${diff.additions} -${diff.deletions}`,\r\n summary,\r\n diff,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `删除范围失败:${message}`,\r\n error: \"DELETE_RANGE_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// bash 工具 — 执行 shell 命令\r\n// ---------------------------------------------------------------------------\r\n\r\nimport process from \"node:process\";\r\nimport { existsSync } from \"node:fs\";\r\nimport { join } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { execCommand, truncateOutput, getDefaultTimeout } from \"../sandbox.js\";\r\n\r\n/** 是否为 Windows 平台 */\r\nconst isWindows = process.platform === \"win32\";\r\n\r\ninterface DetectResult {\r\n /** shell 可执行文件路径,Windows 未找到 Git Bash 时为 null */\r\n bin: string | null;\r\n /** shell 调用参数前缀,bash 用 [\"-c\"],cmd 用 [\"/c\"] */\r\n args: string[];\r\n /** 给 LLM 看的 shell 名称 */\r\n label: string;\r\n /** 给 LLM 看的语法指引 */\r\n hint: string;\r\n}\r\n\r\n/**\r\n * 探测本机可用的 bash。\r\n *\r\n * Windows 上优先 Git Bash(命令语法与 Unix 一致,跨平台命令可复用),\r\n * 找不到才回退 cmd.exe。这样 LLM 生成的 `pwd && ls` 在装有 Git Bash 的\r\n * Windows 机器上能直接跑通,不再像旧的“硬走 cmd”实现那样在中文\r\n * GBK 报错里循环重试(见曾经的日志\r\n * e3cb5aff-34c0-499a-a3be-dee13c18d14f 中 round 2-6 的 EXIT_CODE_1 风暴)。\r\n */\r\nfunction detectShell(): DetectResult {\r\n if (!isWindows) {\r\n return {\r\n bin: \"sh\",\r\n args: [\"-c\"],\r\n label: \"sh\",\r\n hint: \"当前 shell 是 bash 兼容语法,支持 pwd/ls/&&/管道等标准 Unix 工具。\",\r\n };\r\n }\r\n\r\n // 1. 用户显式指定的 bash 路径(优先级最高)\r\n const envBash = process.env.DSKCODE_BASH;\r\n if (envBash && existsSync(envBash)) {\r\n return { bin: envBash, args: [\"-c\"], label: \"Git Bash\", hint: SHELL_HINT_BASH };\r\n }\r\n\r\n // 2. 常见 Git for Windows 安装位置(C/D 盘 × Program Files × LocalAppData)\r\n // 同时覆盖 64 位默认目录与 32 位兼容目录,以及单用户安装。\r\n const roots = [\r\n process.env.ProgramFiles,\r\n process.env[\"ProgramFiles(x86)\"],\r\n process.env.LOCALAPPDATA ? join(process.env.LOCALAPPDATA, \"Programs\") : null,\r\n // 非 C 盘安装(你机器就是 D 盘),枚举几个常见盘符根\r\n \"D:\\\\Program Files\",\r\n \"E:\\\\Program Files\",\r\n ].filter((v): v is string => Boolean(v));\r\n\r\n for (const root of roots) {\r\n for (const sub of [\"Git\\\\bin\\\\bash.exe\", \"Git\\\\usr\\\\bin\\\\bash.exe\"]) {\r\n const candidate = join(root, sub);\r\n if (existsSync(candidate)) {\r\n return { bin: candidate, args: [\"-c\"], label: \"Git Bash\", hint: SHELL_HINT_BASH };\r\n }\r\n }\r\n }\r\n\r\n // 3. 回退 cmd.exe —— 命令语法不同,要在 hint 里明确告知 LLM\r\n return {\r\n bin: \"cmd\",\r\n args: [\"/c\"],\r\n label: \"cmd.exe\",\r\n hint:\r\n \"当前 shell 是 Windows cmd.exe。请使用 cmd 语法:路径用反斜杠或正斜杠均可、命令链用 & 或换行,避免使用 pwd/ls/&& 等 Unix 命令。\",\r\n };\r\n}\r\n\r\n/** Git Bash 检出后给 LLM 的语法提示 */\r\nconst SHELL_HINT_BASH =\r\n \"当前 shell 是 bash 兼容语法,支持 pwd/ls/&&/管道等标准 Unix 工具。\";\r\n\r\nconst SHELL_INFO = detectShell();\r\n\r\n/** bash 工具的参数格式 */\r\nexport interface BashArgs {\r\n /** 要执行的命令 */\r\n command: string;\r\n /** 执行超时时间(毫秒),默认 30000 */\r\n timeout?: number;\r\n}\r\n\r\n/** 仅供测试导出当前探测到的 shell 信息(不参与运行期逻辑) */\r\nexport const __shellInfo = SHELL_INFO;\r\n\r\n/**\r\n * bash 工具 — 在 shell 中执行命令。\r\n */\r\nexport const bashTool: AgentTool<BashArgs> = {\r\n name: \"bash\",\r\n kind: ToolKind.Other,\r\n description: [\r\n \"在 shell 中执行命令,返回 stdout / stderr / 退出码。支持超时控制和信号中止。\",\r\n `当前 shell:${SHELL_INFO.label}。${SHELL_INFO.hint}`,\r\n \"读取文件请优先使用 read_file 工具,不要用 cat/type/Get-Content;\",\r\n \"搜索代码用 grep / glob,列目录用 ls——这些专用工具在所有平台都可用且无需关心 shell 语法。\",\r\n ].join(\"\"),\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n command: {\r\n type: \"string\",\r\n description: `要执行的 shell 命令(当前 shell:${SHELL_INFO.label})`,\r\n },\r\n timeout: {\r\n type: \"number\",\r\n description: \"执行超时时间(毫秒),默认 30000\",\r\n },\r\n },\r\n required: [\"command\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: BashArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.command || typeof args.command !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 command\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const timeout = args.timeout ?? ctx.timeout ?? getDefaultTimeout();\r\n\r\n try {\r\n // shell 已在启动期按平台探测好:Windows 走 Git Bash,没有才回退 cmd\r\n const shellCommand = SHELL_INFO.bin ?? \"cmd\";\r\n const shellArgs = [...SHELL_INFO.args, args.command];\r\n\r\n const result = await execCommand(\r\n shellCommand,\r\n shellArgs,\r\n ctx.cwd,\r\n timeout,\r\n ctx.signal,\r\n true,\r\n );\r\n\r\n const parts: string[] = [];\r\n if (result.stdout) {\r\n parts.push(truncateOutput(result.stdout));\r\n }\r\n if (result.stderr) {\r\n parts.push(`[stderr]\\n${truncateOutput(result.stderr)}`);\r\n }\r\n\r\n const success = result.exitCode === 0;\r\n const output = parts.length > 0 ? parts.join(\"\\n\") : \"(无输出)\";\r\n\r\n const cmdPreview = args.command.length > 60\r\n ? args.command.slice(0, 57) + \"...\"\r\n : args.command;\r\n const summary = `🔧 $ ${cmdPreview}(exit ${result.exitCode ?? \"未知\"})`;\r\n\r\n return {\r\n success,\r\n data: `${output}\\n[退出码: ${result.exitCode ?? \"未知\"}]`,\r\n summary,\r\n error: success ? undefined : `EXIT_CODE_${result.exitCode ?? \"UNKNOWN\"}`,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `命令执行失败:${message}`,\r\n error: \"EXECUTION_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// glob 工具 — 文件搜索(模式匹配)\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readdir, stat } from \"node:fs/promises\";\r\nimport { join, relative, isAbsolute } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { truncateOutput, stripMentionPrefix } from \"../sandbox.js\";\r\n\r\n/** glob 工具的参数格式 */\r\nexport interface GlobArgs {\r\n /** 搜索模式(支持 * 和 ** 通配符) */\r\n pattern: string;\r\n /** 搜索的起始目录,默认为 cwd */\r\n directory?: string;\r\n}\r\n\r\n/**\r\n * 将 glob 模式转换为正则表达式。\r\n */\r\nfunction globToRegex(pattern: string): RegExp {\r\n let regexStr = pattern;\r\n regexStr = regexStr.replace(/\\*\\*\\//g, \"<<GLOBSTAR_SLASH>>\");\r\n regexStr = regexStr.replace(/\\*\\*/g, \"<<GLOBSTAR>>\");\r\n regexStr = regexStr.replace(/\\?/g, \"<<QUESTION>>\");\r\n regexStr = regexStr.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\");\r\n regexStr = regexStr.replace(/\\*/g, \"[^/]*\");\r\n regexStr = regexStr.replace(/<<GLOBSTAR_SLASH>>/g, \"(.*/)?\");\r\n regexStr = regexStr.replace(/<<GLOBSTAR>>/g, \".*\");\r\n regexStr = regexStr.replace(/<<QUESTION>>/g, \"[^/]\");\r\n return new RegExp(`^${regexStr}$`, \"i\");\r\n}\r\n\r\n/**\r\n * 递归遍历目录收集文件路径。\r\n */\r\nasync function walkDir(dir: string, baseDir: string): Promise<string[]> {\r\n const results: string[] = [];\r\n let entries;\r\n\r\n try {\r\n entries = await readdir(dir, { withFileTypes: true });\r\n } catch {\r\n return results;\r\n }\r\n\r\n for (const entry of entries) {\r\n if (entry.isDirectory() && (entry.name === \"node_modules\" || entry.name === \".git\")) {\r\n continue;\r\n }\r\n\r\n const fullPath = join(dir, entry.name);\r\n const relPath = relative(baseDir, fullPath);\r\n\r\n if (entry.isDirectory()) {\r\n results.push(...await walkDir(fullPath, baseDir));\r\n } else {\r\n results.push(relPath);\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\n/**\r\n * glob 工具 — 按模式搜索文件路径。\r\n *\r\n * 功能:\r\n * - 支持 * 和 ** 通配符\r\n * - 自动跳过 node_modules 和 .git 目录\r\n * - 返回相对于搜索目录的路径列表\r\n */\r\nexport const globTool: AgentTool<GlobArgs> = {\r\n name: \"glob\",\r\n kind: ToolKind.Read,\r\n description:\r\n \"按模式搜索文件路径。支持 *(匹配文件名部分)和 **(匹配多层目录)通配符。自动跳过 node_modules 和 .git 目录。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n pattern: {\r\n type: \"string\",\r\n description: \"搜索模式(支持 * 和 ** 通配符,如 **/*.ts、src/**/*.test.ts)\",\r\n },\r\n directory: {\r\n type: \"string\",\r\n description: \"搜索的起始目录,默认为当前工作目录\",\r\n },\r\n },\r\n required: [\"pattern\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: GlobArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.pattern || typeof args.pattern !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 pattern\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const dir = args.directory ? stripMentionPrefix(args.directory) : undefined;\r\n const searchDir = dir\r\n ? (isAbsolute(dir) ? dir : join(ctx.cwd, dir))\r\n : ctx.cwd;\r\n const regex = globToRegex(args.pattern);\r\n\r\n try {\r\n const dirStat = await stat(searchDir);\r\n if (!dirStat.isDirectory()) {\r\n return { success: false, data: `路径不是目录:${searchDir}`, error: \"NOT_DIRECTORY\" };\r\n }\r\n\r\n const allFiles = await walkDir(searchDir, searchDir);\r\n const matched = allFiles.filter((f) => regex.test(f));\r\n\r\n if (matched.length === 0) {\r\n return {\r\n success: true,\r\n data: `未找到匹配 \"${args.pattern}\" 的文件`,\r\n summary: `${args.pattern} → 0 个文件`,\r\n };\r\n }\r\n\r\n const limit = 200;\r\n const limited = matched.slice(0, limit);\r\n const output = limited.join(\"\\n\");\r\n const suffix = matched.length > limit ? `\\n\\n... 共 ${matched.length} 个文件,只显示前 ${limit} 个` : \"\";\r\n\r\n return {\r\n success: true,\r\n data: truncateOutput(output + suffix),\r\n summary: `${args.pattern} → ${matched.length} 个文件`,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `文件搜索失败:${message}`,\r\n error: \"GLOB_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// grep 工具 — 文件内容正则搜索\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readdir, readFile, stat } from \"node:fs/promises\";\r\nimport { join, relative, isAbsolute } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { truncateOutput, stripMentionPrefix } from \"../sandbox.js\";\r\n\r\n/** grep 工具的参数格式 */\r\nexport interface GrepArgs {\r\n /** 正则表达式模式 */\r\n pattern: string;\r\n /** 搜索的目录路径,默认为 cwd */\r\n directory?: string;\r\n /** 文件扩展名过滤(如 \"ts\"、\"json\"),不含点号 */\r\n include?: string;\r\n /** 是否大小写敏感,默认 false */\r\n case_sensitive?: boolean;\r\n /** 最大搜索文件数,默认 200 */\r\n max_files?: number;\r\n}\r\n\r\n/** 单个匹配结果 */\r\ninterface GrepMatch {\r\n file: string;\r\n line: number;\r\n content: string;\r\n}\r\n\r\n/**\r\n * 递归遍历目录收集文件路径。\r\n */\r\nasync function collectFiles(\r\n dir: string,\r\n extension?: string,\r\n maxFiles = 200,\r\n): Promise<string[]> {\r\n const results: string[] = [];\r\n let entries;\r\n\r\n try {\r\n entries = await readdir(dir, { withFileTypes: true });\r\n } catch {\r\n return results;\r\n }\r\n\r\n for (const entry of entries) {\r\n if (results.length >= maxFiles) break;\r\n\r\n if (entry.isDirectory() && (entry.name === \"node_modules\" || entry.name === \".git\" || entry.name === \"dist\")) {\r\n continue;\r\n }\r\n\r\n const fullPath = join(dir, entry.name);\r\n\r\n if (entry.isDirectory()) {\r\n results.push(...await collectFiles(fullPath, extension, maxFiles - results.length));\r\n } else {\r\n if (extension && !entry.name.endsWith(`.${extension}`)) {\r\n continue;\r\n }\r\n results.push(fullPath);\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\n/**\r\n * grep 工具 — 在文件内容中搜索正则表达式匹配。\r\n */\r\nexport const grepTool: AgentTool<GrepArgs> = {\r\n name: \"grep\",\r\n kind: ToolKind.Read,\r\n description:\r\n \"在文件内容中搜索正则表达式。返回匹配行的文件路径、行号和内容。支持大小写敏感、文件扩展名过滤。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n pattern: {\r\n type: \"string\",\r\n description: \"正则表达式搜索模式\",\r\n },\r\n directory: {\r\n type: \"string\",\r\n description: \"搜索的目录路径,默认为当前工作目录\",\r\n },\r\n include: {\r\n type: \"string\",\r\n description: \"文件扩展名过滤(如 ts、json),不含点号\",\r\n },\r\n case_sensitive: {\r\n type: \"boolean\",\r\n description: \"是否大小写敏感,默认 false\",\r\n },\r\n max_files: {\r\n type: \"number\",\r\n description: \"最大搜索文件数,默认 200\",\r\n },\r\n },\r\n required: [\"pattern\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: GrepArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.pattern || typeof args.pattern !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 pattern\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const dir = args.directory ? stripMentionPrefix(args.directory) : undefined;\r\n const searchDir = dir\r\n ? (isAbsolute(dir) ? dir : join(ctx.cwd, dir))\r\n : ctx.cwd;\r\n const maxFiles = args.max_files ?? 200;\r\n\r\n try {\r\n const flags = args.case_sensitive ? \"g\" : \"gi\";\r\n const regex = new RegExp(args.pattern, flags);\r\n\r\n const dirStat = await stat(searchDir);\r\n if (!dirStat.isDirectory()) {\r\n return { success: false, data: `路径不是目录:${searchDir}`, error: \"NOT_DIRECTORY\" };\r\n }\r\n\r\n const files = await collectFiles(searchDir, args.include, maxFiles);\r\n const matches: GrepMatch[] = [];\r\n\r\n for (const filePath of files) {\r\n try {\r\n const content = await readFile(filePath, \"utf-8\");\r\n const lines = content.split(\"\\n\");\r\n const relPath = relative(searchDir, filePath);\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n if (regex.test(lines[i] ?? \"\")) {\r\n regex.lastIndex = 0;\r\n matches.push({\r\n file: relPath,\r\n line: i + 1,\r\n content: lines[i]!,\r\n });\r\n if (matches.length >= 500) break;\r\n }\r\n }\r\n\r\n if (matches.length >= 500) break;\r\n } catch {\r\n continue;\r\n }\r\n }\r\n\r\n if (matches.length === 0) {\r\n return {\r\n success: true,\r\n data: `未找到匹配 \"${args.pattern}\" 的内容`,\r\n summary: `🔍 \"${args.pattern}\" → 0 条命中`,\r\n };\r\n }\r\n\r\n const output = matches\r\n .map((m) => `${m.file}:${m.line}: ${m.content}`)\r\n .join(\"\\n\");\r\n\r\n const fileSet = new Set(matches.map((m) => m.file));\r\n const summary = `🔍 \"${args.pattern}\" → ${matches.length} 条命中 / ${fileSet.size} 个文件`;\r\n\r\n return {\r\n success: true,\r\n data: truncateOutput(output),\r\n summary,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `内容搜索失败:${message}`,\r\n error: \"GREP_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// ls 工具 — 列出目录内容\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readdir, stat } from \"node:fs/promises\";\r\nimport { join, relative } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { resolvePath, truncateOutput } from \"../sandbox.js\";\r\n\r\n/** ls 工具的参数格式 */\r\nexport interface LsArgs {\r\n /** 目录路径,默认为 cwd */\r\n path?: string;\r\n /** 是否显示隐藏文件,默认 false */\r\n all?: boolean;\r\n}\r\n\r\n/** 目录项类型标记 */\r\ntype EntryType = \"FILE\" | \"DIR\" | \"LINK\";\r\n\r\n/**\r\n * ls 工具 — 列出目录内容。\r\n *\r\n * 功能:\r\n * - 显示文件/目录类型标记\r\n * - 显示文件大小\r\n * - 可选显示隐藏文件\r\n * - 自动跳过无权访问的目录\r\n */\r\nexport const lsTool: AgentTool<LsArgs> = {\r\n name: \"ls\",\r\n kind: ToolKind.Read,\r\n description:\r\n \"列出目录内容。显示条目类型(文件/目录/链接)和大小。可选择是否显示隐藏文件。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n path: {\r\n type: \"string\",\r\n description: \"目录路径(相对于当前工作目录或绝对路径),默认为当前目录\",\r\n },\r\n all: {\r\n type: \"boolean\",\r\n description: \"是否显示隐藏文件(以 . 开头的文件),默认 false\",\r\n },\r\n },\r\n required: [],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: LsArgs, ctx: ToolContext): Promise<ToolResult> {\r\n const dirPath = args.path ? resolvePath(args.path, ctx.cwd) : ctx.cwd;\r\n const showAll = args.all ?? false;\r\n\r\n try {\r\n const entries = await readdir(dirPath, { withFileTypes: true });\r\n const lines: string[] = [];\r\n\r\n // 按名称排序:目录在前,文件在后\r\n const sorted = [...entries].toSorted((a, b) => {\r\n if (a.isDirectory() !== b.isDirectory()) {\r\n return a.isDirectory() ? -1 : 1;\r\n }\r\n return a.name.localeCompare(b.name);\r\n });\r\n\r\n for (const entry of sorted) {\r\n // 过滤隐藏文件\r\n if (!showAll && entry.name.startsWith(\".\")) continue;\r\n\r\n const typeMark: EntryType = entry.isDirectory()\r\n ? \"DIR\"\r\n : entry.isSymbolicLink()\r\n ? \"LINK\"\r\n : \"FILE\";\r\n\r\n let sizeStr = \"\";\r\n if (typeMark === \"FILE\") {\r\n try {\r\n const fileStat = await stat(join(dirPath, entry.name));\r\n if (fileStat.size < 1024) {\r\n sizeStr = `${fileStat.size}B`;\r\n } else if (fileStat.size < 1024 * 1024) {\r\n sizeStr = `${(fileStat.size / 1024).toFixed(1)}KB`;\r\n } else {\r\n sizeStr = `${(fileStat.size / 1024 / 1024).toFixed(1)}MB`;\r\n }\r\n } catch {\r\n sizeStr = \"?\";\r\n }\r\n }\r\n\r\n const typeLabel = typeMark === \"DIR\" ? \"📁\" : typeMark === \"LINK\" ? \"🔗\" : \"📄\";\r\n lines.push(`${typeLabel} ${entry.name}${sizeStr ? ` (${sizeStr})` : \"\"}`);\r\n }\r\n\r\n if (lines.length === 0) {\r\n return { success: true, data: \"目录为空\", summary: `📂 ${relative(ctx.cwd, dirPath).replace(/\\\\/g, \"/\")}(空)` };\r\n }\r\n\r\n const relPath = relative(ctx.cwd, dirPath).replace(/\\\\/g, \"/\");\r\n return {\r\n success: true,\r\n data: truncateOutput(`目录:${dirPath}\\n${lines.join(\"\\n\")}`),\r\n summary: `📂 ${relPath}(${lines.length} 项)`,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `列目录失败:${message}`,\r\n error: \"LS_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// fetch 工具 — HTTP 请求\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { truncateOutput } from \"../sandbox.js\";\r\n\r\n/** fetch 工具的参数格式 */\r\nexport interface FetchArgs {\r\n /** 请求 URL */\r\n url: string;\r\n /** 请求方法,默认 GET */\r\n method?: string;\r\n /** 请求头 */\r\n headers?: Record<string, string>;\r\n /** 请求体(POST/PUT 时使用) */\r\n body?: string;\r\n /** 响应最大长度(字符),默认 50000 */\r\n max_length?: number;\r\n}\r\n\r\n/**\r\n * fetch 工具 — 发起 HTTP 请求并返回响应内容。\r\n */\r\nexport const fetchTool: AgentTool<FetchArgs> = {\r\n name: \"fetch\",\r\n kind: ToolKind.Read,\r\n description:\r\n \"发起 HTTP 请求并返回响应内容。支持自定义方法和请求头。适用于获取网页内容、API 调用等场景。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n url: {\r\n type: \"string\",\r\n description: \"请求的 URL\",\r\n },\r\n method: {\r\n type: \"string\",\r\n description: \"HTTP 方法(GET、POST、PUT、DELETE 等),默认 GET\",\r\n },\r\n headers: {\r\n type: \"object\",\r\n description: \"请求头键值对\",\r\n additionalProperties: { type: \"string\" },\r\n },\r\n body: {\r\n type: \"string\",\r\n description: \"请求体内容(POST/PUT 时使用)\",\r\n },\r\n max_length: {\r\n type: \"number\",\r\n description: \"响应内容最大长度(字符),默认 50000\",\r\n },\r\n },\r\n required: [\"url\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: FetchArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.url || typeof args.url !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 url\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const method = (args.method ?? \"GET\").toUpperCase();\r\n const timeout = 30_000;\r\n const maxLength = args.max_length ?? 50_000;\r\n\r\n try {\r\n const fetchOptions: RequestInit = {\r\n method,\r\n headers: args.headers ?? {},\r\n signal: ctx.signal ?? AbortSignal.timeout(timeout),\r\n };\r\n\r\n if (args.body && (method === \"POST\" || method === \"PUT\" || method === \"PATCH\")) {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n (fetchOptions.headers as Record<string, string>)[\"Content-Type\"] ??= \"application/json\";\r\n fetchOptions.body = args.body;\r\n }\r\n\r\n const response = await fetch(args.url, fetchOptions);\r\n\r\n const contentType = response.headers.get(\"content-type\") ?? \"unknown\";\r\n const statusText = `${response.status} ${response.statusText}`;\r\n\r\n let body: string;\r\n try {\r\n body = await response.text();\r\n } catch {\r\n body = \"(无法读取响应体)\";\r\n }\r\n\r\n const truncatedBody = truncateOutput(body, maxLength);\r\n\r\n const header = `状态: ${statusText}\\n内容类型: ${contentType}`;\r\n const separator = body.length > 0 ? \"\\n---\\n\" : \"\";\r\n\r\n const urlPreview = args.url.length > 60\r\n ? args.url.slice(0, 57) + \"...\"\r\n : args.url;\r\n const summary = `🌐 ${method} ${urlPreview} → ${response.status}`;\r\n\r\n return {\r\n success: response.ok,\r\n data: `${header}${separator}${truncatedBody}`,\r\n summary,\r\n error: response.ok ? undefined : `HTTP_${response.status}`,\r\n };\r\n } catch (err: unknown) {\r\n if (err instanceof DOMException && err.name === \"AbortError\") {\r\n return { success: false, data: \"请求被中止\", error: \"ABORTED\" };\r\n }\r\n if (err instanceof TypeError && err.message.includes(\"fetch\")) {\r\n return { success: false, data: `网络错误:${err.message}`, error: \"NETWORK_ERROR\" };\r\n }\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `HTTP 请求失败:${message}`,\r\n error: \"FETCH_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// 内置工具 — 注册所有内置工具的工厂函数\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { type AnyAgentTool, eraseTool } from \"../types.js\";\r\nimport { readFileTool } from \"./read-file.js\";\r\nimport { writeFileTool } from \"./write-file.js\";\r\nimport { editFileTool } from \"./edit-file.js\";\r\nimport { multiEditTool } from \"./multi-edit.js\";\r\nimport { deleteRangeTool } from \"./delete-range.js\";\r\nimport { bashTool } from \"./bash.js\";\r\nimport { globTool } from \"./glob.js\";\r\nimport { grepTool } from \"./grep.js\";\r\nimport { lsTool } from \"./ls.js\";\r\nimport { fetchTool } from \"./fetch.js\";\r\n\r\n/** 所有内置工具列表(类型已擦除,可直接用于注册) */\r\nexport const builtinTools: AnyAgentTool[] = [\r\n eraseTool(readFileTool),\r\n eraseTool(writeFileTool),\r\n eraseTool(editFileTool),\r\n eraseTool(multiEditTool),\r\n eraseTool(deleteRangeTool),\r\n eraseTool(bashTool),\r\n eraseTool(globTool),\r\n eraseTool(grepTool),\r\n eraseTool(lsTool),\r\n eraseTool(fetchTool),\r\n];\r\n\r\n/**\r\n * 获取所有内置工具映射表(按名称索引)。\r\n */\r\nexport function getBuiltinToolMap(): Map<string, AnyAgentTool> {\r\n return new Map(builtinTools.map((t) => [t.name, t]));\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 渐变色工具 — 用于占位符从左到右的渐变动画\r\n// ---------------------------------------------------------------------------\r\n\r\n/** RGB 色标元组 [R, G, B],每个值 0~255 */\r\nexport type ColorStop = [number, number, number];\r\n\r\n// ---- 预设渐变方案 ----\r\n\r\n/** 空闲占位符渐变:赛博朋克绿 → 青 → 紫 */\r\nexport const IDLE_GRADIENT_STOPS: ColorStop[] = [\r\n [0, 255, 65], // #00ff41\r\n [0, 255, 255], // #00ffff\r\n [189, 147, 249], // #bd93f9\r\n];\r\n\r\n/** 命令提示条渐变:浅柠黄 → 暖黄 → 橙色(从左到右流动) */\r\nexport const CMD_TIP_GRADIENT_STOPS: ColorStop[] = [\r\n [255, 245, 180], // #fff5b4 浅柠黄\r\n [255, 210, 80], // #ffd250 暖黄\r\n [255, 150, 50], // #ff9632 橙色\r\n];\r\n\r\n/** 流式占位符渐变:琥珀 → 焦糖 → 棕红 */\r\nexport const STREAMING_GRADIENT_STOPS: ColorStop[] = [\r\n [255, 191, 0], // #ffbf00 琥珀金\r\n [210, 140, 60], // #d28c3c 焦糖\r\n [175, 70, 40], // #af4628 棕红\r\n];\r\n\r\n// ---- 渐变动画配置 ----\r\n\r\n/** 动画参数:每帧相位增量 & 帧间隔(ms) */\r\nexport const GRADIENT_ANIMATION = {\r\n /** 空闲占位符动画:每帧相位步进(值越大流速越快) */\r\n idlePhaseStep: 0.06,\r\n /** 空闲占位符动画:帧间隔(ms) */\r\n idleInterval: 40,\r\n /** 流式占位符动画:每帧相位步进 */\r\n streamingPhaseStep: 0.05,\r\n /** 流式占位符动画:帧间隔(ms) */\r\n streamingInterval: 40,\r\n /** 命令提示条动画:每帧相位步进 */\r\n cmdTipPhaseStep: 0.05,\r\n /** 命令提示条动画:帧间隔(ms) */\r\n cmdTipInterval: 40,\r\n} as const;\r\n\r\n// ---- 渲染时跳过着色的字符 ----\r\n\r\n/** 不参与渐变着色的字符(空格、标点、emoji 等) */\r\nexport const SKIP_CHARS = new Set([\" \", \"~\", \".\", \"。\", \"!\", \"👇\"]);\r\n\r\n// ---- 核心算法 ----\r\n\r\n/** 根据 t(0~1) 在色标间线性插值,返回 hex 色值 */\r\nexport function lerpStopsToHex(t: number, stops: readonly ColorStop[]): string {\r\n const len = stops.length;\r\n const segment = t * (len - 1);\r\n const idx = Math.min(Math.floor(segment), len - 2);\r\n const frac = segment - idx;\r\n const stop = stops[idx];\r\n const nextStop = stops[idx + 1];\r\n if (!stop || !nextStop) return \"#808080\";\r\n const r = Math.round(stop[0] + (nextStop[0] - stop[0]) * frac);\r\n const g = Math.round(stop[1] + (nextStop[1] - stop[1]) * frac);\r\n const b = Math.round(stop[2] + (nextStop[2] - stop[2]) * frac);\r\n return `#${r.toString(16).padStart(2, \"0\")}${g.toString(16).padStart(2, \"0\")}${b.toString(16).padStart(2, \"0\")}`;\r\n}\r\n\r\n/** 对文本计算逐字渐变色数组,phase 控制色彩偏移位置(0~1) */\r\nexport function getGradientColors(\r\n text: string,\r\n phase: number,\r\n stops: readonly ColorStop[],\r\n): string[] {\r\n const len = text.length;\r\n if (len === 0) return [];\r\n return text.split(\"\").map((ch, i) => {\r\n if (SKIP_CHARS.has(ch)) return \"\";\r\n const t = ((i / (len - 1)) + phase) % 1;\r\n return lerpStopsToHex(t, stops);\r\n });\r\n}","import { Box, Text, useInput } from \"ink\";\r\nimport { useState, useCallback } from \"react\";\r\nimport type { Game } from \"../game/index.js\";\r\nimport { useDoubleCtrlC } from \"../ui/useDoubleCtrlC.js\";\r\n\r\ninterface GamePickerProps {\r\n games: Game[];\r\n onSelect: (game: Game) => void;\r\n onExit?: () => void;\r\n onBackToChat?: () => void;\r\n}\r\n\r\nexport function GamePicker({ games, onSelect, onExit, onBackToChat }: GamePickerProps) {\r\n const [selectedIndex, setSelectedIndex] = useState(0);\r\n\r\n const exitAction = onBackToChat ?? onExit ?? (() => process.exit(0));\r\n const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(exitAction);\r\n\r\n useInput(\r\n useCallback(\r\n (input, key) => {\r\n // Ctrl+C:双击退出\r\n if (input === \"c\" && key.ctrl) {\r\n handleCtrlC();\r\n return;\r\n }\r\n\r\n if (games.length === 0) return;\r\n if (key.upArrow || input === \"k\") {\r\n setSelectedIndex((prev) => (prev > 0 ? prev - 1 : games.length - 1));\r\n } else if (key.downArrow || input === \"j\") {\r\n setSelectedIndex((prev) => (prev < games.length - 1 ? prev + 1 : 0));\r\n } else if (key.return) {\r\n const game = games[selectedIndex];\r\n if (game) onSelect(game);\r\n } else if (key.escape || input === \"q\") {\r\n if (onBackToChat) onBackToChat();\r\n else onExit?.();\r\n }\r\n },\r\n [games, selectedIndex, onSelect, onExit, onBackToChat, handleCtrlC],\r\n ),\r\n );\r\n\r\n return (\r\n <Box flexDirection=\"column\">\r\n <Box marginBottom={1}>\r\n <Text bold color=\"#00ffff\">\r\n 🎮 游戏列表\r\n </Text>\r\n </Box>\r\n\r\n <Box flexDirection=\"column\">\r\n {games.map((game, index) => {\r\n const isSelected = index === selectedIndex;\r\n return (\r\n <Box key={game.id} flexDirection=\"row\">\r\n <Box width={3} flexShrink={0}>\r\n {isSelected ? (\r\n <Text bold color=\"#00ff41\">\r\n {\"▸ \"}\r\n </Text>\r\n ) : (\r\n <Text>{\" \"}</Text>\r\n )}\r\n </Box>\r\n <Box width={20} flexShrink={0}>\r\n <Text bold color={isSelected ? \"#00ff41\" : \"#ffffff\"}>\r\n {game.name}\r\n </Text>\r\n </Box>\r\n <Box>\r\n <Text color=\"#888888\">{game.description}</Text>\r\n </Box>\r\n </Box>\r\n );\r\n })}\r\n </Box>\r\n\r\n <Box marginTop={1}>\r\n <Text dimColor>\r\n {\" ↑/↓ 选择 Enter 启动 q 返回\"}\r\n </Text>\r\n </Box>\r\n\r\n {/* 双击 Ctrl+C 退出提示 */}\r\n {doubleCtrlC && (\r\n <Box marginTop={1}>\r\n <Text color=\"#ff1493\" bold>\r\n {\" ⚠ 再按一次 Ctrl+C 退出 dskcode\"}\r\n </Text>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}\r\n","export interface Game {\r\n /** 游戏唯一标识 */\r\n id: string;\r\n /** 游戏名称 */\r\n name: string;\r\n /** 简短描述 */\r\n description: string;\r\n /** 启动游戏 */\r\n play: () => Promise<void>;\r\n}\r\n\r\nconst registry = new Map<string, Game>();\r\n\r\nexport function registerGame(game: Game): void {\r\n registry.set(game.id, game);\r\n}\r\n\r\nexport function getGame(id: string): Game | undefined {\r\n return registry.get(id);\r\n}\r\n\r\nexport function listGames(): Game[] {\r\n return Array.from(registry.values());\r\n}\r\n","import { Box, Text, useInput, render } from \"ink\";\r\nimport { useState, useEffect, useRef, useCallback } from \"react\";\r\n\r\nconst GAME_WIDTH = 40;\r\nconst GAME_HEIGHT = 18;\r\nconst PADDLE_WIDTH = 9;\r\nconst BRICK_COLORS = [166, 214, 76, 69];\r\n\r\ninterface Vec2 {\r\n x: number;\r\n y: number;\r\n}\r\n\r\ninterface Brick {\r\n x: number;\r\n y: number;\r\n w: number;\r\n alive: boolean;\r\n}\r\n\r\n// ─── 10 个关卡定义 ──────────────────────────────────────────────\r\n\r\ninterface LevelDef {\r\n rows: number;\r\n cols: number;\r\n bw: number; // 砖块宽度(格数)\r\n desc: string;\r\n // 返回 true 表示该位置有砖块\r\n pattern: (col: number, row: number, rows: number, cols: number) => boolean;\r\n}\r\n\r\nconst LEVELS: LevelDef[] = [\r\n { rows: 4, cols: 8, bw: 3, desc: \"经典 4×8\", pattern: () => true },\r\n { rows: 3, cols: 8, bw: 3, desc: \"轻松 3 层\", pattern: () => true },\r\n { rows: 6, cols: 8, bw: 3, desc: \"厚墙 6 层\", pattern: () => true },\r\n { rows: 4, cols: 8, bw: 3, desc: \"棋盘格\", pattern: (c, r) => (c + r) % 2 === 0 },\r\n { rows: 4, cols: 8, bw: 3, desc: \"金字塔\", pattern: (c, r, _, tc) => c >= r && c < tc - r },\r\n { rows: 4, cols: 8, bw: 3, desc: \"交错排列\", pattern: (c, r) => r % 2 === 0 || (c >= 1 && c <= 6) },\r\n { rows: 4, cols: 8, bw: 3, desc: \"中空边框\", pattern: (c, r, tr, tc) => r === 0 || r === tr - 1 || c === 0 || c === tc - 1 },\r\n { rows: 4, cols: 10, bw: 2, desc: \"密集 10 列\", pattern: () => true },\r\n { rows: 7, cols: 8, bw: 3, desc: \"高墙 7 层\", pattern: () => true },\r\n { rows: 8, cols: 8, bw: 3, desc: \"满屏 8 层\", pattern: () => true },\r\n];\r\n\r\nfunction getLevel(level: number): LevelDef {\r\n return LEVELS[(level - 1) % LEVELS.length]!;\r\n}\r\n\r\nfunction createBricks(level: number): Brick[] {\r\n const def = getLevel(level);\r\n const bricks: Brick[] = [];\r\n const gap = 2;\r\n const totalW = def.cols * def.bw + (def.cols - 1) * gap;\r\n const startX = Math.floor((GAME_WIDTH - totalW) / 2);\r\n const step = def.bw + gap;\r\n\r\n for (let row = 0; row < def.rows; row++) {\r\n for (let col = 0; col < def.cols; col++) {\r\n if (def.pattern(col, row, def.rows, def.cols)) {\r\n bricks.push({\r\n x: startX + col * step,\r\n y: 2 + row * 2,\r\n w: def.bw,\r\n alive: true,\r\n });\r\n }\r\n }\r\n }\r\n return bricks;\r\n}\r\n\r\n// ─── 游戏状态 ──────────────────────────────────────────────────\r\n\r\ninterface GameState {\r\n level: number;\r\n bricks: Brick[];\r\n paddleX: number;\r\n ball: Vec2;\r\n ballDir: Vec2;\r\n score: number;\r\n lives: number;\r\n gameOver: boolean;\r\n win: boolean;\r\n paused: boolean;\r\n}\r\n\r\nfunction createInitialState(level: number): GameState {\r\n const def = getLevel(level);\r\n const totalW = def.cols * def.bw + (def.cols - 1) * 2;\r\n const _startX = Math.floor((GAME_WIDTH - totalW) / 2);\r\n return {\r\n level,\r\n bricks: createBricks(level),\r\n paddleX: Math.floor(GAME_WIDTH / 2) - Math.floor(PADDLE_WIDTH / 2),\r\n ball: { x: GAME_WIDTH / 2, y: GAME_HEIGHT - 3 },\r\n ballDir: { x: 1, y: -1 },\r\n score: 0,\r\n lives: 3,\r\n gameOver: false,\r\n win: false,\r\n paused: false,\r\n };\r\n}\r\n\r\n// ─── 物理更新 ──────────────────────────────────────────────────\r\n\r\nfunction update(state: GameState): void {\r\n if (state.paused || state.gameOver || state.win) return;\r\n\r\n state.ball.x += state.ballDir.x;\r\n state.ball.y += state.ballDir.y;\r\n\r\n if (state.ball.x <= 0) { state.ball.x = 0; state.ballDir.x = 1; }\r\n if (state.ball.x >= GAME_WIDTH - 1) { state.ball.x = GAME_WIDTH - 1; state.ballDir.x = -1; }\r\n if (state.ball.y <= 0) { state.ball.y = 0; state.ballDir.y = 1; }\r\n\r\n // 挡板碰撞\r\n if (\r\n state.ball.y === GAME_HEIGHT - 1 &&\r\n state.ball.x >= state.paddleX &&\r\n state.ball.x <= state.paddleX + PADDLE_WIDTH\r\n ) {\r\n state.ballDir.y = -1;\r\n const hitPos = (state.ball.x - state.paddleX) / PADDLE_WIDTH;\r\n state.ballDir.x = hitPos < 0.5 ? -1 : 1;\r\n }\r\n\r\n // 出界\r\n if (state.ball.y > GAME_HEIGHT) {\r\n state.lives--;\r\n if (state.lives <= 0) {\r\n state.gameOver = true;\r\n } else {\r\n state.ball = { x: GAME_WIDTH / 2, y: GAME_HEIGHT - 3 };\r\n state.ballDir = { x: 1, y: -1 };\r\n state.paddleX = Math.floor(GAME_WIDTH / 2) - Math.floor(PADDLE_WIDTH / 2);\r\n }\r\n }\r\n\r\n // 砖块碰撞\r\n const hitBrick = state.bricks.find((b) => {\r\n if (!b.alive) return false;\r\n return state.ball.x >= b.x && state.ball.x < b.x + b.w && state.ball.y >= b.y && state.ball.y < b.y + 1;\r\n });\r\n\r\n if (hitBrick) {\r\n hitBrick.alive = false;\r\n state.score += 10;\r\n state.ballDir.y = -state.ballDir.y;\r\n }\r\n\r\n if (state.bricks.every((b) => !b.alive)) state.win = true;\r\n}\r\n\r\n// ─── 画面渲染(含 ANSI 颜色) ─────────────────────────────────\r\n\r\nfunction buildBoard(state: GameState): string {\r\n const lines: string[] = [];\r\n\r\n function brickColorIndex(x: number, y: number): number | undefined {\r\n const row = Math.floor((y - 2) / 2);\r\n if (row >= 0) {\r\n const b = state.bricks.find((br) => br.y === y && br.alive && x >= br.x && x < br.x + br.w);\r\n if (b) return row % BRICK_COLORS.length;\r\n }\r\n return undefined;\r\n }\r\n\r\n for (let y = 0; y < GAME_HEIGHT; y++) {\r\n let line = \"\";\r\n for (let x = 0; x < GAME_WIDTH; x++) {\r\n const isBall = state.ball.x === x && state.ball.y === y;\r\n const isPaddle = y === GAME_HEIGHT - 1 && x >= state.paddleX && x < state.paddleX + PADDLE_WIDTH;\r\n const brickRow = brickColorIndex(x, y);\r\n\r\n if (isBall) {\r\n line += \"\\x1b[97m●\\x1b[0m\";\r\n } else if (isPaddle) {\r\n line += \"\\x1b[94m▄\\x1b[0m\";\r\n } else if (brickRow !== undefined) {\r\n line += `\\x1b[38;5;${BRICK_COLORS[brickRow]}m▀\\x1b[0m`;\r\n } else {\r\n line += \" \";\r\n }\r\n }\r\n lines.push(line);\r\n }\r\n\r\n return lines.map((l) => `│${l}│`).join(\"\\n\");\r\n}\r\n\r\n// ─── Ink 组件 ──────────────────────────────────────────────────\r\n\r\ninterface BrickBreakerGameProps {\r\n onExit: () => void;\r\n}\r\n\r\nfunction BrickBreakerGame({ onExit: _onExit }: BrickBreakerGameProps) {\r\n const [initialLevel, setInitialLevel] = useState(1);\r\n const [selectingLevel, setSelectingLevel] = useState(true);\r\n const stateRef = useRef<GameState>(createInitialState(initialLevel));\r\n const [tick, setTick] = useState(0);\r\n const onExitRef = useRef(_onExit);\r\n onExitRef.current = _onExit;\r\n\r\n useEffect(() => {\r\n if (selectingLevel) return;\r\n const interval = setInterval(() => {\r\n update(stateRef.current);\r\n setTick((t) => t + 1);\r\n }, 80);\r\n return () => clearInterval(interval);\r\n }, [selectingLevel]);\r\n\r\n // 重新开始(保留当前关卡)\r\n const restart = useCallback((level?: number) => {\r\n const lv = level ?? stateRef.current.level;\r\n stateRef.current = createInitialState(lv);\r\n setInitialLevel(lv);\r\n setSelectingLevel(false);\r\n setTick(0);\r\n }, []);\r\n\r\n // 选关\r\n const startLevelSelect = useCallback(() => {\r\n setSelectingLevel(true);\r\n }, []);\r\n\r\n useInput(\r\n useCallback((input, key) => {\r\n const s = stateRef.current;\r\n\r\n // 选关模式\r\n if (selectingLevel) {\r\n if (input >= \"1\" && input <= \"9\") {\r\n restart(Number(input));\r\n } else if (input === \"0\") {\r\n restart(10);\r\n } else if (key.escape || input === \"q\") {\r\n onExitRef.current();\r\n }\r\n return;\r\n }\r\n\r\n if (key.leftArrow) {\r\n s.paddleX = Math.max(0, s.paddleX - 1);\r\n setTick((t) => t + 1);\r\n } else if (key.rightArrow) {\r\n s.paddleX = Math.min(GAME_WIDTH - PADDLE_WIDTH, s.paddleX + 1);\r\n setTick((t) => t + 1);\r\n } else if (input === \"p\" || input === \" \") {\r\n s.paused = !s.paused;\r\n } else if (input === \"r\") {\r\n if (s.gameOver || s.win) restart();\r\n } else if (input === \"l\") {\r\n if (s.gameOver || s.win) startLevelSelect();\r\n } else if (input === \"q\" || key.escape) {\r\n onExitRef.current();\r\n }\r\n }, [selectingLevel, restart, startLevelSelect]),\r\n );\r\n\r\n const s = stateRef.current;\r\n const aliveCount = s.bricks.filter((b) => b.alive).length;\r\n const board = buildBoard(s);\r\n const def = getLevel(s.level);\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n void tick;\r\n\r\n return (\r\n <Box flexDirection=\"column\">\r\n {/* 状态栏 */}\r\n <Box flexDirection=\"row\">\r\n <Box width={20}>\r\n <Text>\r\n 关卡 {s.level}: <Text color=\"cyan\">{def.desc}</Text>\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text>\r\n 分数: <Text color=\"yellow\">{String(s.score).padStart(3, \"0\")}</Text>\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text>\r\n 生命: <Text color=\"red\">{\"♥\".repeat(Math.max(0, s.lives))}</Text>\r\n </Text>\r\n </Box>\r\n <Box width={10}>\r\n <Text>\r\n 砖块: <Text color=\"cyan\">{aliveCount}</Text>\r\n </Text>\r\n </Box>\r\n <Box>\r\n <Text color={s.paused ? \"gray\" : \"green\"}>\r\n [{s.paused ? \"暂停\" : \"运行中\"}]\r\n </Text>\r\n </Box>\r\n </Box>\r\n\r\n {/* 游戏画面 */}\r\n <Box flexDirection=\"column\">\r\n <Text>┌{\"─\".repeat(GAME_WIDTH)}┐</Text>\r\n <Text>{selectingLevel ? \" \\x1b[90m选择关卡后开始...\\x1b[0m\" : board}</Text>\r\n <Text>└{\"─\".repeat(GAME_WIDTH)}┘</Text>\r\n </Box>\r\n\r\n {/* 选关界面 */}\r\n {selectingLevel && (\r\n <Box marginTop={1} flexDirection=\"column\">\r\n <Text bold color=\"yellow\">选择关卡</Text>\r\n <Box flexDirection=\"row\" flexWrap=\"wrap\">\r\n {LEVELS.map((lv, i) => (\r\n <Box key={i} width={22}>\r\n <Text>\r\n <Text color={initialLevel === i + 1 ? \"green\" : \"white\"}>\r\n {i + 1 === 10 ? \"0\" : String(i + 1)}\r\n </Text>\r\n . {lv.desc} ({lv.rows}×{lv.cols})\r\n </Text>\r\n </Box>\r\n ))}\r\n </Box>\r\n <Box marginTop={1}>\r\n <Text dimColor>按数字选关 q 退出</Text>\r\n </Box>\r\n </Box>\r\n )}\r\n\r\n {/* 结束/通关信息 */}\r\n {!selectingLevel && (s.gameOver || s.win) && (\r\n <Box marginTop={1}>\r\n <Text bold color={s.gameOver ? \"red\" : \"green\"}>\r\n {s.gameOver ? \"游戏结束!\" : \"恭喜通关!\"}\r\n </Text>\r\n <Text>\r\n {\" 分数: \"}<Text color=\"yellow\">{s.score}</Text>\r\n </Text>\r\n </Box>\r\n )}\r\n\r\n {/* 操作提示 */}\r\n {!selectingLevel && (\r\n <Box marginTop={1}>\r\n {s.gameOver || s.win ? (\r\n <Text dimColor>\r\n {\"← → 移动 r 重开 l 选关 q 退出\"}\r\n </Text>\r\n ) : (\r\n <Text dimColor>\r\n {\"← → 移动 p 暂停 q 退出\"}\r\n </Text>\r\n )}\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}\r\n\r\n// ─── 导出 ──────────────────────────────────────────────────────\r\n\r\nexport default {\r\n id: \"brick-breaker\",\r\n name: \"Brick Breaker\",\r\n description: \"经典打砖块游戏,10 个关卡可选!\",\r\n play: async () => {\r\n await new Promise<void>((resolve) => {\r\n const { unmount } = render(\r\n <BrickBreakerGame onExit={() => { unmount(); resolve(); }} />,\r\n );\r\n });\r\n },\r\n};\r\n","import { Box, Text, useInput, render } from \"ink\";\r\nimport { useState, useEffect, useRef, useCallback } from \"react\";\r\n\r\nconst GAME_W = 66;\r\nconst GAME_H = 20; // 6 行分数 + 14 行掉落单词\r\nconst SCORE_H = 6;\r\n// const WORD_H = 14;\r\nconst MAX_WORDS = 10;\r\nconst CYBER_PALETTE = [\"#00ffff\", \"#ff00ff\", \"#00ff41\", \"#ff1493\", \"#8b00ff\"];\r\n\r\n// 3×5 像素大号数字(每个像素 = ██,每数字 6 列)\r\nconst DIGIT_ART: Record<string, string[]> = {\r\n \"0\": [\" ██████ \", \"██ ██\", \"██ ██\", \"██ ██\", \" ██████ \"],\r\n \"1\": [\" ██ \", \" ████ \", \" ██ \", \" ██ \", \" ██████ \"],\r\n \"2\": [\" ██████ \", \" ██ \", \" ██████ \", \" ██ \", \" ██████ \"],\r\n \"3\": [\" ██████ \", \" ██ \", \" ██████ \", \" ██ \", \" ██████ \"],\r\n \"4\": [\"██ ██\", \"██ ██\", \" ██████ \", \" ██ \", \" ██ \"],\r\n \"5\": [\" ██████ \", \" ██ \", \" ██████ \", \" ██ \", \" ██████ \"],\r\n \"6\": [\" ██████ \", \" ██ \", \" ██████ \", \" ██ ██\", \" ██████ \"],\r\n \"7\": [\" ██████ \", \" ██ \", \" ██ \", \" ██ \", \" ██ \"],\r\n \"8\": [\" ██████ \", \"██ ██\", \" ██████ \", \"██ ██\", \" ██████ \"],\r\n \"9\": [\" ██████ \", \"██ ██\", \" ██████ \", \" ██ \", \" ██████ \"],\r\n};\r\n\r\nfunction buildScoreLines(scoreStr: string): string[] {\r\n const lines = [\"\", \"\", \"\", \"\", \"\"];\r\n for (const ch of scoreStr) {\r\n const art = DIGIT_ART[ch] ?? DIGIT_ART[\"0\"]!;\r\n for (let r = 0; r < 5; r++) {\r\n lines[r] += \" \" + (art[r] ?? \"\");\r\n }\r\n }\r\n // 左右各留 6 格间距\r\n const pad = 6;\r\n return lines.map((l) => \" \".repeat(pad) + l);\r\n}\r\n\r\nfunction hexToRgb(hex: string): string {\r\n const r = parseInt(hex.slice(1, 3), 16);\r\n const g = parseInt(hex.slice(3, 5), 16);\r\n const b = parseInt(hex.slice(5, 7), 16);\r\n return `${r};${g};${b}`;\r\n}\r\n\r\n// ─── 200 个常见单词 ────────────────────────────────────────────\r\n\r\nconst WORDS_BANK = [\r\n // ── 框架 & 库 ──\r\n \"react\", \"vue\", \"next\", \"node\", \"axios\", \"express\", \"lodash\", \"jquery\",\r\n \"webpack\", \"vite\", \"babel\", \"eslint\", \"prettier\", \"tailwind\", \"bootstrap\",\r\n \"sass\", \"less\", \"postcss\", \"redux\", \"pinia\", \"vuex\", \"router\",\r\n \"nestjs\", \"socket\", \"graphql\", \"rest\", \"grpc\", \"prisma\", \"typeorm\",\r\n\r\n // ── 前端 ──\r\n \"html\", \"css\", \"jsx\", \"tsx\", \"dom\", \"spa\", \"ssr\", \"csr\", \"pwa\",\r\n \"component\", \"prop\", \"hook\", \"composable\", \"directive\", \"filter\", \"mixin\",\r\n \"template\", \"render\", \"virtual\", \"diff\", \"patch\", \"hydration\",\r\n \"responsive\", \"flexbox\", \"grid\", \"animation\", \"transition\",\r\n\r\n // ── 后端 ──\r\n \"api\", \"route\", \"middleware\", \"controller\", \"service\", \"module\",\r\n \"dto\", \"entity\", \"schema\", \"migration\", \"seeder\", \"factory\",\r\n \"auth\", \"jwt\", \"oauth\", \"session\", \"cookie\", \"token\", \"cors\",\r\n \"cache\", \"redis\", \"mq\", \"rabbit\", \"kafka\", \"nats\",\r\n\r\n // ── 数据库 ──\r\n \"sql\", \"mysql\", \"pg\", \"sqlite\", \"mongo\", \"redis\", \"orm\",\r\n \"table\", \"index\", \"query\", \"join\", \"union\", \"group\", \"order\",\r\n \"where\", \"having\", \"limit\", \"offset\", \"insert\", \"update\", \"delete\",\r\n\r\n // ── DevOps ──\r\n \"docker\", \"nginx\", \"linux\", \"bash\", \"shell\", \"yaml\", \"toml\",\r\n \"ci\", \"cd\", \"deploy\", \"rollback\", \"release\", \"build\", \"test\",\r\n \"lint\", \"format\", \"stage\", \"commit\", \"branch\", \"merge\", \"rebase\",\r\n\r\n // ── 数据结构 & 算法 ──\r\n \"array\", \"stack\", \"queue\", \"tree\", \"graph\", \"list\", \"map\", \"set\",\r\n \"sort\", \"search\", \"filter\", \"reduce\", \"map\", \"async\", \"await\",\r\n \"promise\", \"callback\", \"closure\", \"proxy\", \"reflect\", \"decorator\",\r\n\r\n // ── 常用操作 ──\r\n \"create\", \"read\", \"update\", \"delete\", \"crud\", \"parse\", \"stringify\",\r\n \"encode\", \"decode\", \"transform\", \"validate\", \"format\", \"parse\",\r\n \"upload\", \"download\", \"export\", \"import\", \"backup\", \"restore\",\r\n\r\n // ── 类型 & 变量 ──\r\n \"string\", \"number\", \"boolean\", \"object\", \"array\", \"tuple\", \"enum\",\r\n \"interface\", \"type\", \"class\", \"function\", \"method\", \"property\",\r\n \"public\", \"private\", \"static\", \"readonly\", \"optional\", \"abstract\",\r\n \"const\", \"let\", \"var\", \"void\", \"null\", \"undef\", \"never\", \"any\",\r\n\r\n // ── 补充 ──\r\n \"config\", \"logger\", \"monitor\", \"metric\", \"alert\", \"webhook\",\r\n \"endpoint\", \"payload\", \"header\", \"status\", \"timeout\", \"retry\",\r\n \"fallback\", \"circuit\", \"breaker\", \"throttle\", \"debounce\",\r\n \"scroll\", \"resize\", \"click\", \"hover\", \"focus\", \"blur\",\r\n];\r\n\r\nfunction randomWord(used: Set<string>): string {\r\n let w: string;\r\n do { w = WORDS_BANK[Math.floor(Math.random() * WORDS_BANK.length)]!; }\r\n while (used.has(w));\r\n return w;\r\n}\r\n\r\n// ─── 游戏状态 ──────────────────────────────────────────────────\r\n\r\ninterface DropWord {\r\n text: string;\r\n row: number;\r\n col: number;\r\n}\r\n\r\ninterface GameState {\r\n words: DropWord[];\r\n score: number;\r\n lives: number;\r\n speed: number;\r\n spawnTimer: number;\r\n gameOver: boolean;\r\n paused: boolean;\r\n typed: string;\r\n target: string | null;\r\n combo: number;\r\n message: string;\r\n messageTimer: number;\r\n usedWords: Set<string>;\r\n}\r\n\r\nfunction createInitialState(): GameState {\r\n return {\r\n words: [],\r\n score: 0,\r\n lives: 3,\r\n speed: 0.3,\r\n spawnTimer: 0,\r\n gameOver: false,\r\n paused: false,\r\n typed: \"\",\r\n target: null,\r\n combo: 0,\r\n message: \"\",\r\n messageTimer: 0,\r\n usedWords: new Set(),\r\n };\r\n}\r\n\r\nfunction pickTarget(s: GameState): string | null {\r\n // 选取最靠左的存活单词作为目标\r\n let best: DropWord | null = null;\r\n for (const w of s.words) {\r\n if (!best || w.col < best.col) best = w;\r\n }\r\n return best?.text ?? null;\r\n}\r\n\r\n// ─── 物理更新 ──────────────────────────────────────────────────\r\n\r\nfunction update(s: GameState): void {\r\n if (s.paused || s.gameOver) return;\r\n\r\n // 生成新单词\r\n s.spawnTimer++;\r\n if (s.spawnTimer >= Math.max(20, 50 - Math.floor(s.speed * 15))) {\r\n s.spawnTimer = 0;\r\n if (s.words.length < MAX_WORDS) {\r\n const used = new Set(s.usedWords);\r\n for (const w of s.words) used.add(w.text);\r\n const text = randomWord(used);\r\n // 找空白行\r\n const usedRows = new Set(s.words.map((w) => w.row));\r\n let row = -1;\r\n for (let r = SCORE_H; r < GAME_H; r++) {\r\n if (!usedRows.has(r)) { row = r; break; }\r\n }\r\n if (row >= 0) {\r\n s.words.push({ text, row, col: GAME_W - 1 });\r\n s.usedWords.add(text);\r\n }\r\n }\r\n }\r\n\r\n // 移动单词\r\n for (const w of s.words) {\r\n w.col -= s.speed;\r\n }\r\n\r\n // 移除超出左边的 + 更新目标\r\n const before = s.words.length;\r\n s.words = s.words.filter((w) => w.col > -w.text.length);\r\n const removed = before - s.words.length;\r\n\r\n // 单词漏掉了 → 扣命\r\n if (removed > 0) {\r\n s.lives -= removed;\r\n s.typed = \"\";\r\n s.target = pickTarget(s);\r\n if (s.lives <= 0) {\r\n s.gameOver = true;\r\n s.words = [];\r\n }\r\n }\r\n\r\n // 更新目标\r\n s.target = pickTarget(s);\r\n\r\n // 消息计时\r\n if (s.messageTimer > 0) {\r\n s.messageTimer--;\r\n if (s.messageTimer <= 0) s.message = \"\";\r\n }\r\n}\r\n\r\n// ─── 画面渲染 ──────────────────────────────────────────────────\r\n\r\nfunction buildGameView(s: GameState, scoreLines: string[], scoreColor: string, message: string): string[] {\r\n const rows: string[] = [];\r\n\r\n for (let y = 0; y < GAME_H; y++) {\r\n let line = \"\";\r\n\r\n // 分数区域(前 6 行)\r\n if (y < SCORE_H) {\r\n if (y < 5) {\r\n // 数字行 — 先补足宽度再套赛博朋克色\r\n const raw = (scoreLines[y] ?? \"\").padEnd(GAME_W);\r\n line = `\\x1b[38;2;${hexToRgb(scoreColor)}m${raw}\\x1b[0m`;\r\n } else if (y === 5) {\r\n // 第 6 行:连击 / 暂停\r\n if (s.combo >= 3) {\r\n const comboText = `${s.combo}连击!`;\r\n const pad = Math.floor((GAME_W - comboText.length) / 2);\r\n const comboColor = CYBER_PALETTE[s.combo % CYBER_PALETTE.length]!;\r\n const raw = \" \".repeat(pad) + comboText + \" \".repeat(GAME_W - pad - comboText.length);\r\n line = `\\x1b[38;2;${hexToRgb(comboColor)}m${raw}\\x1b[0m`;\r\n } else if (s.paused) {\r\n const pauseText = \"暂停\";\r\n const pad = Math.floor((GAME_W - pauseText.length) / 2);\r\n line = \" \".repeat(pad) + pauseText;\r\n line = line.padEnd(GAME_W);\r\n }\r\n }\r\n // 补全到 GAME_W 宽度\r\n line = line.padEnd(GAME_W);\r\n } else {\r\n // 单词区域\r\n // 第 1 行显示消息(如果有)\r\n if (y === SCORE_H && message) {\r\n const pad = Math.floor((GAME_W - message.length) / 2);\r\n const raw = \" \".repeat(pad) + message + \" \".repeat(GAME_W - pad - message.length);\r\n const msgColor = CYBER_PALETTE[Math.floor(Math.random() * CYBER_PALETTE.length)]!;\r\n line = `\\x1b[38;2;${hexToRgb(msgColor)}m${raw}\\x1b[0m`;\r\n } else {\r\n for (let x = 0; x < GAME_W; x++) {\r\n const word = s.words.find((w) => {\r\n const charIdx = x - Math.floor(w.col);\r\n return charIdx >= 0 && charIdx < w.text.length && w.row === y;\r\n });\r\n if (word) {\r\n const charIdx = x - Math.floor(word.col);\r\n const ch = word.text[charIdx]!;\r\n const isTarget = word.text === s.target;\r\n const typedIdx = s.target === word.text ? s.typed.length : 0;\r\n const isTyped = isTarget && charIdx < typedIdx;\r\n if (isTarget) {\r\n line += isTyped ? `\\x1b[92m${ch}\\x1b[0m` : `\\x1b[97m${ch}\\x1b[0m`;\r\n } else {\r\n line += `\\x1b[90m${ch}\\x1b[0m`;\r\n }\r\n } else {\r\n line += \" \";\r\n }\r\n }\r\n }\r\n }\r\n\r\n rows.push(line);\r\n }\r\n return rows;\r\n}\r\n\r\n// ─── Ink 组件 ──────────────────────────────────────────────────\r\n\r\ninterface CoderCheckProps {\r\n onExit: () => void;\r\n}\r\n\r\nfunction CoderCheck({ onExit: _onExit }: CoderCheckProps) {\r\n const stateRef = useRef<GameState>(createInitialState());\r\n const [tick, setTick] = useState(0);\r\n const [colorOffset, setColorOffset] = useState(0);\r\n const onExitRef = useRef(_onExit);\r\n onExitRef.current = _onExit;\r\n\r\n // 赛博朋克颜色动画\r\n useEffect(() => {\r\n const timer = setInterval(() => {\r\n setColorOffset((prev) => (prev + 1) % CYBER_PALETTE.length);\r\n }, 400);\r\n return () => clearInterval(timer);\r\n }, []);\r\n\r\n // 游戏循环\r\n useEffect(() => {\r\n const interval = setInterval(() => {\r\n update(stateRef.current);\r\n setTick((t) => t + 1);\r\n }, 60);\r\n return () => clearInterval(interval);\r\n }, []);\r\n\r\n useInput(\r\n useCallback((input, key) => {\r\n const s = stateRef.current;\r\n if (s.gameOver) {\r\n if (input === \"r\") {\r\n stateRef.current = createInitialState();\r\n setTick(0);\r\n } else if (input === \"q\" || key.escape) {\r\n onExitRef.current();\r\n }\r\n return;\r\n }\r\n\r\n if ((input === \"p\" && key.ctrl) || input === \" \") {\r\n s.paused = !s.paused;\r\n return;\r\n }\r\n if ((input === \"q\" && key.ctrl) || key.escape) {\r\n onExitRef.current();\r\n return;\r\n }\r\n\r\n if (s.paused) return;\r\n\r\n // 打字输入\r\n if (input.length === 1 && input >= \"a\" && input <= \"z\") {\r\n // 选取目标\r\n if (!s.target) {\r\n s.target = pickTarget(s);\r\n }\r\n if (s.target) {\r\n const nextChar = s.target[s.typed.length];\r\n if (nextChar === input) {\r\n s.typed += input;\r\n // 检查是否完成\r\n if (s.typed === s.target) {\r\n // 移除该单词\r\n s.words = s.words.filter((w) => w.text !== s.target);\r\n s.combo++;\r\n // 连击 >= 3 时以当前分数翻倍奖励\r\n const prevScore = s.score;\r\n const basePts = s.target.length;\r\n const comboBonus = s.combo >= 3 ? s.score : 0;\r\n s.score += basePts + comboBonus;\r\n s.speed = s.speed + 0.02;\r\n\r\n // 每 500 分提速\r\n const prevMilestone = Math.floor(prevScore / 500);\r\n const newMilestone = Math.floor(s.score / 500);\r\n if (newMilestone > prevMilestone) {\r\n s.speed += 0.15;\r\n }\r\n\r\n // 超过 99999 分提示通关\r\n if (s.score >= 100000 && prevScore < 100000) {\r\n s.message = \"恭喜通关! 难度最大化!\";\r\n s.messageTimer = 100;\r\n }\r\n s.typed = \"\";\r\n s.target = pickTarget(s);\r\n }\r\n } else {\r\n // 打错字符,连击中断\r\n s.combo = 0;\r\n }\r\n }\r\n }\r\n }, []),\r\n );\r\n\r\n const s = stateRef.current;\r\n const scoreColor = CYBER_PALETTE[colorOffset]!;\r\n const scoreStr = String(s.score).padStart(5, \"0\");\r\n const scoreLines = buildScoreLines(scoreStr);\r\n const view = buildGameView(s, scoreLines, scoreColor, s.message);\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n void tick;\r\n\r\n return (\r\n <Box flexDirection=\"column\" paddingX={1}>\r\n {/* 顶栏:生命 + 速度 */}\r\n <Box flexDirection=\"row\">\r\n <Text>\r\n 生命 <Text color=\"red\">{\"♥\".repeat(Math.max(0, s.lives))}</Text>\r\n {\" \"}速度 <Text color=\"cyan\">Lv.{Math.floor(s.speed * 10)}</Text>\r\n </Text>\r\n </Box>\r\n\r\n {/* 目标单词提示 */}\r\n {!s.gameOver && s.target && (\r\n <Box marginTop={1}>\r\n <Text>\r\n 打字: <Text color=\"green\">{s.typed}</Text>\r\n <Text color=\"white\">{s.target.slice(s.typed.length)}</Text>\r\n </Text>\r\n </Box>\r\n )}\r\n\r\n {/* 游戏画面(含分数) */}\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n <Text>┌{\"─\".repeat(GAME_W)}┐</Text>\r\n {view.map((row, i) => (\r\n <Text key={i}>{`│${row}│`}</Text>\r\n ))}\r\n <Text>└{\"─\".repeat(GAME_W)}┘</Text>\r\n </Box>\r\n\r\n {/* 游戏结束 */}\r\n {s.gameOver && (\r\n <Box marginTop={1}>\r\n <Text bold color=\"red\">\r\n 游戏结束!\r\n </Text>\r\n <Text>\r\n {\" 得分: \"}<Text color=\"yellow\">{s.score}</Text>\r\n {\" r 重开 q 退出\"}\r\n </Text>\r\n </Box>\r\n )}\r\n\r\n {/* 操作提示 */}\r\n <Box marginTop={1}>\r\n <Text dimColor>\r\n {\"打字消除单词 空格/Ctrl+P暂停 Ctrl+Q退出\"}\r\n </Text>\r\n </Box>\r\n </Box>\r\n );\r\n}\r\n\r\n// ─── 导出 ──────────────────────────────────────────────────────\r\n\r\nexport default {\r\n id: \"coder-check\",\r\n name: \"Coder Check\",\r\n description: \"极速打字游戏,输入单词消除它们!\",\r\n play: async () => {\r\n await new Promise<void>((resolve) => {\r\n const { unmount } = render(\r\n <CoderCheck onExit={() => { unmount(); resolve(); }} />,\r\n );\r\n });\r\n },\r\n};\r\n","import type { Direction, GameState, GameConfig, Food, Point } from './types.js';\r\n\r\n// ─── 辅助函数 ───────────────────────────────────────────\r\n\r\nfunction isOpposite(a: Direction, b: Direction): boolean {\r\n return (\r\n (a === 'UP' && b === 'DOWN') ||\r\n (a === 'DOWN' && b === 'UP') ||\r\n (a === 'LEFT' && b === 'RIGHT') ||\r\n (a === 'RIGHT' && b === 'LEFT')\r\n );\r\n}\r\n\r\nfunction samePoint(a: Point, b: Point): boolean {\r\n return a.x === b.x && a.y === b.y;\r\n}\r\n\r\nfunction movePoint(p: Point, dir: Direction): Point {\r\n switch (dir) {\r\n case 'UP': return { x: p.x, y: p.y - 1 };\r\n case 'DOWN': return { x: p.x, y: p.y + 1 };\r\n case 'LEFT': return { x: p.x - 1, y: p.y };\r\n case 'RIGHT': return { x: p.x + 1, y: p.y };\r\n }\r\n}\r\n\r\nfunction wrapPoint(p: Point, config: GameConfig): Point {\r\n return {\r\n x: ((p.x % config.width) + config.width) % config.width,\r\n y: ((p.y % config.height) + config.height) % config.height,\r\n };\r\n}\r\n\r\n// ─── 食物生成 ───────────────────────────────────────────\r\n\r\n/**\r\n * 在蛇身 + 指定排除位置之外的随机空位生成食物。\r\n * 排除位置用于防止普通食物和特殊食物重叠。\r\n */\r\nfunction generateFood(\r\n snake: Point[],\r\n config: GameConfig,\r\n type: 'normal' | 'special',\r\n excludePositions: Point[] = [],\r\n): Food | null {\r\n const occupied = new Set(snake.map(p => `${p.x},${p.y}`));\r\n // 排除位置也加入已占用集合(防止与另一种食物重叠)\r\n for (const p of excludePositions) {\r\n occupied.add(`${p.x},${p.y}`);\r\n }\r\n\r\n const totalCells = config.width * config.height;\r\n if (occupied.size >= totalCells) return null;\r\n\r\n let pos: Point;\r\n do {\r\n pos = {\r\n x: Math.floor(Math.random() * config.width),\r\n y: Math.floor(Math.random() * config.height),\r\n };\r\n } while (occupied.has(`${pos.x},${pos.y}`));\r\n\r\n return {\r\n position: pos,\r\n type,\r\n ...(type === 'special' ? { spawnedAt: Date.now() } : {}),\r\n };\r\n}\r\n\r\n// ─── 状态工厂 ───────────────────────────────────────────\r\n\r\n/** 创建初始游戏状态 */\r\nexport function createInitialState(config: GameConfig): GameState {\r\n const startX = Math.floor(config.width / 2);\r\n const startY = Math.floor(config.height / 2);\r\n\r\n const snake: Point[] = [\r\n { x: startX, y: startY },\r\n { x: startX - 1, y: startY },\r\n { x: startX - 2, y: startY },\r\n ];\r\n\r\n const food = generateFood(snake, config, 'normal')!;\r\n\r\n return {\r\n snake,\r\n direction: 'RIGHT',\r\n nextDirection: 'RIGHT',\r\n food,\r\n specialFood: null,\r\n score: 0,\r\n speedLevel: 1,\r\n isGameOver: false,\r\n isPaused: false,\r\n wallWrap: Math.random() < 0.5,\r\n tickInterval: config.initialSpeed,\r\n };\r\n}\r\n\r\n// ─── 核心逻辑:每帧推进 ─────────────────────────────────\r\n\r\nexport function tick(state: GameState, config: GameConfig): void {\r\n // 1. 应用缓冲方向(防止同帧内 180° 掉头)\r\n state.direction = state.nextDirection;\r\n\r\n // 2. 计算新蛇头位置\r\n const head = state.snake[0]!;\r\n let newHead = movePoint(head, state.direction);\r\n\r\n // 3. 边界处理\r\n const outOfBounds =\r\n newHead.x < 0 ||\r\n newHead.x >= config.width ||\r\n newHead.y < 0 ||\r\n newHead.y >= config.height;\r\n\r\n if (outOfBounds) {\r\n if (state.wallWrap) {\r\n newHead = wrapPoint(newHead, config);\r\n } else {\r\n state.isGameOver = true;\r\n return;\r\n }\r\n }\r\n\r\n // 4. 判断本帧是否会吃到食物(影响自碰撞检测的判定范围)\r\n const ateNormal = samePoint(newHead, state.food.position);\r\n const ateSpecial = state.specialFood !== null && samePoint(newHead, state.specialFood.position);\r\n const willEat = ateNormal || ateSpecial;\r\n\r\n // 自碰撞检测:不吃食物时排除蛇尾(因为蛇尾会移除)\r\n const bodyToCheck = willEat ? state.snake : state.snake.slice(0, -1);\r\n for (const segment of bodyToCheck) {\r\n if (samePoint(newHead, segment)) {\r\n state.isGameOver = true;\r\n return;\r\n }\r\n }\r\n\r\n // 5. 移动蛇\r\n state.snake.unshift(newHead);\r\n\r\n // 6. 处理进食\r\n if (ateNormal) {\r\n state.score += 1;\r\n updateSpeed(state, config);\r\n\r\n // 生成新普通食物时排除特殊食物的位置\r\n const exclude: Point[] = state.specialFood ? [state.specialFood.position] : [];\r\n const newFood = generateFood(state.snake, config, 'normal', exclude);\r\n if (newFood) {\r\n state.food = newFood;\r\n } else {\r\n // 没有空位了(蛇填满了棋盘),宣告胜利\r\n state.isGameOver = true;\r\n return;\r\n }\r\n\r\n // 25% 概率生成特殊食物,排除普通食物的位置\r\n if (state.specialFood === null && Math.random() < 0.25) {\r\n state.specialFood = generateFood(state.snake, config, 'special', [state.food.position]);\r\n }\r\n } else if (ateSpecial) {\r\n state.score += 3;\r\n updateSpeed(state, config);\r\n state.specialFood = null;\r\n } else {\r\n // 没吃到食物,移除蛇尾\r\n state.snake.pop();\r\n }\r\n\r\n // 7. 特殊食物倒计时(5 秒后消失)\r\n if (state.specialFood?.spawnedAt !== undefined) {\r\n if (Date.now() - state.specialFood.spawnedAt > 5000) {\r\n state.specialFood = null;\r\n }\r\n }\r\n}\r\n\r\n// ─── 速度更新 ───────────────────────────────────────────\r\n\r\nfunction updateSpeed(state: GameState, config: GameConfig): void {\r\n const newLevel = Math.floor(state.score / config.scorePerLevel) + 1;\r\n state.speedLevel = newLevel;\r\n state.tickInterval = Math.max(\r\n config.minSpeed,\r\n config.initialSpeed - (newLevel - 1) * config.speedDecrement,\r\n );\r\n}\r\n\r\n// ─── 方向变更(外部调用) ──────────────────────────────\r\n\r\n/**\r\n * 尝试改变方向。\r\n * 检查新方向是否与当前移动方向相反,防止 180° 掉头。\r\n */\r\nexport function changeDirection(state: GameState, newDir: Direction): void {\r\n if (!isOpposite(newDir, state.direction)) {\r\n state.nextDirection = newDir;\r\n }\r\n}\r\n","import chalk from 'chalk';\r\nimport type { GameState, GameConfig } from './types.js';\r\n\r\n// 每个格子在终端中占 2 个字符宽度,让网格看起来更接近正方形\r\nconst CELL_W = 2;\r\nconst EMPTY = ' ';\r\nconst BODY_CHAR = '▓';\r\nconst HEAD_CHAR = '▓';\r\nconst NORMAL_FOOD_CHAR = '◆';\r\nconst SPECIAL_FOOD_CHAR = '★';\r\n\r\n// ─── ANSI 控制序列 ──────────────────────────────────────\r\n\r\nconst CSI = '\\x1b[';\r\nconst CURSOR_HIDE = `${CSI}?25l`;\r\nconst CURSOR_SHOW = `${CSI}?25h`;\r\nconst CURSOR_HOME = `${CSI}H`;\r\n\r\n/** 隐藏终端光标 */\r\nexport function hideCursor(): void {\r\n process.stdout.write(CURSOR_HIDE);\r\n}\r\n\r\n/** 显示终端光标 */\r\nexport function showCursor(): void {\r\n process.stdout.write(CURSOR_SHOW);\r\n}\r\n\r\n/** 清屏并复位光标 */\r\nexport function clearScreen(): void {\r\n process.stdout.write(`${CSI}2J${CURSOR_HOME}`);\r\n}\r\n\r\n// ─── 着色辅助 ───────────────────────────────────────────\r\n\r\nfunction cellColor(char: string, type: 'snake-head' | 'snake-body' | 'normal-food' | 'special-food' | 'empty'): string {\r\n switch (type) {\r\n case 'snake-head': return chalk.greenBright(char);\r\n case 'snake-body': return chalk.green(char);\r\n case 'normal-food': return chalk.red(char);\r\n case 'special-food': return chalk.yellow(char);\r\n case 'empty': return char;\r\n }\r\n}\r\n\r\n// ─── 主渲染函数 ─────────────────────────────────────────\r\n\r\n/**\r\n * 将当前游戏状态渲染到终端。\r\n * 先移动光标到左上角再逐行覆盖,避免闪烁。\r\n */\r\nexport function renderToTerminal(state: GameState, config: GameConfig): void {\r\n const lines = buildRenderLines(state, config);\r\n // 先清屏再回左上角绘制,避免状态切换/终端滚动时旧行残留导致重叠\r\n process.stdout.write(`${CSI}2J${CURSOR_HOME}` + lines.join('\\n'));\r\n}\r\n\r\nfunction buildRenderLines(state: GameState, config: GameConfig): string[] {\r\n const { width: W, height: H } = config;\r\n const lines: string[] = [];\r\n\r\n // ── 构建网格数据 ──\r\n // grid[y][x] 存储该位置要显示的内容类型\r\n const grid: string[][] = Array.from({ length: H }, () => Array(W).fill('empty'));\r\n\r\n // 蛇身(除蛇头外)\r\n for (let i = 1; i < state.snake.length; i++) {\r\n const seg = state.snake[i]!;\r\n if (seg.y >= 0 && seg.y < H && seg.x >= 0 && seg.x < W) {\r\n grid[seg.y]![seg.x] = 'snake-body';\r\n }\r\n }\r\n\r\n // 蛇头(覆盖蛇身标记,确保蛇头颜色优先)\r\n const head = state.snake[0]!;\r\n if (head.y >= 0 && head.y < H && head.x >= 0 && head.x < W) {\r\n grid[head.y]![head.x] = 'snake-head';\r\n }\r\n\r\n // 普通食物\r\n const fp = state.food.position;\r\n if (grid[fp.y]![fp.x] === 'empty') {\r\n grid[fp.y]![fp.x] = 'normal-food';\r\n }\r\n\r\n // 特殊食物\r\n if (state.specialFood) {\r\n const sp = state.specialFood.position;\r\n if (grid[sp.y]![sp.x] === 'empty') {\r\n grid[sp.y]![sp.x] = 'special-food';\r\n }\r\n }\r\n\r\n // ── 顶部边框 ──\r\n lines.push(' ' + chalk.gray('╔' + '══'.repeat(W) + '╗'));\r\n\r\n // ── 每行渲染 ──\r\n for (let y = 0; y < H; y++) {\r\n let row = ' ' + chalk.gray('║');\r\n for (let x = 0; x < W; x++) {\r\n const cellType = grid[y]![x]!;\r\n let char: string;\r\n switch (cellType) {\r\n case 'snake-head':\r\n char = HEAD_CHAR.repeat(CELL_W);\r\n break;\r\n case 'snake-body':\r\n char = BODY_CHAR.repeat(CELL_W);\r\n break;\r\n case 'normal-food':\r\n char = NORMAL_FOOD_CHAR + ' ';\r\n break;\r\n case 'special-food':\r\n char = SPECIAL_FOOD_CHAR + ' ';\r\n break;\r\n default:\r\n char = EMPTY;\r\n }\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n row += cellColor(char, cellType as unknown as 'snake-head' | 'snake-body' | 'normal-food' | 'special-food' | 'empty');\r\n }\r\n row += chalk.gray('║');\r\n lines.push(row);\r\n }\r\n\r\n // ── 底部边框 ──\r\n lines.push(' ' + chalk.gray('╚' + '══'.repeat(W) + '╝'));\r\n\r\n // ── HUD 信息 ──\r\n lines.push('');\r\n lines.push(\r\n ` ${chalk.cyan('分数')}: ${state.score} ` +\r\n `${chalk.cyan('长度')}: ${state.snake.length} ` +\r\n `${chalk.cyan('速度')}: Lv.${state.speedLevel} ` +\r\n `${chalk.cyan('边界')}: ${state.wallWrap ? '穿墙' : '撞墙'}`,\r\n );\r\n\r\n // 特殊食物倒计时提示\r\n if (state.specialFood?.spawnedAt !== undefined) {\r\n const remaining = Math.ceil((5000 - (Date.now() - state.specialFood.spawnedAt)) / 1000);\r\n if (remaining > 0) {\r\n lines.push(` ${chalk.yellow(`⭐ 特殊食物 ${remaining} 秒后消失`)}`);\r\n }\r\n }\r\n\r\n // 操作提示\r\n lines.push(` ${chalk.gray('W/A/S/D 或 方向键移动 | P 暂停 | M 切换模式 | Q 退出')}`);\r\n\r\n // 暂停 / 游戏结束提示\r\n if (state.isPaused) {\r\n lines.push('');\r\n lines.push(` ${chalk.yellow('⏸ 已暂停 — 按 P 继续')}`);\r\n }\r\n\r\n if (state.isGameOver) {\r\n lines.push('');\r\n lines.push(` ${chalk.red.bold('💀 游戏结束!')} ${chalk.cyan('最终分数: ' + state.score)}`);\r\n lines.push(` ${chalk.gray('按 R 重新开始 | 按 Q 退出')}`);\r\n }\r\n\r\n return lines;\r\n}\r\n","import * as readline from 'node:readline';\r\nimport type { Direction, GameState } from './types.js';\r\nimport { changeDirection } from './engine.js';\r\n\r\n// ─── 按键 → 方向映射 ───────────────────────────────────\r\n\r\nconst KEY_TO_DIR: Record<string, Direction> = {\r\n up: 'UP',\r\n w: 'UP',\r\n W: 'UP',\r\n down: 'DOWN',\r\n s: 'DOWN',\r\n S: 'DOWN',\r\n left: 'LEFT',\r\n a: 'LEFT',\r\n A: 'LEFT',\r\n right: 'RIGHT',\r\n d: 'RIGHT',\r\n D: 'RIGHT',\r\n};\r\n\r\n// ─── 输入设置 ───────────────────────────────────────────\r\n\r\nexport interface InputContext {\r\n state: GameState;\r\n}\r\n\r\nexport type InputCleanup = () => void;\r\n\r\n/**\r\n * 设置终端键盘输入监听。\r\n * - WASD / 方向键 → 控制方向\r\n * - P → 暂停/继续\r\n * - R → 游戏结束后重新开始\r\n * - Q / Ctrl+C → 退出游戏\r\n *\r\n * @param ctx 可变状态上下文(重启时替换 state 引用)\r\n * @param onQuit 退出回调\r\n * @param onRestart 重新开始回调(应返回新的 GameState)\r\n */\r\nexport function setupInput(\r\n ctx: InputContext,\r\n onQuit: () => void,\r\n onRestart: () => GameState,\r\n): InputCleanup {\r\n readline.emitKeypressEvents(process.stdin);\r\n\r\n if (process.stdin.isTTY) {\r\n process.stdin.setRawMode(true);\r\n }\r\n\r\n const handler = (_: unknown, key: readline.Key) => {\r\n if (!key || !key.name) return;\r\n\r\n const { name, ctrl } = key;\r\n\r\n // Ctrl+C / Ctrl+D → 退出\r\n if (ctrl && (name === 'c' || name === 'd')) {\r\n onQuit();\r\n return;\r\n }\r\n\r\n // Q → 退出\r\n if (name === 'q') {\r\n onQuit();\r\n return;\r\n }\r\n\r\n const state = ctx.state;\r\n\r\n // 游戏结束后 R → 重新开始\r\n if (state.isGameOver) {\r\n if (name === 'r') {\r\n ctx.state = onRestart();\r\n }\r\n return;\r\n }\r\n\r\n // P → 暂停/继续\r\n if (name === 'p') {\r\n state.isPaused = !state.isPaused;\r\n return;\r\n }\r\n\r\n // M → 切换边界模式(穿墙 / 撞墙)\r\n if (name === 'm') {\r\n state.wallWrap = !state.wallWrap;\r\n return;\r\n }\r\n\r\n // 方向控制\r\n const dir = KEY_TO_DIR[name];\r\n if (dir) {\r\n changeDirection(state, dir);\r\n }\r\n };\r\n\r\n process.stdin.on('keypress', handler);\r\n\r\n return () => {\r\n process.stdin.removeListener('keypress', handler);\r\n if (process.stdin.isTTY) {\r\n process.stdin.setRawMode(false);\r\n }\r\n };\r\n}\r\n","import type { Game } from '../index.js';\r\nimport type { GameConfig } from './types.js';\r\nimport { createInitialState, tick } from './engine.js';\r\nimport { renderToTerminal, clearScreen, hideCursor, showCursor } from './renderer.js';\r\nimport { setupInput } from './input.js';\r\nimport type { InputContext } from './input.js';\r\n\r\nconst CONFIG: GameConfig = {\r\n width: 20,\r\n height: 16,\r\n initialSpeed: 180,\r\n minSpeed: 60,\r\n speedDecrement: 10,\r\n scorePerLevel: 5,\r\n};\r\n\r\n/** 游戏结束后或暂停时的循环间隔(不需要高帧率) */\r\nconst IDLE_INTERVAL = 200;\r\n\r\nconst snakeGame: Game = {\r\n id: 'snake',\r\n name: 'Big Snake',\r\n description: '经典贪吃蛇,双食物系统、难度递增、穿墙/撞墙模式',\r\n play: async () => {\r\n const ctx: InputContext = { state: createInitialState(CONFIG) };\r\n\r\n return new Promise<void>((resolve) => {\r\n const cleanupInput = setupInput(\r\n ctx,\r\n () => {\r\n // ── 退出 ──\r\n stop = true;\r\n clearTimeout(timerId);\r\n showCursor();\r\n cleanupInput();\r\n resolve();\r\n },\r\n () => {\r\n // ── 重新开始 ──\r\n return createInitialState(CONFIG);\r\n },\r\n );\r\n\r\n // 初始化终端\r\n clearScreen();\r\n hideCursor();\r\n\r\n let stop = false;\r\n let timerId: ReturnType<typeof setTimeout>;\r\n\r\n function loop(): void {\r\n if (stop) return;\r\n\r\n const s = ctx.state;\r\n\r\n if (!s.isGameOver && !s.isPaused) {\r\n tick(s, CONFIG);\r\n }\r\n\r\n renderToTerminal(s, CONFIG);\r\n\r\n // 游戏进行中 → 用 tick 速度;暂停/结束时用恒定低速,节省 CPU\r\n const interval = s.isGameOver || s.isPaused ? IDLE_INTERVAL : s.tickInterval;\r\n timerId = setTimeout(loop, interval);\r\n }\r\n\r\n timerId = setTimeout(loop, 0);\r\n });\r\n },\r\n};\r\n\r\nexport default snakeGame;\r\n","import { registerGame, listGames } from \"./index.js\";\r\nimport type { Game } from \"./index.js\";\r\nimport brickBreaker from \"./brick-breaker/index.js\";\r\nimport coderCheck from \"./coder-check/index.js\";\r\nimport snake from \"./snake/index.js\";\r\n\r\n/** 在此注册所有游戏 */\r\nexport function initGames(): Game[] {\r\n registerGame(brickBreaker);\r\n registerGame(coderCheck);\r\n registerGame(snake);\r\n return listGames();\r\n}\r\n","import { Box, Text, useInput } from \"ink\";\r\nimport { useState, useCallback, useEffect, useMemo } from \"react\";\r\nimport asciichart from \"asciichart\";\r\nimport os from \"node:os\";\r\nimport { join } from \"node:path\";\r\nimport type { StockRow } from \"./types.js\";\r\nimport { useDoubleCtrlC } from \"../ui/useDoubleCtrlC.js\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 自选股配置文件路径(取用户全局 ~/.dskcode/settings.json 的真实绝对路径)\r\n// ---------------------------------------------------------------------------\r\n\r\nconst SETTINGS_PATH = join(os.homedir(), \".dskcode\", \"settings.json\");\r\n\r\n/**\r\n * 将本地绝对路径转换为 file:// URL,兼容 Windows 与 POSIX。\r\n * Windows: C:\\Users\\me\\.dskcode\\settings.json → file:///C:/Users/me/.dskcode/settings.json\r\n */\r\nfunction toFileUrl(p: string): string {\r\n const norm = p.replace(/\\\\/g, \"/\");\r\n if (process.platform === \"win32\") {\r\n return \"file://\" + norm.replace(/^([a-zA-Z]:)/, \"/$1\");\r\n }\r\n return \"file://\" + norm;\r\n}\r\n\r\n/**\r\n * 用 OSC 8 转义序列把 text 包装成可点击的终端超链接。\r\n * 现代终端(Windows Terminal / iTerm2 / kitty / WezTerm 等)点击后会以系统默认程序打开该 URI。\r\n * 不可见宽度的转义对 string-width 透明(strip-ansi 会剥除),不影响布局。\r\n */\r\nfunction osc8Link(uri: string, text: string): string {\r\n return `\\x1b]8;;${uri}\\x1b\\\\${text}\\x1b]8;;\\x1b\\\\`;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 分时数据接口\r\n// ---------------------------------------------------------------------------\r\n\r\n/** API 接口地址模板 */\r\nconst MINUTE_API = \"https://web.ifzq.gtimg.cn/appstock/app/minute/query?code={code}&r=0.1\";\r\n\r\ninterface MinuteResponse {\r\n code: number;\r\n data?: Record<string, {\r\n data?: { data?: string[]; date?: string };\r\n qt?: Record<string, (string | string[])[]>;\r\n }>;\r\n}\r\n\r\n/** 确保股票代码带市场前缀(如 513090 → sh513090) */\r\nfunction normalizeApiCode(code: string): string {\r\n if (code.startsWith(\"sh\") || code.startsWith(\"sz\")) return code;\r\n if (/^60|^68|^51/.test(code)) return \"sh\" + code;\r\n if (/^00|^30|^39/.test(code)) return \"sz\" + code;\r\n return \"sh\" + code;\r\n}\r\n\r\n/**\r\n * 调用分时接口获取某只股票的全天分钟数据。\r\n * 返回 { prices: 每分钟价格数组, quote: 实时行情快照, date: 日期 }\r\n */\r\nasync function fetchStockMinute(code: string): Promise<{\r\n prices: number[];\r\n quote: StockRow | null;\r\n date: string;\r\n} | null> {\r\n const url = MINUTE_API.replace(\"{code}\", normalizeApiCode(code));\r\n try {\r\n const resp = await fetch(url);\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const json = (await resp.json()) as unknown as MinuteResponse;\r\n if (json.code !== 0) return null;\r\n\r\n const stockKey = normalizeApiCode(code);\r\n const stockData = json.data?.[stockKey];\r\n if (!stockData) return null;\r\n\r\n // 1. 分钟行情 — 用于折线图\r\n const rawMinutes = stockData.data?.data ?? [];\r\n const prices: number[] = [];\r\n for (const line of rawMinutes) {\r\n // 格式: \"HHMM price volume amount\"\r\n const parts = (line).split(\" \");\r\n if (parts.length >= 2) {\r\n const p = parseFloat(parts[1]!);\r\n if (!isNaN(p)) prices.push(p);\r\n }\r\n }\r\n\r\n // 2. 实时行情快照 — 用于列表\r\n const qtKey = stockKey;\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const qt = stockData.qt?.[qtKey] as unknown as string[] | undefined;\r\n let quote: StockRow | null = null;\r\n if (qt && qt.length >= 35) {\r\n quote = {\r\n code,\r\n name: qt[1] ?? \"\",\r\n price: parseFloat(qt[3] ?? \"0\"),\r\n changePercent: parseFloat(qt[32] ?? \"0\"),\r\n changeAmount: parseFloat(qt[31] ?? \"0\"),\r\n high: parseFloat(qt[33] ?? \"0\"),\r\n low: parseFloat(qt[34] ?? \"0\"),\r\n volume: parseInt(qt[6] ?? \"0\", 10),\r\n // 成交额(单位:万元)→ 转为元\r\n amount: parseFloat(qt[37] ?? \"0\") * 10_000,\r\n };\r\n }\r\n\r\n return {\r\n prices,\r\n quote,\r\n date: stockData.data?.date ?? \"\",\r\n };\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 缓存\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 缓存各股票的分钟价格数据,key = 股票代码 */\r\nconst minuteCache = new Map<string, number[]>();\r\n\r\nfunction cacheMinute(code: string, prices: number[]): void {\r\n minuteCache.set(code, prices);\r\n}\r\n\r\n/** 仅用于测试环境重置缓存 */\r\nexport function _clearMinuteCache(): void {\r\n minuteCache.clear();\r\n}\r\n\r\nfunction _getCachedMinutes(_code: string): number[] | undefined {\r\n return minuteCache.get(_code);\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 演示兜底数据\r\n// ---------------------------------------------------------------------------\r\n\r\nconst FALLBACK_STOCKS: StockRow[] = [\r\n // 上证指数成交额约 1.56 万亿\r\n { code: \"000001\", name: \"上证指数\", price: 3150.00, changePercent: 0.35, changeAmount: 11.02, high: 3160.00, low: 3140.00, volume: 285430000, amount: 1_560_000_000_000 },\r\n // 紫金矿业成交额约 108.5 亿\r\n { code: \"601899\", name: \"紫金矿业\", price: 18.20, changePercent: 1.85, changeAmount: 0.33, high: 18.45, low: 17.90, volume: 59615384, amount: 10_850_000_000 },\r\n { code: \"399006\", name: \"创业板指\", price: 1820.00, changePercent: -0.52, changeAmount: -9.50, high: 1835.00, low: 1815.00, volume: 98650000, amount: 18_240_000_000 },\r\n];\r\n\r\n// ---------------------------------------------------------------------------\r\n// fetchStocks — 加载列表数据\r\n// ---------------------------------------------------------------------------\r\n\r\nasync function fetchStocks(codes: string[]): Promise<StockRow[]> {\r\n const results = await Promise.all(\r\n codes.map(async (code) => {\r\n const data = await fetchStockMinute(code);\r\n if (data?.quote) {\r\n // 同时缓存分钟数据,进详情就不用再请求了\r\n if (data.prices.length > 0) cacheMinute(code, data.prices);\r\n return data.quote;\r\n }\r\n return null;\r\n }),\r\n );\r\n\r\n // 用真实数据替换,缺失的用兜底\r\n const real: StockRow[] = results.filter((r): r is StockRow => r !== null);\r\n if (real.length > 0) return real;\r\n\r\n // 全都没取到?用兜底数据\r\n return FALLBACK_STOCKS;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 格式化工具\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction formatPrice(p: number): string {\r\n return p >= 100 ? p.toFixed(2) : p.toFixed(3);\r\n}\r\n\r\nfunction _formatVolume(_v: number): string {\r\n if (_v >= 10000) return (_v / 10000).toFixed(1) + \"万\";\r\n return _v.toLocaleString();\r\n}\r\n\r\n/**\r\n * 成交额格式化(元 → 万/亿/万亿)\r\n * 1 万亿 = 1_0000_0000_0000,1 亿 = 1_0000_0000,1 万 = 1_0000\r\n */\r\nfunction formatAmount(yuan: number): string {\r\n if (yuan >= 1_000_000_000_000) return (yuan / 1_000_000_000_000).toFixed(2) + \"万亿\";\r\n if (yuan >= 100_000_000) return (yuan / 100_000_000).toFixed(2) + \"亿\";\r\n if (yuan >= 10_000) return (yuan / 10_000).toFixed(1) + \"万\";\r\n return yuan.toLocaleString();\r\n}\r\n\r\n/**\r\n * 取最新 maxPoints 个点,用于绘制折线图。\r\n * 分时数据从 09:30 累积到当前时间,取尾部最新的 60 点\r\n * 能展示最近的行情走势,同时适配终端宽度。\r\n */\r\nfunction latestPoints(data: number[], maxPoints = 60): number[] {\r\n if (data.length <= maxPoints) return data;\r\n return data.slice(data.length - maxPoints);\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// StockList 组件\r\n// ---------------------------------------------------------------------------\r\n\r\ninterface StockListProps {\r\n /** 自选股代码列表 */\r\n codes?: string[];\r\n /** 按 q 时退出 */\r\n onExit: () => void;\r\n /** 按 q 时返回上级(从 chat 内跳转时使用) */\r\n onBackToChat?: () => void;\r\n}\r\n\r\nexport function StockList({ codes, onExit, onBackToChat }: StockListProps) {\r\n const [stocks, setStocks] = useState<StockRow[]>([]);\r\n const [selectedIndex, setSelectedIndex] = useState(0);\r\n const [loading, setLoading] = useState(true);\r\n const [lastUpdate, setLastUpdate] = useState<string>(\"\");\r\n const [detailView, setDetailView] = useState<StockRow | null>(null);\r\n const [detailPrices, setDetailPrices] = useState<number[] | null>(null);\r\n const [detailLoading, setDetailLoading] = useState(false);\r\n const [detailCountdown, setDetailCountdown] = useState(10);\r\n const [countdown, setCountdown] = useState(5);\r\n const [currentTime, setCurrentTime] = useState<string>(() =>\r\n new Date().toLocaleTimeString(\"zh-CN\", { hour12: false }),\r\n );\r\n // h 键一键置灰:所有高亮色替换为 dimColor\r\n const [dimMode, setDimMode] = useState(false);\r\n\r\n // o 键排序:默认 → 降序 → 升序 → 默认\r\n type SortOrder = \"default\" | \"asc\" | \"desc\";\r\n const [sortOrder, setSortOrder] = useState<SortOrder>(\"default\");\r\n\r\n const sortedStocks = useMemo(() => {\r\n if (sortOrder === \"default\") return stocks;\r\n return [...stocks].toSorted((a, b) =>\r\n sortOrder === \"desc\"\r\n ? b.changePercent - a.changePercent\r\n : a.changePercent - b.changePercent,\r\n );\r\n }, [stocks, sortOrder]);\r\n\r\n const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(onExit);\r\n\r\n // 实时时钟(每秒更新)\r\n useEffect(() => {\r\n const timer = setInterval(() => {\r\n setCurrentTime(new Date().toLocaleTimeString(\"zh-CN\", { hour12: false }));\r\n }, 1000);\r\n return () => clearInterval(timer);\r\n }, []);\r\n\r\n // ---------- 数据加载 ----------\r\n const loadData = useCallback(async () => {\r\n setLoading(true);\r\n try {\r\n const data = await fetchStocks(codes ?? []);\r\n setStocks(data);\r\n setLastUpdate(new Date().toLocaleTimeString());\r\n } catch {\r\n // 保持旧数据\r\n }\r\n setLoading(false);\r\n }, [codes]);\r\n\r\n // 首次加载\r\n useEffect(() => {\r\n void loadData();\r\n }, [loadData]);\r\n\r\n // 5 秒自动刷新 + 倒计时\r\n useEffect(() => {\r\n const interval = setInterval(() => {\r\n setCountdown((prev) => {\r\n if (prev <= 1) {\r\n void loadData();\r\n return 5;\r\n }\r\n return prev - 1;\r\n });\r\n }, 1000);\r\n return () => clearInterval(interval);\r\n }, [loadData]);\r\n\r\n // ---------- 进入详情时加载分钟数据,每 30s 自动刷新 ----------\r\n useEffect(() => {\r\n if (detailView) {\r\n const loadDetail = () => {\r\n // 清除缓存,强制走网络\r\n minuteCache.delete(detailView.code);\r\n void fetchStockMinute(detailView.code).then((data) => {\r\n if (data && data.prices.length > 0) {\r\n cacheMinute(detailView.code, data.prices);\r\n setDetailPrices(data.prices);\r\n }\r\n });\r\n };\r\n\r\n // 立即加载\r\n loadDetail();\r\n\r\n // 重置倒计时\r\n setDetailCountdown(10);\r\n\r\n // 每 10 秒刷新\r\n const timer = setInterval(loadDetail, 10000);\r\n return () => clearInterval(timer);\r\n }\r\n\r\n setDetailPrices(null);\r\n setDetailLoading(false);\r\n }, [detailView]);\r\n\r\n // 详情页倒计时\r\n useEffect(() => {\r\n if (!detailView) return;\r\n const timer = setInterval(() => {\r\n setDetailCountdown((prev) => (prev > 0 ? prev - 1 : 10));\r\n }, 1000);\r\n return () => clearInterval(timer);\r\n }, [detailView]);\r\n\r\n // ---------- 键盘控制 ----------\r\n useInput(\r\n useCallback(\r\n (input, key) => {\r\n // Ctrl+C:双击退出\r\n if (input === \"c\" && key.ctrl) {\r\n handleCtrlC();\r\n return;\r\n }\r\n\r\n if (detailView) {\r\n // 详情模式下按 Esc/q 返回列表\r\n if (key.escape || input === \"q\" || input === \" \") {\r\n setDetailView(null);\r\n }\r\n return;\r\n }\r\n\r\n if (stocks.length === 0) return;\r\n\r\n if (key.upArrow || input === \"k\") {\r\n setSelectedIndex((prev) => (prev > 0 ? prev - 1 : stocks.length - 1));\r\n } else if (key.downArrow || input === \"j\") {\r\n setSelectedIndex((prev) => (prev < stocks.length - 1 ? prev + 1 : 0));\r\n } else if (key.return) {\r\n const stock = stocks[selectedIndex];\r\n if (stock) setDetailView(stock);\r\n } else if (key.escape || input === \"q\") {\r\n if (onBackToChat) onBackToChat();\r\n else onExit();\r\n } else if (input === \"r\") {\r\n setCountdown(5);\r\n void loadData();\r\n } else if (input === \"o\") {\r\n setSortOrder((prev) => (prev === \"default\" ? \"desc\" : prev === \"desc\" ? \"asc\" : \"default\"));\r\n } else if (input === \"h\") {\r\n setDimMode((v) => !v);\r\n }\r\n },\r\n [stocks, selectedIndex, detailView, onExit, onBackToChat, loadData, handleCtrlC, setDimMode],\r\n ),\r\n );\r\n\r\n // ---------- 详情视图 ----------\r\n if (detailView) {\r\n if (detailLoading) {\r\n return (\r\n <Box paddingLeft={1}>\r\n <Text dimColor>{\" ⟳ 加载分时数据...\"}</Text>\r\n </Box>\r\n );\r\n }\r\n return renderDetail(detailView, () => setDetailView(null), detailPrices ?? undefined, detailCountdown, currentTime);\r\n }\r\n\r\n // ---------- 列表视图 ----------\r\n // 置灰模式下,所有高亮色统一替换为 dimColor\r\n const cp = (c: string) => dimMode ? { dimColor: true } : { color: c };\r\n return (\r\n <Box flexDirection=\"column\">\r\n {/* 顶部状态栏 */}\r\n <Box marginBottom={1} justifyContent=\"space-between\">\r\n <Text bold {...cp(\"#00ffff\")}>\r\n {\" 📈 自选股监控\"}\r\n </Text>\r\n <Text dimColor>\r\n {\" 🕐 \"}{currentTime}\r\n </Text>\r\n <Text dimColor>\r\n {loading ? \" ⟳ 刷新中...\" : ` ${countdown}s 后自动刷新`}\r\n </Text>\r\n </Box>\r\n\r\n {/* 表头 */}\r\n <Box>\r\n <Box width={3} />\r\n <Box width={9}>\r\n <Text dimColor>代码</Text>\r\n </Box>\r\n <Box width={16}>\r\n <Text dimColor>名称</Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text dimColor>最新价</Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text dimColor>\r\n 涨跌幅{sortOrder === \"desc\" ? \" ▼\" : sortOrder === \"asc\" ? \" ▲\" : \"\"}\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text dimColor>涨跌额</Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text dimColor>最高</Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text dimColor>最低</Text>\r\n </Box>\r\n <Box>\r\n <Text dimColor>成交额</Text>\r\n </Box>\r\n </Box>\r\n\r\n {/* 分隔线 */}\r\n <Box>\r\n <Text dimColor>{\" \" + \"─\".repeat(100)}</Text>\r\n </Box>\r\n\r\n {/* 股票列表 */}\r\n <Box flexDirection=\"column\">\r\n {sortedStocks.map((stock, index) => {\r\n const isSelected = index === selectedIndex;\r\n const isUp = stock.changePercent >= 0;\r\n const color = isUp ? \"#ff1493\" : \"#00ff41\";\r\n\r\n return (\r\n <Box key={stock.code}>\r\n <Box width={3} flexShrink={0}>\r\n {isSelected ? (\r\n <Text bold {...cp(\"#00ffff\")}>\r\n {\"▸ \"}\r\n </Text>\r\n ) : (\r\n <Text>{\" \"}</Text>\r\n )}\r\n </Box>\r\n <Box width={9}>\r\n <Text bold {...cp(isSelected ? \"#00ffff\" : \"#ffffff\")}>\r\n {stock.code}\r\n </Text>\r\n </Box>\r\n <Box width={16}>\r\n <Text {...cp(isSelected ? \"#ffffff\" : \"#cccccc\")}>\r\n {stock.name}\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text bold {...cp(color)}>\r\n {formatPrice(stock.price)}\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text {...cp(color)}>\r\n {isUp ? \"+\" : \"\"}{stock.changePercent.toFixed(2)}%\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text {...cp(color)}>\r\n {isUp ? \"+\" : \"\"}{stock.changeAmount.toFixed(3)}\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text {...cp(\"#cccccc\")}>\r\n {formatPrice(stock.high)}\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text {...cp(\"#888888\")}>\r\n {formatPrice(stock.low)}\r\n </Text>\r\n </Box>\r\n <Box>\r\n <Text {...cp(\"#888888\")}>\r\n {formatAmount(stock.amount)}\r\n </Text>\r\n </Box>\r\n </Box>\r\n );\r\n })}\r\n </Box>\r\n\r\n {/* 底栏 */}\r\n <Box marginTop={1}>\r\n <Text dimColor>\r\n {` ↑/↓ 选择 Enter 详情 r 手动刷新 o 排序 h 置灰/恢复 q 返回`}\r\n </Text>\r\n </Box>\r\n <Box>\r\n <Text dimColor>{` 最后更新: ${lastUpdate} 编辑自选股: `}</Text>\r\n <Text {...cp(\"#c792ea\")}>\r\n {osc8Link(toFileUrl(SETTINGS_PATH), SETTINGS_PATH)}\r\n </Text>\r\n </Box>\r\n\r\n {/* 双击 Ctrl+C 退出提示 */}\r\n {doubleCtrlC && (\r\n <Box marginTop={1}>\r\n <Text bold {...cp(\"#ff1493\")}>\r\n {\" ⚠ 再按一次 Ctrl+C 退出 dskcode\"}\r\n </Text>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 详情视图(独立的渲染函数)\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction renderDetail(\r\n stock: StockRow,\r\n _onBack: () => void,\r\n prices?: number[],\r\n countdown = 10,\r\n currentTime?: string,\r\n) {\r\n const isUp = stock.changePercent >= 0;\r\n const colorCode = isUp ? \"#ff1493\" : \"#00ff41\";\r\n const arrow = isUp ? \"▲\" : \"▼\";\r\n\r\n // 折线图\r\n let chartLines: string[] = [];\r\n if (prices && prices.length > 0) {\r\n const chartColor = isUp ? asciichart.red : asciichart.green;\r\n // 取最新 60 条分时数据展示近期走势\r\n const latest = latestPoints(prices, 60);\r\n let raw = asciichart.plot(latest, {\r\n height: 10,\r\n colors: [chartColor],\r\n });\r\n // 弯角 → 直角,折线更硬朗\r\n raw = raw\r\n .replaceAll(\"╭\", \"┌\")\r\n .replaceAll(\"╮\", \"┐\")\r\n .replaceAll(\"╰\", \"└\")\r\n .replaceAll(\"╯\", \"┘\");\r\n chartLines = raw.split(\"\\n\");\r\n }\r\n\r\n return (\r\n <Box flexDirection=\"column\" paddingLeft={1}>\r\n {/* 标题行 — 左:名称代码 + 实时时间,右:刷新倒计时 */}\r\n <Box marginBottom={1} justifyContent=\"space-between\">\r\n <Box>\r\n <Text bold color=\"#00ffff\">\r\n {\" 📊 \"}{stock.name}{\" \"}\r\n </Text>\r\n <Text dimColor>\r\n {stock.code}\r\n </Text>\r\n {currentTime && (\r\n <Text dimColor>\r\n {\" 🕐 \"}{currentTime}\r\n </Text>\r\n )}\r\n </Box>\r\n <Text dimColor>\r\n {`${countdown}s 后刷新`}\r\n </Text>\r\n </Box>\r\n\r\n {/* 价格摘要 */}\r\n <Box>\r\n <Box width={16}>\r\n <Text bold color=\"#888888\">当前价</Text>\r\n </Box>\r\n <Box>\r\n <Text bold color={colorCode}>\r\n {arrow} {formatPrice(stock.price)}\r\n </Text>\r\n </Box>\r\n </Box>\r\n <Box>\r\n <Box width={16}>\r\n <Text color=\"#888888\">涨跌幅</Text>\r\n </Box>\r\n <Box>\r\n <Text color={colorCode}>\r\n {isUp ? \"+\" : \"\"}{stock.changePercent.toFixed(2)}%\r\n {\" \"}\r\n {isUp ? \"+\" : \"\"}{stock.changeAmount.toFixed(3)}\r\n </Text>\r\n </Box>\r\n </Box>\r\n\r\n {/* 折线图 */}\r\n {chartLines.length > 0 && (\r\n <Box marginTop={1} flexDirection=\"column\">\r\n {chartLines.map((line, i) => (\r\n <Box key={i}>\r\n <Text color={colorCode}>\r\n {line || \" \"}\r\n </Text>\r\n </Box>\r\n ))}\r\n </Box>\r\n )}\r\n\r\n {/* 底部 */}\r\n <Box marginTop={1}>\r\n <Text dimColor>\r\n {\" Space/q 返回列表\"}\r\n </Text>\r\n </Box>\r\n </Box>\r\n );\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 递归扫描项目源码文件(排除 node_modules 等非源码目录)\r\n// 启动时调用一次,结果缓存为扁平路径数组传给 UI\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readdir } from \"node:fs/promises\";\r\nimport { join, relative } from \"node:path\";\r\n\r\n/** 需要跳过的目录名(任何层级) */\r\nconst IGNORE_DIRS = new Set([\r\n \"node_modules\",\r\n \".git\",\r\n \".svn\",\r\n \".hg\",\r\n \".dskcode\",\r\n \".claude\",\r\n \"dist\",\r\n \"build\",\r\n \".next\",\r\n \".turbo\",\r\n \".nx\",\r\n \"coverage\",\r\n \".cache\",\r\n \".nyc_output\",\r\n \".vscode\",\r\n \".idea\",\r\n \"__pycache__\",\r\n \".venv\",\r\n \"venv\",\r\n \".tox\",\r\n \"target\",\r\n \"vendor\",\r\n \"bower_components\",\r\n \"jspm_packages\",\r\n]);\r\n\r\n/** 只索引这些源码扩展名 */\r\nconst SOURCE_EXTS = new Set([\r\n \".ts\", \".tsx\", \".js\", \".jsx\", \".mjs\", \".cjs\",\r\n \".vue\", \".svelte\", \".astro\",\r\n \".css\", \".scss\", \".less\",\r\n \".html\",\r\n \".json\",\r\n \".md\",\r\n \".yaml\", \".yml\",\r\n \".toml\",\r\n]);\r\n\r\n/**\r\n * 递归扫描 dir 下的所有文件,返回相对于 baseDir 的路径数组。\r\n *\r\n * 优化手段:\r\n * - readdir withFileTypes 省掉 stat 调用(减少一半 syscall)\r\n * - 同层目录的 readdir 用 Promise.all 并行\r\n * - 跳过以 . 开头的隐藏目录和已知非源码目录\r\n *\r\n * 性能预期(SSD):\r\n * 1,000 文件 → ~15ms\r\n * 10,000 文件 → ~150ms\r\n * 50,000 文件 → ~800ms\r\n */\r\nexport async function scanProjectFiles(\r\n baseDir: string,\r\n dir?: string,\r\n): Promise<string[]> {\r\n const currentDir = dir ?? baseDir;\r\n\r\n let entries;\r\n try {\r\n entries = await readdir(currentDir, { withFileTypes: true });\r\n } catch {\r\n return [];\r\n }\r\n\r\n const files: string[] = [];\r\n const dirs: string[] = [];\r\n\r\n for (const entry of entries) {\r\n const name = entry.name;\r\n if (IGNORE_DIRS.has(name) || name.startsWith(\".\")) continue;\r\n\r\n if (entry.isDirectory()) {\r\n dirs.push(name);\r\n } else if (entry.isFile()) {\r\n const ext = name.slice(name.lastIndexOf(\".\")).toLowerCase();\r\n if (SOURCE_EXTS.has(ext)) {\r\n files.push(relative(baseDir, join(currentDir, name)));\r\n }\r\n }\r\n }\r\n\r\n // 同层子目录并行扫描\r\n const nested = await Promise.all(\r\n dirs.map((d) => scanProjectFiles(baseDir, join(currentDir, d))),\r\n );\r\n\r\n for (const n of nested) {\r\n files.push(...n);\r\n }\r\n\r\n return files;\r\n}\r\n","/** dskcode 退出码规范 */\r\nexport const ExitCode = {\r\n /** 正常执行完成 */\r\n SUCCESS: 0,\r\n /** 通用错误 */\r\n GENERAL_ERROR: 1,\r\n /** 配置错误 */\r\n CONFIG_ERROR: 2,\r\n /** 用户通过 Ctrl+C 中断 */\r\n SIGINT: 130,\r\n} as const;\r\n","#!/usr/bin/env node\r\n\r\nimport { createCli } from \"./cli/index.js\";\r\nimport { ExitCode } from \"./cli/exit-codes.js\";\r\nimport { CostTracker } from \"./provider/cost-tracker.js\";\r\nimport { ConversationLogger } from \"./logger/index.js\";\r\n\r\n/**\r\n * 双击 Ctrl+C 退出:\r\n * 第一次 SIGINT → 提示用户再按一次\r\n * 第二次 SIGINT(1.5 秒内)→ 立即退出\r\n *\r\n * 在 ink 交互模式下,Ctrl+C 由 useInput 捕获处理,\r\n * 此处只处理 ink 未运行时的 SIGINT(如启动阶段)。\r\n */\r\nlet sigintCount = 0;\r\n// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents\r\nlet sigintTimer: NodeJS.Timeout | null = null;\r\n\r\n/**\r\n * 全局兜底 SIGINT 处理:双击退出。\r\n * ink 交互模式下 Ctrl+C 由 useDoubleCtrlC hook 处理,\r\n * 此处仅处理 ink 未运行时的场景(如启动阶段、异常退出后)。\r\n *\r\n * 第二次 SIGINT / 任意 SIGTERM / 正常退出前都会统一 flush 所有\r\n * CostTracker 实例,确保今日用量数据落盘。\r\n */\r\nasync function gracefulExit(code: number): Promise<never> {\r\n try {\r\n await Promise.all([\r\n CostTracker.flushAll(),\r\n ConversationLogger.flushAll(),\r\n ]);\r\n } catch {\r\n // 兜底路径,flush 失败不影响退出码\r\n }\r\n process.exit(code);\r\n}\r\n\r\nprocess.on(\"SIGINT\", () => {\r\n sigintCount++;\r\n if (sigintCount >= 2) {\r\n void gracefulExit(ExitCode.SIGINT);\r\n return;\r\n }\r\n process.stdout.write(\"\\n ⚠ 再按一次 Ctrl+C 退出 dskcode\\n\");\r\n if (sigintTimer) clearTimeout(sigintTimer);\r\n sigintTimer = setTimeout(() => {\r\n sigintCount = 0;\r\n }, 1500);\r\n});\r\n\r\nprocess.on(\"SIGTERM\", () => {\r\n void gracefulExit(ExitCode.SIGINT);\r\n});\r\n\r\nconst program = createCli();\r\n\r\ntry {\r\n await program.parseAsync(process.argv);\r\n // 正常执行完毕后也兜底 flush(一般 record() 内部已 fire-and-forget,\r\n // 这里保证若本次没有任何 record 调用也不漏盘)\r\n await Promise.all([CostTracker.flushAll(), ConversationLogger.flushAll()]);\r\n} catch (err: unknown) {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const error = err as { exitCode?: number; code?: string };\r\n\r\n if (error.code === \"commander.helpDisplayed\" || error.code === \"commander.version\") {\r\n await Promise.all([CostTracker.flushAll(), ConversationLogger.flushAll()]);\r\n process.exit(error.exitCode ?? ExitCode.SUCCESS);\r\n }\r\n\r\n if (typeof error.exitCode === \"number\") {\r\n await Promise.all([CostTracker.flushAll(), ConversationLogger.flushAll()]);\r\n process.exit(error.exitCode);\r\n }\r\n\r\n console.error(String(err));\r\n await Promise.all([CostTracker.flushAll(), ConversationLogger.flushAll()]);\r\n process.exit(ExitCode.GENERAL_ERROR);\r\n}\r\n"],"mappings":"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","names":["SUPPORTED_MODELS","program","chalk","resolve","createInterface","existsSync","mkdir","readFile","join","chalk","stat","createInterface","resolve","mkdir","join","existsSync","stat","chalk","readFile","readFile","join","Text","jsxs","Box","Text","jsx","Box","Text","useEffect","useState","useCallback","useRef","useState","useEffect","InkSpinner","Box","Text","useEffect","useRef","useState","mkdir","readFile","writeFile","join","join","readFile","mkdir","writeFile","Box","Text","jsx","jsxs","Box","Text","cp","lines","jsx","jsxs","Box","Text","jsx","jsxs","useState","useRef","useEffect","cost","Text","Box","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","HIGHLIGHT_COLOR","Box","Text","InkSpinner","jsx","jsxs","registry","mkdir","readFile","writeFile","readdir","join","mkdir","join","cp","relative","realpath","process","process","patch","writeFile","readFile","relative","readFile","relative","mkdir","readFile","relative","basename","readFile","mkdir","basename","relative","readFile","writeFile","basename","readFile","writeFile","basename","readFile","writeFile","basename","readFile","writeFile","basename","readFile","writeFile","basename","readFile","writeFile","basename","process","existsSync","join","isWindows","process","existsSync","join","readdir","stat","join","relative","isAbsolute","readdir","join","relative","isAbsolute","stat","readdir","readFile","stat","join","relative","isAbsolute","readdir","join","isAbsolute","stat","readFile","relative","readdir","stat","join","relative","readdir","stat","join","relative","Fragment","jsx","jsxs","useState","useRef","useEffect","useCallback","DeepSeekProvider","Box","Text","cp","InkSpinner","Box","Text","useInput","useState","useCallback","jsx","jsxs","useState","useInput","useCallback","Box","Text","Box","Text","useInput","render","useState","useEffect","useRef","useCallback","jsx","jsxs","tick","s","resolve","Box","Text","useInput","render","useState","useEffect","useRef","useCallback","jsx","jsxs","CYBER_PALETTE","createInitialState","update","tick","s","resolve","createInitialState","chalk","createInitialState","resolve","render","chalk","Box","Text","useInput","useState","useCallback","useEffect","useMemo","join","jsx","jsxs","join","useState","useMemo","useEffect","useCallback","useInput","jsx","Box","Text","cp","jsxs","readdir","join","relative","jsx","program","chalk","join","readFile","resolve","render"]}
|