dskcode 0.1.0 → 0.1.2

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/README.md CHANGED
@@ -1,282 +1,132 @@
1
- # 从零开发 TypeScript 版 Agent CLI 系列博客
1
+ # dskcode
2
2
 
3
- > 本系列基于 [Reasonix](https://github.com/esengine/DeepSeek-Reasonix) 的架构设计,从零开始用 TypeScript 构建一个生产级的 AI 编程助手 CLI 工具。贯穿单体 CLI 设计、LLM 集成、插件系统等核心主题。
3
+ > 基于 DeepSeek AI 编程助手终端 CLI 工具。让 AI 直接在终端中理解你的代码、读写文件、执行命令。
4
+
5
+ [![npm version](https://img.shields.io/npm/v/dskcode)](https://www.npmjs.com/package/dskcode)
6
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)
7
+ [![License](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
4
8
 
5
9
  ---
6
10
 
7
- ## 目录
8
-
9
- | # | 章节 | 核心内容 |
10
- |---|------|----------|
11
- | 01 | [项目初始化与工程基建](#01-项目初始化与工程基建) | monorepo 结构、tsconfig、ESM/CJS、lint、test |
12
- | 02 | [CLI 框架搭建与子命令路由](#02-cli-框架搭建与子命令路由) | commander + inquirer、chat/run/setup 子命令 |
13
- | 03 | [配置系统:TOML 加载与分层合并](#03-配置系统toml-加载与分层合并) | 多级配置文件、flag 覆盖、环境变量 |
14
- | 04 | [Provider 抽象层与多模型支持](#04-provider-抽象层与多模型支持) | Provider 接口、DeepSeek/OpenAI 适配器、工厂注册 |
15
- | 05 | [LLM API 客户端:流式补全与错误处理](#05-llm-api-客户端流式补全与错误处理) | fetch SSE、AbortController、超时重试 |
16
- | 06 | [Token 计价与成本追踪系统](#06-token-计价与成本追踪系统) | 消耗记录、Prefix Cache 优化、累计统计 |
17
- | 07 | [Agent 主循环:消息编排与多轮对话](#07-agent-主循环消息编排与多轮对话) | System/User/Assistant/Tool 消息组装、轮次控制 |
18
- | 08 | [工具系统:内置工具的设计与注册](#08-工具系统内置工具的设计与注册) | Tool 接口、Registry 模式、文件操作 / Bash / Grep |
19
- | 09 | [MCP 插件系统:stdio JSON-RPC 协议](#09-mcp-插件系统stdio-json-rpc-协议) | 子进程管理、JSON-RPC 编解码、生命周期 |
20
- | 10 | [权限控制:逐调用审批策略](#10-权限控制逐调用审批策略) | Allow/Ask/Deny 三级策略、交互式审批 |
21
- | 11 | [会话管理与项目记忆](#11-会话管理与项目记忆) | 对话历史持久化、AGENTS.md 上下文注入 |
22
- | 12 | [终端交互:流式渲染与彩色输出](#12-终端交互流式渲染与彩色输出) | 打字机效果、spinner、markdown 渲染 |
23
- | 13 | [管道模式与 stdin/stdout 集成](#13-管道模式与-stdinstdout-集成) | pipe 输入、非交互模式、Unix 哲学 |
24
- | 14 | [配置向导与初次运行体验](#14-配置向导与初次运行体验) | 交互式 setup、API Key 管理、环境检测 |
25
- | 15 | [构建发布与 CI/CD 流水线](#15-构建发布与cicd-流水线) | tsup 打包、npm publish、GitHub Actions、跨平台 |
11
+ ## 特性
12
+
13
+ - **终端原生交互** `dskcode chat` 进入交互式对话,在终端中直接与 AI 协作编码
14
+ - **一次性任务执行** — `dskcode run 重构所有 TODO 为 Jira 链接` 让 AI 自动完成
15
+ - **DeepSeek 深度集成** 原生 DeepSeek API 支持,Prefix Cache 感知,成本透明
16
+ - **工具系统** AI 可以读文件、写代码、执行命令、搜索代码,像人类开发者一样工作
17
+ - **MCP 插件** 通过 Model Context Protocol 扩展任意外部工具
18
+ - **项目记忆** AGENTS.md 让你的项目上下文被 AI 理解
19
+ - **权限控制** 三级审批策略(Allow / Ask / Deny),安全可控
20
+ - **TOML 配置** 多层级配置(全局 + 项目 + 环境变量 + CLI flag)
21
+ - **中文优先** 界面提示、帮助信息、文档均为中文
26
22
 
27
- ---
23
+ ## 快速开始
28
24
 
29
- ## 01 项目初始化与工程基建
30
-
31
- 建立 TypeScript CLI 项目的完整基础设施。
32
-
33
- - **包管理选型**:pnpm workspace 还是 bun?monorepo vs single package
34
- - **tsconfig 配置**:`target: ES2022`、`module: NodeNext`、`moduleResolution: NodeNext`
35
- - **ESM 与 CJS**:双格式输出策略,`exports` 字段配置
36
- - **Lint & Format**:ESLint flat config + Prettier + 自定义规则
37
- - **测试框架**:Vitest 单测 + tsx 执行 ESM
38
- - **目录结构设计**:`src/` 分层(`cli/` `agent/` `provider/` `tool/` `plugin/` `config/`)
39
- - **依赖选型**:最小依赖原则,只有 `commander` `smol-toml` 等少数核心依赖
40
- - **构建工具**:tsup 打包为单文件 CLI(esbuild 底座)
41
- - **入口与 shebang**:`#!/usr/bin/env node` + `bin` 字段
25
+ ```bash
26
+ # 全局安装
27
+ npm install -g dskcode
42
28
 
43
- ## 02 CLI 框架搭建与子命令路由
29
+ # 查看帮助
30
+ dskcode --help
44
31
 
45
- 设计 CLI 的外壳:命令解析、全局选项、子命令分发。
32
+ # 启动交互式对话
33
+ dskcode chat
34
+ ```
46
35
 
47
- - **框架选型**:commander vs yargs vs citty,选择 commander 的原因
48
- - **顶层入口**:`reasonix` 主命令,全局 `--version` `--verbose` `--config`
49
- - **子命令体系**:
50
- - `chat` — 交互式对话
51
- - `run` — 一次性任务执行
52
- - `setup` — 配置向导
53
- - `init` — 生成 AGENTS.md
54
- - **全局中间件**:配置加载、鉴权检查、日志初始化
55
- - **退出码规范**:0=成功 1=通用错误 2=配置错误 130=SIGINT
56
- - **帮助信息**:自定义 help 格式化,彩色输出
57
- - **自动补全**:commander 内置的 shell completion
58
-
59
- ## 03 配置系统:TOML 加载与分层合并
60
-
61
- 构建健壮的多级配置体系,支持从全局到项目的逐层覆盖。
62
-
63
- - **TOML 解析**:为什么选 `smol-toml` 而非 `@iarna/toml`
64
- - **配置层级**:默认值 → 用户全局 `~/.config/reasonix.toml` → 项目 `.reasonix.toml` → 环境变量 → CLI flag
65
- - **配置合并**:deepMerge 策略,数组 vs 对象的合并规则
66
- - **配置类型**:用 TypeScript 类型定义完整的 Config 结构体
67
- - **Provider 声明**:多个 provider 的配置格式(apiKey、baseUrl、model 映射)
68
- - **Tool 开关**:默认配置中启用/禁用工具
69
- - **Plugin 声明**:外部 MCP 服务器配置
70
- - **配置校验**:必填字段检查,友好的错误提示
71
- - **配置热加载**:watch 模式检测文件变更
72
-
73
- ## 04 Provider 抽象层与多模型支持
74
-
75
- 设计 Provider 接口,支持 DeepSeek、OpenAI 兼容接口等多种后端。
76
-
77
- - **Provider 接口设计**:
78
-
79
- ```typescript
80
- interface Provider {
81
- name: string
82
- chat(messages: Message[], opts: ChatOptions): AsyncIterable<Chunk>
83
- countTokens(text: string): number
84
- model(): string
85
- }
86
- ```
87
-
88
- - **工厂注册模式**:`registry.register('deepseek', DeepSeekProvider)` + `registry.get('deepseek')`
89
- - **DeepSeek Provider**:DeepSeek API 适配,prefix cache 感知
90
- - **OpenAI 兼容 Provider**:通用 OpenAI 适配器,支持任意 baseUrl
91
- - **双模型模式**:executor + planner 分离,独立缓存会话
92
- - **模型预设**:内置 `deepseek-flash` `deepseek-pro` `mimo-pro` 预设
93
- - **错误映射**:HTTP 状态码 → 结构化错误(auth / rate-limit / server-error)
94
-
95
- ## 05 LLM API 客户端:流式补全与错误处理
96
-
97
- 实现底层的 API 通信层,确保流式传输稳定可靠。
98
-
99
- - **Fetch 封装**:原生 `fetch` vs `undici`,Node 18+ 兼容策略
100
- - **SSE 流式解析**:手动解析 `data: ...` 事件流,处理 `[DONE]` 标记
101
- - **AsyncGenerator**:用 `async function*` 暴露流式块
102
- - **AbortController**:用户 Ctrl+C 中断请求
103
- - **超时控制**:连接超时 + 流式空闲超时
104
- - **指数退避重试**:429 / 5xx 自动重试
105
- - **请求合并**:同一轮对话的 token 统计汇总
106
- - **Types 定义**:ChatCompletionRequest / Chunk / Usage 等完整类型
107
-
108
- ## 06 Token 计价与成本追踪系统
109
-
110
- LLM 成本透明化,让用户清楚每次调用的开销。
111
-
112
- - **Token 统计**:`usage.prompt_tokens` + `usage.completion_tokens`
113
- - **计价模型**:不同 model 的不同单价表(输入/输出/缓存命中)
114
- - **Prefix Cache**:DeepSeek 专属优化,缓存命中的 token 半价
115
- - **会话累计成本**:单次会话的 token 和费用累加
116
- - **成本显示**:每次工具调用后显示 ≈$0.0032 格式
117
- - **预算限制**:配置最大成本/最大 token 数,自动中止
118
- - **持久化历史**:历史会话的成本查询
119
-
120
- ## 07 Agent 主循环:消息编排与多轮对话
121
-
122
- Agent 的核心——消息构建、循环控制、工具决策。
123
-
124
- - **消息结构**:SystemMessage / UserMessage / AssistantMessage / ToolMessage
125
- - **System Prompt 构建**:动态注入 AGENTS.md、可用工具描述、时间信息
126
- - **主循环流程**:
127
-
128
- ```
129
- user input → build messages → call provider →
130
- parse response → has tool calls? → execute tools → append results → loop
131
- ```
132
-
133
- - **最大轮次保护**:`max_tool_rounds` 防止无限循环
134
- - **终止条件判断**:模型返回最终答案 or 达到轮次上限
135
- - **上下文窗口管理**:超出 max_tokens 时自动裁剪历史
136
- - **多轮消息组装**:append 模式,保持完整上下文
137
-
138
- ## 08 工具系统:内置工具的设计与注册
139
-
140
- 构建类型安全、可扩展的工具系统,是 Agent 能力的核心。
141
-
142
- - **Tool 接口**:
143
-
144
- ```typescript
145
- interface Tool {
146
- name: string
147
- description: string
148
- parameters: JSONSchema
149
- execute(args: unknown, ctx: ToolContext): Promise<ToolResult>
150
- }
151
- ```
152
-
153
- - **JSON Schema 定义**:parameters 用 JSONSchema 描述,供模型理解
154
- - **Registry 注册机制**:`ToolRegistry.register(tool)` + `ToolRegistry.list()`
155
- - **内置工具列表**:
156
- - `read_file` — 读取文件
157
- - `write_file` — 写入/修改文件
158
- - `edit_file` — 精确替换
159
- - `bash` — 执行 shell 命令
160
- - `glob` — 文件搜索
161
- - `grep` — 内容搜索
162
- - `ls` — 目录列表
163
- - `fetch` — HTTP 请求
164
- - **工具执行沙箱**:当前工作目录约束、超时控制
165
- - **toolUse 解析**:从模型响应中解析 function_call / tool_use
166
-
167
- ## 09 MCP 插件系统:stdio JSON-RPC 协议
168
-
169
- 支持 MCP(Model Context Protocol)插件,扩展外部工具。
170
-
171
- - **MCP 协议概览**:JSON-RPC 2.0 over stdio
172
- - **子进程管理**:`child_process.spawn()` 启动插件进程
173
- - **生命周期**:initialize → listTools → callTool → shutdown
174
- - **传输层**:stdin 写入请求,stdout 读取响应(行分隔 JSON)
175
- - **请求/响应匹配**:JSON-RPC `id` 字段关联
176
- - **适配器转换**:MCP Tool → 系统 Tool 接口适配
177
- - **插件声明配置**:`[[plugin]]` TOML 配置段
178
- - **错误处理**:插件崩溃自动恢复、超时机制
179
- - **参考实现**:编写一个 `reasonix-plugin-example`
180
-
181
- ## 10 权限控制:逐调用审批策略
182
-
183
- 安全执行机制,防止危险操作未经用户确认。
184
-
185
- - **三级权限策略**:
186
-
187
- ```
188
- Allow — 自动批准(白名单命令)
189
- Ask — 每次询问用户(默认)
190
- Deny — 禁止执行(黑名单)
191
- ```
192
-
193
- - **策略匹配规则**:工具名 + 参数模式匹配
194
- - **交互式审批**:确认/拒绝/永久记住,彩色提示
195
- - **允许列表**:`read_file` `grep` `glob` 默认 allow
196
- - **询问列表**:`write_file` `edit_file` `bash` `fetch` 默认 ask
197
- - **拒绝列表**:`rm -rf /` 等危险命令正则匹配
198
- - **Policy 引擎**:`PermissionPolicy.shouldAllow(tool, args) → Decision`
199
- - **Session 级记住**:一次会话内不再重复询问
200
-
201
- ## 11 会话管理与项目记忆
202
-
203
- 持久化对话上下文,让 Agent 记住项目信息。
204
-
205
- - **会话文件结构**:`.reasonix/sessions/*.json`
206
- - **消息序列化**:完整消息历史存入磁盘
207
- - **项目记忆(AGENTS.md)**:自动生成项目上下文文件
208
- - **`/init` 命令**:扫描项目结构,生成初始 AGENTS.md
209
- - **AGENTS.md 注入**:系统提示词末尾注入项目记忆
210
- - **会话恢复**:`--resume` 会话 ID 恢复历史
211
- - **自动清理**:超过 `max_sessions` 自动淘汰
212
- - **Session ID 生成**:UUID + 时间戳
213
-
214
- ## 12 终端交互:流式渲染与彩色输出
215
-
216
- 打造丝滑的终端体验。
217
-
218
- - **打字机效果**:逐块叠加文本,无闪烁
219
- - **Spinner 动画**:等待 LLM/Tool 响应时的进度指示
220
- - **Markdown 渲染**:使用 `marked` + `terminal-link` 渲染内联代码和链接
221
- - **代码块高亮**:行号 + 语法高亮(shiki light)
222
- - **彩色输出**:chalk / picocolors 的颜色方案
223
- - **Tool 调用可视化**:嵌套缩进展示工具调用链
224
- - **进度条**:长耗时工具调用的进度指示
225
- - **多行刷新**:`process.stdout.write` + `\r` 实现原位更新
226
-
227
- ## 13 管道模式与 stdin/stdout 集成
228
-
229
- 让 CLI 遵循 Unix 哲学:做好一件事,支持管道组合。
230
-
231
- - **Pipe 输入检测**:`isTTY` 判断是否管道输入
232
- - **非交互模式**:单条输入,单次输出
233
- - **`echo "explain this" | reasonix run`**:管道输入处理
234
- - **JSON 输出模式**:`--json` 输出结构化结果
235
- - **静默模式**:`--silent` 仅输出最终结果
236
- - **stdin 流处理**:大文件管道输入的分块读取
237
- - **与 jq 组合**:`reasonix run --json | jq .files[].path`
238
-
239
- ## 14 配置向导与初次运行体验
240
-
241
- 降低新用户的上手门槛,提供友好的初次运行体验。
242
-
243
- - **`reasonix setup` 命令**:
244
- - 检测 `DEEPSEEK_API_KEY` 环境变量
245
- - 交互式填写 API Key(inquirer password 模式)
246
- - 选择默认模型(flash / pro)
247
- - 选择工作目录
248
- - **一键测试**:`reasonix setup --test` 发送测试请求验证
249
- - **`.env` 文件支持**:自动生成 `.env.example`
250
- - **`.gitignore` 集成**:自动添加 `.env` 到 gitignore
251
- - **首次运行检测**:无配置时自动跳转 setup
252
- - **配置导出**:`reasonix setup --export` 输出 JSON 格式配置
253
- - **`reasonix doctor`**:诊断环境问题(API Key、网络、Node 版本)
254
-
255
- ## 15 构建发布与 CI/CD 流水线
256
-
257
- 从源码到 npm 包的完整发布流程。
258
-
259
- - **构建配置(tsup)**:
260
- - `entry: src/index.ts`
261
- - `target: node18`
262
- - `format: ['esm', 'cjs']`
263
- - minify + bundle 单文件
264
- - **npm 包配置**:
265
- - `bin` 字段
266
- - `exports` 条件导出
267
- - `files` 白名单
268
- - **GitHub Actions CI**:
269
- - lint + type-check + test
270
- - ESM/CJS 兼容性测试
271
- - 跨平台 Node 版本矩阵
272
- - **自动发布**:
273
- - `semantic-release` + commitlint
274
- - npm publish 自动化
275
- - Changelog 自动生成
276
- - **发布检查清单**:手动发布前的完整性自检
277
- - **版本策略**:SemVer + 预发布标签
36
+ ### 使用 npx
278
37
 
279
- ---
38
+ ```bash
39
+ npx dskcode --help
40
+ npx dskcode chat
41
+ ```
42
+
43
+ ## 命令
44
+
45
+ | 命令 | 说明 |
46
+ |------|------|
47
+ | `dskcode chat` | 启动交互式对话会话 |
48
+ | `dskcode run <prompt>` | 执行一次性任务(如"修改所有 TODO") |
49
+ | `dskcode setup` | 运行配置向导,设置 API Key 等 |
50
+ | `dskcode init` | 在当前项目生成 AGENTS.md 项目记忆文件 |
51
+ | `dskcode completion` | 生成 shell 自动补全配置 |
52
+
53
+ ### 全局选项
54
+
55
+ | 选项 | 说明 |
56
+ |------|------|
57
+ | `-V, --version` | 显示版本号 |
58
+ | `--verbose` | 开启详细日志输出 |
59
+ | `--config <path>` | 指定配置文件路径 |
60
+ | `-h, --help` | 显示帮助信息 |
61
+
62
+ ## 配置
63
+
64
+ dskcode 使用 TOML 格式的配置文件,支持多层级合并:
65
+
66
+ 1. **内置默认值** — 无需配置即可运行
67
+ 2. **用户全局** — `~/.config/dskcode.toml`
68
+ 3. **项目本地** — 当前目录下的 `.dskcode.toml`
69
+ 4. **环境变量** — 如 `DEEPSEEK_API_KEY`
70
+ 5. **CLI flag** — 命令行参数优先级最高
71
+
72
+ ### 配置示例
73
+
74
+ ```toml
75
+ defaultProvider = "deepseek"
76
+
77
+ [providers]
78
+ [providers.deepseek]
79
+ apiKey = "sk-xxx"
80
+ baseUrl = "https://api.deepseek.com"
81
+ model = "deepseek-chat"
82
+
83
+ [tools]
84
+ read_file = true
85
+ write_file = true
86
+ bash = true
87
+ grep = true
88
+ ```
89
+
90
+ ## 架构
91
+
92
+ ```
93
+ src/
94
+ ├── index.ts # 入口,shebang + 异常处理
95
+ ├── cli/ # commander 命令路由
96
+ ├── config/ # TOML 配置加载与合并
97
+ ├── provider/ # LLM Provider 接口(DeepSeek / OpenAI 兼容)
98
+ ├── tool/ # 内置工具接口(读文件、写文件、bash 等)
99
+ ├── plugin/ # MCP 插件管理器
100
+ ├── agent/ # Agent 会话循环
101
+ └── ui/ # Ink 交互式终端 UI
102
+ ```
103
+
104
+ ## 开发
105
+
106
+ ```bash
107
+ # 克隆仓库
108
+ git clone https://github.com/esengine/DeepSeek-Reasonix.git
109
+ cd ts-version
110
+
111
+ # 安装依赖
112
+ npm install
113
+
114
+ # 开发模式(自动监听重构建)
115
+ npm run dev
116
+
117
+ # 构建
118
+ npm run build
119
+
120
+ # 测试
121
+ npm test
122
+
123
+ # 类型检查
124
+ npm run type-check
125
+
126
+ # 代码检查
127
+ npm run lint
128
+ ```
129
+
130
+ ## 许可
280
131
 
281
- > **下篇预告**:各章节完成时将附带完整的代码仓库链接和可运行示例。
282
- > 欢迎 Star & PR!
132
+ [MIT](LICENSE)
@@ -29,8 +29,8 @@ async function loadConfig(configPath) {
29
29
  candidates.push(configPath);
30
30
  } else {
31
31
  candidates.push(
32
- join(process.env.HOME ?? process.env.USERPROFILE ?? "~", ".config", "dsk.toml"),
33
- join(process.cwd(), ".dsk.toml")
32
+ join(process.env.HOME ?? process.env.USERPROFILE ?? "~", ".config", "dskcode.toml"),
33
+ join(process.cwd(), ".dskcode.toml")
34
34
  );
35
35
  }
36
36
  let config = structuredClone(defaultConfig);
@@ -58,4 +58,4 @@ export {
58
58
  defaultConfig,
59
59
  loadConfig
60
60
  };
61
- //# sourceMappingURL=chunk-URZUL36S.js.map
61
+ //# sourceMappingURL=chunk-GMSRSNWA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config/loader.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\r\nimport { join } from \"node:path\";\r\nimport { parse } from \"smol-toml\";\r\nimport type { Config } from \"./types.js\";\r\n\r\n/** 出厂默认配置 — 在找不到配置文件时使用。 */\r\nexport const defaultConfig: Config = {\r\n defaultProvider: \"deepseek\",\r\n providers: [\r\n {\r\n name: \"deepseek\",\r\n baseUrl: \"https://api.deepseek.com\",\r\n model: \"deepseek-chat\",\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};\r\n\r\n/**\r\n * 从 TOML 文件加载配置,未找到时回退到默认值。\r\n *\r\n * 解析顺序(后加载的优先级更高):\r\n * 1. 内置默认值\r\n * 2. 用户全局 ~/.config/dskcode.toml\r\n * 3. 项目本地 .dskcode.toml(或通过 --config 指定的路径)\r\n */\r\nexport async function loadConfig(configPath?: string): Promise<Config> {\r\n const candidates: string[] = [];\r\n\r\n if (configPath) {\r\n candidates.push(configPath);\r\n } else {\r\n candidates.push(\r\n join(process.env.HOME ?? process.env.USERPROFILE ?? \"~\", \".config\", \"dskcode.toml\"),\r\n join(process.cwd(), \".dskcode.toml\"),\r\n );\r\n }\r\n\r\n let config: Config = structuredClone(defaultConfig);\r\n\r\n for (const candidate of candidates) {\r\n try {\r\n const raw = await readFile(candidate, \"utf-8\");\r\n const parsed = parse(raw) as unknown as Partial<Config>;\r\n config = mergeConfig(config, parsed);\r\n } catch {\r\n // file doesn't exist or can't be read — skip\r\n }\r\n }\r\n\r\n return config;\r\n}\r\n\r\n/** 将部分配置深度合并到已有配置之上。 */\r\nfunction mergeConfig(base: Config, overlay: Partial<Config>): Config {\r\n return {\r\n ...base,\r\n ...(overlay.defaultProvider !== undefined && { defaultProvider: overlay.defaultProvider }),\r\n ...(overlay.providers !== undefined && { providers: overlay.providers }),\r\n ...(overlay.tools !== undefined && { tools: overlay.tools }),\r\n ...(overlay.plugins !== undefined && { plugins: overlay.plugins }),\r\n };\r\n}\r\n"],"mappings":";AAAA,SAAS,gBAAgB;AACzB,SAAS,YAAY;AACrB,SAAS,aAAa;AAIf,IAAM,gBAAwB;AAAA,EACnC,iBAAiB;AAAA,EACjB,WAAW;AAAA,IACT;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,OAAO;AAAA,IACL,EAAE,MAAM,aAAa,SAAS,KAAK;AAAA,IACnC,EAAE,MAAM,cAAc,SAAS,KAAK;AAAA,IACpC,EAAE,MAAM,aAAa,SAAS,KAAK;AAAA,IACnC,EAAE,MAAM,QAAQ,SAAS,KAAK;AAAA,IAC9B,EAAE,MAAM,QAAQ,SAAS,KAAK;AAAA,IAC9B,EAAE,MAAM,QAAQ,SAAS,KAAK;AAAA,IAC9B,EAAE,MAAM,MAAM,SAAS,KAAK;AAAA,IAC5B,EAAE,MAAM,SAAS,SAAS,KAAK;AAAA,EACjC;AAAA,EACA,SAAS,CAAC;AACZ;AAUA,eAAsB,WAAW,YAAsC;AACrE,QAAM,aAAuB,CAAC;AAE9B,MAAI,YAAY;AACd,eAAW,KAAK,UAAU;AAAA,EAC5B,OAAO;AACL,eAAW;AAAA,MACT,KAAK,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe,KAAK,WAAW,cAAc;AAAA,MAClF,KAAK,QAAQ,IAAI,GAAG,eAAe;AAAA,IACrC;AAAA,EACF;AAEA,MAAI,SAAiB,gBAAgB,aAAa;AAElD,aAAW,aAAa,YAAY;AAClC,QAAI;AACF,YAAM,MAAM,MAAM,SAAS,WAAW,OAAO;AAC7C,YAAM,SAAS,MAAM,GAAG;AACxB,eAAS,YAAY,QAAQ,MAAM;AAAA,IACrC,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SAAO;AACT;AAGA,SAAS,YAAY,MAAc,SAAkC;AACnE,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAI,QAAQ,oBAAoB,UAAa,EAAE,iBAAiB,QAAQ,gBAAgB;AAAA,IACxF,GAAI,QAAQ,cAAc,UAAa,EAAE,WAAW,QAAQ,UAAU;AAAA,IACtE,GAAI,QAAQ,UAAU,UAAa,EAAE,OAAO,QAAQ,MAAM;AAAA,IAC1D,GAAI,QAAQ,YAAY,UAAa,EAAE,SAAS,QAAQ,QAAQ;AAAA,EAClE;AACF;","names":[]}
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  defaultConfig,
3
3
  loadConfig
4
- } from "./chunk-URZUL36S.js";
4
+ } from "./chunk-GMSRSNWA.js";
5
5
  export {
6
6
  defaultConfig,
7
7
  loadConfig
8
8
  };
9
- //# sourceMappingURL=config-57HYL75X.js.map
9
+ //# sourceMappingURL=config-42MSCTVE.js.map
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  loadConfig
4
- } from "./chunk-URZUL36S.js";
4
+ } from "./chunk-GMSRSNWA.js";
5
5
 
6
6
  // src/cli/index.tsx
7
7
  import { Command } from "commander";
@@ -14,7 +14,7 @@ async function loadConfigMiddleware() {
14
14
  try {
15
15
  config = await loadConfig(opts.config);
16
16
  } catch {
17
- const { defaultConfig } = await import("./config-57HYL75X.js");
17
+ const { defaultConfig } = await import("./config-42MSCTVE.js");
18
18
  config = defaultConfig;
19
19
  }
20
20
  return { config, verbose };
@@ -26,7 +26,7 @@ function customHelp(program2) {
26
26
  const lines = [];
27
27
  lines.push("");
28
28
  lines.push(chalk.bold("\u7528\u6CD5:"));
29
- lines.push(` ${chalk.cyan("dsk")} ${chalk.dim("[global-options]")} ${chalk.green("<command>")} ${chalk.dim("[options]")}`);
29
+ lines.push(` ${chalk.cyan("dskcode")} ${chalk.dim("[global-options]")} ${chalk.green("<command>")} ${chalk.dim("[options]")}`);
30
30
  lines.push("");
31
31
  const globalOpts = program2.options.filter(
32
32
  (o) => o.long !== "--help" && o.long !== "--version" && o.long !== "--config"
@@ -59,13 +59,13 @@ function customHelp(program2) {
59
59
  }
60
60
  lines.push(chalk.bold("\u793A\u4F8B:"));
61
61
  lines.push(` ${chalk.dim("# \u542F\u52A8\u4EA4\u4E92\u5F0F\u5BF9\u8BDD")}`);
62
- lines.push(" dsk chat");
62
+ lines.push(" dskcode chat");
63
63
  lines.push(` ${chalk.dim("# \u8BA9 AI \u6267\u884C\u4E00\u4E2A\u4EFB\u52A1")}`);
64
- lines.push(" dsk run \u4FEE\u6539\u6240\u6709 TODO \u6CE8\u91CA");
64
+ lines.push(" dskcode run \u4FEE\u6539\u6240\u6709 TODO \u6CE8\u91CA");
65
65
  lines.push(` ${chalk.dim("# \u8FD0\u884C\u914D\u7F6E\u5411\u5BFC")}`);
66
- lines.push(" dsk setup");
66
+ lines.push(" dskcode setup");
67
67
  lines.push(` ${chalk.dim("# \u751F\u6210 shell \u81EA\u52A8\u8865\u5168")}`);
68
- lines.push(" dsk completion");
68
+ lines.push(" dskcode completion");
69
69
  lines.push("");
70
70
  return lines.join("\n");
71
71
  }
@@ -86,7 +86,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
86
86
  import { Box, Text as Text2 } from "ink";
87
87
  import { jsxs as jsxs2 } from "react/jsx-runtime";
88
88
 
89
- // src/ui/DskSplash.tsx
89
+ // src/ui/DskcodeSplash.tsx
90
90
  import { Box as Box2, Text as Text3 } from "ink";
91
91
  import { useEffect, useState } from "react";
92
92
  import { jsx as jsx2 } from "react/jsx-runtime";
@@ -119,7 +119,7 @@ var COMMANDS = {
119
119
  ].join("\n")
120
120
  },
121
121
  "/clear": { desc: "\u6E05\u7A7A\u5BF9\u8BDD\u5386\u53F2", handler: () => "" },
122
- "/version": { desc: "\u663E\u793A\u7248\u672C\u4FE1\u606F", handler: () => "dsk v0.0.0" }
122
+ "/version": { desc: "\u663E\u793A\u7248\u672C\u4FE1\u606F", handler: () => "dskcode v0.0.0" }
123
123
  };
124
124
  function ChatSession({ providerCount, toolCount, verbose }) {
125
125
  const [offset, setOffset] = useState2(0);
@@ -168,7 +168,7 @@ function ChatSession({ providerCount, toolCount, verbose }) {
168
168
  setMessages((prev) => [
169
169
  ...prev,
170
170
  { role: "user", content: trimmed },
171
- { role: "assistant", content: "dsk AI \u2014 \u5F85\u5B9E\u73B0\uFF08\u7B2C07\u7AE0\uFF09\u3002\u5F53\u524D\u4E3A CLI \u6846\u67B6\u6F14\u793A\u6A21\u5F0F\u3002" }
171
+ { role: "assistant", content: "dskcode AI \u2014 \u5F85\u5B9E\u73B0\uFF08\u7B2C07\u7AE0\uFF09\u3002\u5F53\u524D\u4E3A CLI \u6846\u67B6\u6F14\u793A\u6A21\u5F0F\u3002" }
172
172
  ]);
173
173
  setInput("");
174
174
  }, []);
@@ -220,18 +220,18 @@ var SUBCOMMANDS = ["chat", "run", "setup", "init", "completion"];
220
220
  function createCli() {
221
221
  const program2 = new Command();
222
222
  program2.exitOverride();
223
- program2.name("dsk").description("\u57FA\u4E8E DeepSeek \u7684 AI \u7F16\u7A0B\u52A9\u624B\u7EC8\u7AEF\u5DE5\u5177").version("0.0.0", "-V, --version", "\u663E\u793A\u7248\u672C\u53F7").option("--verbose", "\u5F00\u542F\u8BE6\u7EC6\u65E5\u5FD7\u8F93\u51FA").option("--config <path>", "\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84");
223
+ program2.name("dskcode").description("\u57FA\u4E8E DeepSeek \u7684 AI \u7F16\u7A0B\u52A9\u624B\u7EC8\u7AEF\u5DE5\u5177").version("0.0.0", "-V, --version", "\u663E\u793A\u7248\u672C\u53F7").option("--verbose", "\u5F00\u542F\u8BE6\u7EC6\u65E5\u5FD7\u8F93\u51FA").option("--config <path>", "\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84");
224
224
  program2.helpInformation = () => customHelp(program2);
225
225
  program2.hook("preAction", async (thisCommand) => {
226
226
  const ctx = await loadConfigMiddleware.call(thisCommand);
227
- thisCommand.dskCtx = ctx;
227
+ thisCommand.dskcodeCtx = ctx;
228
228
  });
229
229
  program2.command("chat").description("\u542F\u52A8\u4EA4\u4E92\u5F0F\u5BF9\u8BDD\u4F1A\u8BDD").action(async function() {
230
230
  if (!process.stdin.isTTY) {
231
- console.error("dsk chat \u9700\u8981\u4EA4\u4E92\u5F0F\u7EC8\u7AEF\u3002\u5982\u9700\u6267\u884C\u4E00\u6B21\u6027\u4EFB\u52A1\uFF0C\u8BF7\u4F7F\u7528 dsk run\u3002");
231
+ console.error("dskcode chat \u9700\u8981\u4EA4\u4E92\u5F0F\u7EC8\u7AEF\u3002\u5982\u9700\u6267\u884C\u4E00\u6B21\u6027\u4EFB\u52A1\uFF0C\u8BF7\u4F7F\u7528 dskcode run\u3002");
232
232
  process.exit(1);
233
233
  }
234
- const ctx = this.dskCtx;
234
+ const ctx = this.dskcodeCtx;
235
235
  const app = renderApp(
236
236
  /* @__PURE__ */ jsx4(
237
237
  ChatSession,
@@ -245,22 +245,22 @@ function createCli() {
245
245
  await app.waitUntilExit;
246
246
  });
247
247
  program2.command("run").description("\u6267\u884C\u4E00\u6B21\u6027\u4EFB\u52A1").argument("[prompt...]", "\u4EFB\u52A1\u63CF\u8FF0").option("--model <name>", "\u6307\u5B9A\u4F7F\u7528\u7684\u6A21\u578B").action(async function(_prompt) {
248
- console.log("dsk run \u2014 \u5F85\u5B9E\u73B0\uFF08\u7B2C07\u7AE0\uFF09");
248
+ console.log("dskcode run \u2014 \u5F85\u5B9E\u73B0\uFF08\u7B2C07\u7AE0\uFF09");
249
249
  });
250
250
  program2.command("setup").description("\u8FD0\u884C\u914D\u7F6E\u5411\u5BFC").option("--export", "\u4EE5 JSON \u683C\u5F0F\u5BFC\u51FA\u914D\u7F6E").option("--test", "\u6D4B\u8BD5 API Key \u8FDE\u901A\u6027").action(async function() {
251
- console.log("dsk setup \u2014 \u5F85\u5B9E\u73B0\uFF08\u7B2C14\u7AE0\uFF09");
251
+ console.log("dskcode setup \u2014 \u5F85\u5B9E\u73B0\uFF08\u7B2C14\u7AE0\uFF09");
252
252
  });
253
253
  program2.command("init").description("\u5728\u5F53\u524D\u9879\u76EE\u4E0B\u751F\u6210\u9879\u76EE\u8BB0\u5FC6\u6587\u4EF6\uFF08AGENTS.md\uFF09").action(async function() {
254
- console.log("dsk init \u2014 \u5F85\u5B9E\u73B0\uFF08\u7B2C11\u7AE0\uFF09");
254
+ console.log("dskcode init \u2014 \u5F85\u5B9E\u73B0\uFF08\u7B2C11\u7AE0\uFF09");
255
255
  });
256
256
  program2.command("completion").description("\u8F93\u51FA shell \u81EA\u52A8\u8865\u5168\u914D\u7F6E\u8BF4\u660E\uFF08bash/zsh\uFF09").argument("[shell]", "shell \u7C7B\u578B", /^(bash|zsh)$/i).action(async function(shell) {
257
257
  if (!shell) {
258
- console.log("\u8BF7\u6307\u5B9A shell \u7C7B\u578B\uFF1Adsk completion bash \u6216 dsk completion zsh");
258
+ console.log("\u8BF7\u6307\u5B9A shell \u7C7B\u578B\uFF1Adskcode completion bash \u6216 dskcode completion zsh");
259
259
  return;
260
260
  }
261
261
  if (shell === "bash") {
262
- console.log(`# dsk bash \u81EA\u52A8\u8865\u5168
263
- _dsk_completion() {
262
+ console.log(`# dskcode bash \u81EA\u52A8\u8865\u5168
263
+ _dskcode_completion() {
264
264
  local cur=\${COMP_WORDS[COMP_CWORD]}
265
265
  if [[ \${COMP_CWORD} -eq 1 ]]; then
266
266
  COMPREPLY=( $(compgen -W "${SUBCOMMANDS.join(" ")}" -- "\${cur}") )
@@ -268,10 +268,10 @@ _dsk_completion() {
268
268
  fi
269
269
  COMPREPLY=( $(compgen -W "--verbose --config --model" -- "\${cur}") )
270
270
  }
271
- complete -F _dsk_completion dsk`);
271
+ complete -F _dskcode_completion dskcode`);
272
272
  } else {
273
- console.log(`# dsk zsh \u81EA\u52A8\u8865\u5168
274
- _dsk_completion() {
273
+ console.log(`# dskcode zsh \u81EA\u52A8\u8865\u5168
274
+ _dskcode_completion() {
275
275
  local -a commands
276
276
  commands=(
277
277
  "chat:\u542F\u52A8\u4EA4\u4E92\u5F0F\u5BF9\u8BDD\u4F1A\u8BDD"
@@ -280,9 +280,9 @@ _dsk_completion() {
280
280
  "init:\u751F\u6210\u9879\u76EE\u8BB0\u5FC6\u6587\u4EF6"
281
281
  "completion:\u8F93\u51FA shell \u81EA\u52A8\u8865\u5168\u8BF4\u660E"
282
282
  )
283
- _describe 'dsk commands' commands
283
+ _describe 'dskcode commands' commands
284
284
  }
285
- compdef _dsk_completion dsk`);
285
+ compdef _dskcode_completion dskcode`);
286
286
  }
287
287
  });
288
288
  return program2;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli/index.tsx","../src/cli/middleware.ts","../src/cli/help.ts","../src/ui/RenderScope.tsx","../src/ui/Spinner.tsx","../src/ui/StatusMessage.tsx","../src/ui/DskSplash.tsx","../src/ui/ChatSession.tsx","../src/cli/exit-codes.ts","../src/index.ts"],"sourcesContent":["import { Command } from \"commander\";\r\nimport { loadConfigMiddleware } from \"./middleware.js\";\r\nimport { customHelp } from \"./help.js\";\r\nimport { renderApp, ChatSession } from \"../ui/index.js\";\r\n\r\nconst SUBCOMMANDS = [\"chat\", \"run\", \"setup\", \"init\", \"completion\"];\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(\"dsk\")\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) => {\r\n const ctx = await loadConfigMiddleware.call(thisCommand);\r\n (thisCommand as unknown as Record<string, unknown>).dskCtx = 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(\"dsk chat 需要交互式终端。如需执行一次性任务,请使用 dsk run。\");\r\n process.exit(1);\r\n }\r\n\r\n const ctx = (this as unknown as Record<string, unknown>).dskCtx as\r\n | { verbose: boolean; config: { providers: unknown[]; tools: unknown[] } }\r\n | undefined;\r\n\r\n const app = renderApp(\r\n <ChatSession\r\n providerCount={ctx?.config.providers.length ?? 1}\r\n toolCount={ctx?.config.tools.length ?? 0}\r\n verbose={ctx?.verbose ?? false}\r\n />,\r\n );\r\n\r\n await app.waitUntilExit;\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(\"dsk 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(\"dsk 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(\"dsk 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 类型:dsk completion bash 或 dsk completion zsh\");\r\n return;\r\n }\r\n if (shell === \"bash\") {\r\n console.log(`# dsk bash 自动补全\r\n_dsk_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 _dsk_completion dsk`);\r\n } else {\r\n console.log(`# dsk zsh 自动补全\r\n_dsk_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 )\r\n _describe 'dsk commands' commands\r\n}\r\ncompdef _dsk_completion dsk`);\r\n }\r\n });\r\n\r\n return program;\r\n}\r\n","import type { Command } from \"commander\";\r\nimport type { Config } from \"../config/index.js\";\r\nimport { loadConfig } from \"../config/index.js\";\r\n\r\n/**\r\n * dsk 运行时上下文。\r\n * 通过 commander 的 preAction hook 注入到每个命令中。\r\n */\r\nexport interface DskContext {\r\n config: Config;\r\n verbose: boolean;\r\n}\r\n\r\n/**\r\n * 在 preAction hook 中加载配置并构造上下文。\r\n */\r\nexport async function loadConfigMiddleware(this: Command): Promise<DskContext> {\r\n const opts = this.optsWithGlobals() as { verbose?: boolean; config?: string };\r\n const verbose = opts.verbose ?? false;\r\n\r\n let config: Config;\r\n try {\r\n config = await loadConfig(opts.config);\r\n } catch {\r\n const { defaultConfig } = await import(\"../config/index.js\");\r\n config = defaultConfig;\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(\"dsk\")} ${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(\" dsk chat\");\r\n lines.push(` ${chalk.dim(\"# 让 AI 执行一个任务\")}`);\r\n lines.push(\" dsk run 修改所有 TODO 注释\");\r\n lines.push(` ${chalk.dim(\"# 运行配置向导\")}`);\r\n lines.push(\" dsk setup\");\r\n lines.push(` ${chalk.dim(\"# 生成 shell 自动补全\")}`);\r\n lines.push(\" dsk completion\");\r\n lines.push(\"\");\r\n\r\n return lines.join(\"\\n\");\r\n}\r\n","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 const { waitUntilExit, clear, unmount } = render(node);\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\nconst CYBER_PALETTE = [\"#00ffff\", \"#ff00ff\", \"#00ff41\", \"#ff1493\", \"#8b00ff\"];\r\n\r\nconst LOGO_LINES = [\r\n \" ██████╗ ███████╗██╗ ██╗\",\r\n \" ██╔══██╗██╔════╝██║ ██╔╝\",\r\n \" ██║ ██║███████╗█████╔╝ \",\r\n \" ██║ ██║╚════██║██╔═██╗ \",\r\n \" ██████╔╝███████║██║ ██╗\",\r\n \" ╚═════╝ ╚══════╝╚═╝ ╚═╝\",\r\n];\r\n\r\nexport function DskSplash() {\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","import { Box, Text } from \"ink\";\r\nimport TextInput from \"ink-text-input\";\r\nimport { useEffect, useState, useCallback } from \"react\";\r\n\r\nconst CYBER_PALETTE = [\"#00ffff\", \"#ff00ff\", \"#00ff41\", \"#ff1493\", \"#8b00ff\"];\r\n\r\nconst LOGO_LINES = [\r\n \" ██████╗ ███████╗██╗ ██╗\",\r\n \" ██╔══██╗██╔════╝██║ ██╔╝\",\r\n \" ██║ ██║███████╗█████╔╝ \",\r\n \" ██║ ██║╚════██║██╔═██╗ \",\r\n \" ██████╔╝███████║██║ ██╗\",\r\n \" ╚═════╝ ╚══════╝╚═╝ ╚═╝\",\r\n];\r\n\r\nconst COMMANDS: Record<string, { desc: string; handler: () => string }> = {\r\n \"/exit\": { desc: \"退出对话\", handler: () => \"\" },\r\n \"/quit\": { desc: \"退出对话\", handler: () => \"\" },\r\n \"/help\": {\r\n desc: \"显示帮助信息\",\r\n handler: () =>\r\n [\r\n \"可用命令:\",\r\n \" /exit, /quit 退出对话\",\r\n \" /help 显示此帮助\",\r\n \" /clear 清空对话历史\",\r\n \" /version 显示版本信息\",\r\n ].join(\"\\n\"),\r\n },\r\n \"/clear\": { desc: \"清空对话历史\", handler: () => \"\" },\r\n \"/version\": { desc: \"显示版本信息\", handler: () => \"dsk v0.0.0\" },\r\n};\r\n\r\ninterface ChatMessage {\r\n role: \"user\" | \"assistant\";\r\n content: string;\r\n}\r\n\r\ninterface ChatSessionProps {\r\n providerCount: number;\r\n toolCount: number;\r\n verbose: boolean;\r\n}\r\n\r\nexport function ChatSession({ providerCount, toolCount, verbose }: ChatSessionProps) {\r\n const [offset, setOffset] = useState(0);\r\n const [messages, setMessages] = useState<ChatMessage[]>([]);\r\n const [input, setInput] = useState(\"\");\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 const handleSubmit = useCallback((value: string) => {\r\n const trimmed = value.trim();\r\n if (!trimmed) return;\r\n\r\n if (trimmed.startsWith(\"/\")) {\r\n const cmd = COMMANDS[trimmed.toLowerCase()];\r\n if (cmd) {\r\n if (trimmed.toLowerCase() === \"/exit\" || trimmed.toLowerCase() === \"/quit\") {\r\n process.exit(0);\r\n return;\r\n }\r\n if (trimmed.toLowerCase() === \"/clear\") {\r\n setMessages([]);\r\n setInput(\"\");\r\n return;\r\n }\r\n const result = cmd.handler();\r\n if (result) {\r\n setMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: result },\r\n ]);\r\n }\r\n setInput(\"\");\r\n return;\r\n }\r\n setMessages((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 setMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: \"dsk AI — 待实现(第07章)。当前为 CLI 框架演示模式。\" },\r\n ]);\r\n setInput(\"\");\r\n }, []);\r\n\r\n return (\r\n <Box flexDirection=\"column\" paddingLeft={1} paddingRight={1}>\r\n {/* Logo + 状态栏 — 左右布局 */}\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\">{\" ✔ \"}已加载 {providerCount} 个 Provider</Text>\r\n <Text color=\"#00ffff\">{\" ℹ \"}已就绪 {toolCount} 个工具</Text>\r\n {verbose ? <Text color=\"#ff1493\">{\" ⚡ Verbose\"}</Text> : null}\r\n </Box>\r\n </Box>\r\n\r\n {/* Messages */}\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n {messages.map((msg, i) => (\r\n <Box key={i} marginTop={1}>\r\n <Box width={8} flexShrink={0}>\r\n <Text bold color={msg.role === \"user\" ? \"#00ff41\" : \"#ff00ff\"}>\r\n {msg.role === \"user\" ? \" 👤\" : \" 🤖\"}\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 </Box>\r\n\r\n {/* Input */}\r\n <Box marginTop={1}>\r\n <Box width={8} flexShrink={0}>\r\n <Text bold color=\"#00ff41\">\r\n {\" ⚡\"}\r\n </Text>\r\n </Box>\r\n <Box flexGrow={1}>\r\n <TextInput\r\n value={input}\r\n onChange={setInput}\r\n onSubmit={handleSubmit}\r\n placeholder=\"输入你的问题...\"\r\n />\r\n </Box>\r\n </Box>\r\n\r\n <Box marginTop={1}>\r\n <Text color=\"#00ffff\" dimColor>\r\n {\" \" + \"─\".repeat(36)}\r\n </Text>\r\n </Box>\r\n </Box>\r\n );\r\n}\r\n","/** dsk 退出码规范 */\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\n\r\nprocess.on(\"SIGINT\", () => {\r\n process.exit(ExitCode.SIGINT);\r\n});\r\n\r\nconst program = createCli();\r\n\r\ntry {\r\n await program.parseAsync(process.argv);\r\n} catch (err: unknown) {\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 process.exit(error.exitCode ?? ExitCode.SUCCESS);\r\n }\r\n\r\n if (typeof error.exitCode === \"number\") {\r\n process.exit(error.exitCode);\r\n }\r\n\r\n console.error(String(err));\r\n process.exit(ExitCode.GENERAL_ERROR);\r\n}\r\n"],"mappings":"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","names":["program","Text","jsxs","Box","Text","jsx","Box","Text","useEffect","useState","jsx","jsxs","jsx","program"]}
1
+ {"version":3,"sources":["../src/cli/index.tsx","../src/cli/middleware.ts","../src/cli/help.ts","../src/ui/RenderScope.tsx","../src/ui/Spinner.tsx","../src/ui/StatusMessage.tsx","../src/ui/DskcodeSplash.tsx","../src/ui/ChatSession.tsx","../src/cli/exit-codes.ts","../src/index.ts"],"sourcesContent":["import { Command } from \"commander\";\r\nimport { loadConfigMiddleware } from \"./middleware.js\";\r\nimport { customHelp } from \"./help.js\";\r\nimport { renderApp, ChatSession } from \"../ui/index.js\";\r\n\r\nconst SUBCOMMANDS = [\"chat\", \"run\", \"setup\", \"init\", \"completion\"];\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) => {\r\n const ctx = await loadConfigMiddleware.call(thisCommand);\r\n (thisCommand 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 const ctx = (this as unknown as Record<string, unknown>).dskcodeCtx as\r\n | { verbose: boolean; config: { providers: unknown[]; tools: unknown[] } }\r\n | undefined;\r\n\r\n const app = renderApp(\r\n <ChatSession\r\n providerCount={ctx?.config.providers.length ?? 1}\r\n toolCount={ctx?.config.tools.length ?? 0}\r\n verbose={ctx?.verbose ?? false}\r\n />,\r\n );\r\n\r\n await app.waitUntilExit;\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 )\r\n _describe 'dskcode commands' commands\r\n}\r\ncompdef _dskcode_completion dskcode`);\r\n }\r\n });\r\n\r\n return program;\r\n}\r\n","import type { Command } from \"commander\";\r\nimport type { Config } from \"../config/index.js\";\r\nimport { loadConfig } 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\nexport async function loadConfigMiddleware(this: Command): Promise<DskcodeContext> {\r\n const opts = this.optsWithGlobals() as { verbose?: boolean; config?: string };\r\n const verbose = opts.verbose ?? false;\r\n\r\n let config: Config;\r\n try {\r\n config = await loadConfig(opts.config);\r\n } catch {\r\n const { defaultConfig } = await import(\"../config/index.js\");\r\n config = defaultConfig;\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(\"\");\r\n\r\n return lines.join(\"\\n\");\r\n}\r\n","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 const { waitUntilExit, clear, unmount } = render(node);\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\nconst CYBER_PALETTE = [\"#00ffff\", \"#ff00ff\", \"#00ff41\", \"#ff1493\", \"#8b00ff\"];\r\n\r\nconst LOGO_LINES = [\r\n \" ██████╗ ███████╗██╗ ██╗\",\r\n \" ██╔══██╗██╔════╝██║ ██╔╝\",\r\n \" ██║ ██║███████╗█████╔╝ \",\r\n \" ██║ ██║╚════██║██╔═██╗ \",\r\n \" ██████╔╝███████║██║ ██╗\",\r\n \" ╚═════╝ ╚══════╝╚═╝ ╚═╝\",\r\n];\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","import { Box, Text } from \"ink\";\r\nimport TextInput from \"ink-text-input\";\r\nimport { useEffect, useState, useCallback } from \"react\";\r\n\r\nconst CYBER_PALETTE = [\"#00ffff\", \"#ff00ff\", \"#00ff41\", \"#ff1493\", \"#8b00ff\"];\r\n\r\nconst LOGO_LINES = [\r\n \" ██████╗ ███████╗██╗ ██╗\",\r\n \" ██╔══██╗██╔════╝██║ ██╔╝\",\r\n \" ██║ ██║███████╗█████╔╝ \",\r\n \" ██║ ██║╚════██║██╔═██╗ \",\r\n \" ██████╔╝███████║██║ ██╗\",\r\n \" ╚═════╝ ╚══════╝╚═╝ ╚═╝\",\r\n];\r\n\r\nconst COMMANDS: Record<string, { desc: string; handler: () => string }> = {\r\n \"/exit\": { desc: \"退出对话\", handler: () => \"\" },\r\n \"/quit\": { desc: \"退出对话\", handler: () => \"\" },\r\n \"/help\": {\r\n desc: \"显示帮助信息\",\r\n handler: () =>\r\n [\r\n \"可用命令:\",\r\n \" /exit, /quit 退出对话\",\r\n \" /help 显示此帮助\",\r\n \" /clear 清空对话历史\",\r\n \" /version 显示版本信息\",\r\n ].join(\"\\n\"),\r\n },\r\n \"/clear\": { desc: \"清空对话历史\", handler: () => \"\" },\r\n \"/version\": { desc: \"显示版本信息\", handler: () => \"dskcode v0.0.0\" },\r\n};\r\n\r\ninterface ChatMessage {\r\n role: \"user\" | \"assistant\";\r\n content: string;\r\n}\r\n\r\ninterface ChatSessionProps {\r\n providerCount: number;\r\n toolCount: number;\r\n verbose: boolean;\r\n}\r\n\r\nexport function ChatSession({ providerCount, toolCount, verbose }: ChatSessionProps) {\r\n const [offset, setOffset] = useState(0);\r\n const [messages, setMessages] = useState<ChatMessage[]>([]);\r\n const [input, setInput] = useState(\"\");\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 const handleSubmit = useCallback((value: string) => {\r\n const trimmed = value.trim();\r\n if (!trimmed) return;\r\n\r\n if (trimmed.startsWith(\"/\")) {\r\n const cmd = COMMANDS[trimmed.toLowerCase()];\r\n if (cmd) {\r\n if (trimmed.toLowerCase() === \"/exit\" || trimmed.toLowerCase() === \"/quit\") {\r\n process.exit(0);\r\n return;\r\n }\r\n if (trimmed.toLowerCase() === \"/clear\") {\r\n setMessages([]);\r\n setInput(\"\");\r\n return;\r\n }\r\n const result = cmd.handler();\r\n if (result) {\r\n setMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: result },\r\n ]);\r\n }\r\n setInput(\"\");\r\n return;\r\n }\r\n setMessages((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 setMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: \"dskcode AI — 待实现(第07章)。当前为 CLI 框架演示模式。\" },\r\n ]);\r\n setInput(\"\");\r\n }, []);\r\n\r\n return (\r\n <Box flexDirection=\"column\" paddingLeft={1} paddingRight={1}>\r\n {/* Logo + 状态栏 — 左右布局 */}\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\">{\" ✔ \"}已加载 {providerCount} 个 Provider</Text>\r\n <Text color=\"#00ffff\">{\" ℹ \"}已就绪 {toolCount} 个工具</Text>\r\n {verbose ? <Text color=\"#ff1493\">{\" ⚡ Verbose\"}</Text> : null}\r\n </Box>\r\n </Box>\r\n\r\n {/* Messages */}\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n {messages.map((msg, i) => (\r\n <Box key={i} marginTop={1}>\r\n <Box width={8} flexShrink={0}>\r\n <Text bold color={msg.role === \"user\" ? \"#00ff41\" : \"#ff00ff\"}>\r\n {msg.role === \"user\" ? \" 👤\" : \" 🤖\"}\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 </Box>\r\n\r\n {/* Input */}\r\n <Box marginTop={1}>\r\n <Box width={8} flexShrink={0}>\r\n <Text bold color=\"#00ff41\">\r\n {\" ⚡\"}\r\n </Text>\r\n </Box>\r\n <Box flexGrow={1}>\r\n <TextInput\r\n value={input}\r\n onChange={setInput}\r\n onSubmit={handleSubmit}\r\n placeholder=\"输入你的问题...\"\r\n />\r\n </Box>\r\n </Box>\r\n\r\n <Box marginTop={1}>\r\n <Text color=\"#00ffff\" dimColor>\r\n {\" \" + \"─\".repeat(36)}\r\n </Text>\r\n </Box>\r\n </Box>\r\n );\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\n\r\nprocess.on(\"SIGINT\", () => {\r\n process.exit(ExitCode.SIGINT);\r\n});\r\n\r\nconst program = createCli();\r\n\r\ntry {\r\n await program.parseAsync(process.argv);\r\n} catch (err: unknown) {\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 process.exit(error.exitCode ?? ExitCode.SUCCESS);\r\n }\r\n\r\n if (typeof error.exitCode === \"number\") {\r\n process.exit(error.exitCode);\r\n }\r\n\r\n console.error(String(err));\r\n process.exit(ExitCode.GENERAL_ERROR);\r\n}\r\n"],"mappings":"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","names":["program","Text","jsxs","Box","Text","jsx","Box","Text","useEffect","useState","jsx","jsxs","jsx","program"]}
package/package.json CHANGED
@@ -1,10 +1,14 @@
1
1
  {
2
2
  "name": "dskcode",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+https://github.com/Awu12277/deepseek-agent-cli/tree/main"
7
+ },
4
8
  "description": "A DeepSeek-native AI coding agent for your terminal",
5
9
  "type": "module",
6
10
  "bin": {
7
- "dsk": "dist/index.js"
11
+ "dskcode": "dist/index.js"
8
12
  },
9
13
  "exports": {
10
14
  ".": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/config/loader.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\r\nimport { join } from \"node:path\";\r\nimport { parse } from \"smol-toml\";\r\nimport type { Config } from \"./types.js\";\r\n\r\n/** 出厂默认配置 — 在找不到配置文件时使用。 */\r\nexport const defaultConfig: Config = {\r\n defaultProvider: \"deepseek\",\r\n providers: [\r\n {\r\n name: \"deepseek\",\r\n baseUrl: \"https://api.deepseek.com\",\r\n model: \"deepseek-chat\",\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};\r\n\r\n/**\r\n * 从 TOML 文件加载配置,未找到时回退到默认值。\r\n *\r\n * 解析顺序(后加载的优先级更高):\r\n * 1. 内置默认值\r\n * 2. 用户全局 ~/.config/dsk.toml\r\n * 3. 项目本地 .dsk.toml(或通过 --config 指定的路径)\r\n */\r\nexport async function loadConfig(configPath?: string): Promise<Config> {\r\n const candidates: string[] = [];\r\n\r\n if (configPath) {\r\n candidates.push(configPath);\r\n } else {\r\n candidates.push(\r\n join(process.env.HOME ?? process.env.USERPROFILE ?? \"~\", \".config\", \"dsk.toml\"),\r\n join(process.cwd(), \".dsk.toml\"),\r\n );\r\n }\r\n\r\n let config: Config = structuredClone(defaultConfig);\r\n\r\n for (const candidate of candidates) {\r\n try {\r\n const raw = await readFile(candidate, \"utf-8\");\r\n const parsed = parse(raw) as unknown as Partial<Config>;\r\n config = mergeConfig(config, parsed);\r\n } catch {\r\n // file doesn't exist or can't be read — skip\r\n }\r\n }\r\n\r\n return config;\r\n}\r\n\r\n/** 将部分配置深度合并到已有配置之上。 */\r\nfunction mergeConfig(base: Config, overlay: Partial<Config>): Config {\r\n return {\r\n ...base,\r\n ...(overlay.defaultProvider !== undefined && { defaultProvider: overlay.defaultProvider }),\r\n ...(overlay.providers !== undefined && { providers: overlay.providers }),\r\n ...(overlay.tools !== undefined && { tools: overlay.tools }),\r\n ...(overlay.plugins !== undefined && { plugins: overlay.plugins }),\r\n };\r\n}\r\n"],"mappings":";AAAA,SAAS,gBAAgB;AACzB,SAAS,YAAY;AACrB,SAAS,aAAa;AAIf,IAAM,gBAAwB;AAAA,EACnC,iBAAiB;AAAA,EACjB,WAAW;AAAA,IACT;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,OAAO;AAAA,IACL,EAAE,MAAM,aAAa,SAAS,KAAK;AAAA,IACnC,EAAE,MAAM,cAAc,SAAS,KAAK;AAAA,IACpC,EAAE,MAAM,aAAa,SAAS,KAAK;AAAA,IACnC,EAAE,MAAM,QAAQ,SAAS,KAAK;AAAA,IAC9B,EAAE,MAAM,QAAQ,SAAS,KAAK;AAAA,IAC9B,EAAE,MAAM,QAAQ,SAAS,KAAK;AAAA,IAC9B,EAAE,MAAM,MAAM,SAAS,KAAK;AAAA,IAC5B,EAAE,MAAM,SAAS,SAAS,KAAK;AAAA,EACjC;AAAA,EACA,SAAS,CAAC;AACZ;AAUA,eAAsB,WAAW,YAAsC;AACrE,QAAM,aAAuB,CAAC;AAE9B,MAAI,YAAY;AACd,eAAW,KAAK,UAAU;AAAA,EAC5B,OAAO;AACL,eAAW;AAAA,MACT,KAAK,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe,KAAK,WAAW,UAAU;AAAA,MAC9E,KAAK,QAAQ,IAAI,GAAG,WAAW;AAAA,IACjC;AAAA,EACF;AAEA,MAAI,SAAiB,gBAAgB,aAAa;AAElD,aAAW,aAAa,YAAY;AAClC,QAAI;AACF,YAAM,MAAM,MAAM,SAAS,WAAW,OAAO;AAC7C,YAAM,SAAS,MAAM,GAAG;AACxB,eAAS,YAAY,QAAQ,MAAM;AAAA,IACrC,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SAAO;AACT;AAGA,SAAS,YAAY,MAAc,SAAkC;AACnE,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAI,QAAQ,oBAAoB,UAAa,EAAE,iBAAiB,QAAQ,gBAAgB;AAAA,IACxF,GAAI,QAAQ,cAAc,UAAa,EAAE,WAAW,QAAQ,UAAU;AAAA,IACtE,GAAI,QAAQ,UAAU,UAAa,EAAE,OAAO,QAAQ,MAAM;AAAA,IAC1D,GAAI,QAAQ,YAAY,UAAa,EAAE,SAAS,QAAQ,QAAQ;AAAA,EAClE;AACF;","names":[]}