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 +121 -271
- package/dist/{chunk-URZUL36S.js → chunk-GMSRSNWA.js} +3 -3
- package/dist/chunk-GMSRSNWA.js.map +1 -0
- package/dist/{config-57HYL75X.js → config-42MSCTVE.js} +2 -2
- package/dist/index.js +25 -25
- package/dist/index.js.map +1 -1
- package/package.json +6 -2
- package/dist/chunk-URZUL36S.js.map +0 -1
- /package/dist/{config-57HYL75X.js.map → config-42MSCTVE.js.map} +0 -0
package/README.md
CHANGED
|
@@ -1,282 +1,132 @@
|
|
|
1
|
-
#
|
|
1
|
+
# dskcode
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> 基于 DeepSeek 的 AI 编程助手终端 CLI 工具。让 AI 直接在终端中理解你的代码、读写文件、执行命令。
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/dskcode)
|
|
6
|
+
[](https://nodejs.org)
|
|
7
|
+
[](LICENSE)
|
|
4
8
|
|
|
5
9
|
---
|
|
6
10
|
|
|
7
|
-
##
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
29
|
+
# 查看帮助
|
|
30
|
+
dskcode --help
|
|
44
31
|
|
|
45
|
-
|
|
32
|
+
# 启动交互式对话
|
|
33
|
+
dskcode chat
|
|
34
|
+
```
|
|
46
35
|
|
|
47
|
-
|
|
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", "
|
|
33
|
-
join(process.cwd(), ".
|
|
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-
|
|
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":[]}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
loadConfig
|
|
4
|
-
} from "./chunk-
|
|
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-
|
|
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("
|
|
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("
|
|
62
|
+
lines.push(" dskcode chat");
|
|
63
63
|
lines.push(` ${chalk.dim("# \u8BA9 AI \u6267\u884C\u4E00\u4E2A\u4EFB\u52A1")}`);
|
|
64
|
-
lines.push("
|
|
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("
|
|
66
|
+
lines.push(" dskcode setup");
|
|
67
67
|
lines.push(` ${chalk.dim("# \u751F\u6210 shell \u81EA\u52A8\u8865\u5168")}`);
|
|
68
|
-
lines.push("
|
|
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/
|
|
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: () => "
|
|
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: "
|
|
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("
|
|
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.
|
|
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("
|
|
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.
|
|
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("
|
|
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("
|
|
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("
|
|
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\
|
|
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(`#
|
|
263
|
-
|
|
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
|
|
271
|
+
complete -F _dskcode_completion dskcode`);
|
|
272
272
|
} else {
|
|
273
|
-
console.log(`#
|
|
274
|
-
|
|
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 '
|
|
283
|
+
_describe 'dskcode commands' commands
|
|
284
284
|
}
|
|
285
|
-
compdef
|
|
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":";;;;;;AAAA,SAAS,eAAe;;;ACgBxB,eAAsB,uBAAyD;AAC7E,QAAM,OAAO,KAAK,gBAAgB;AAClC,QAAM,UAAU,KAAK,WAAW;AAEhC,MAAI;AACJ,MAAI;AACF,aAAS,MAAM,WAAW,KAAK,MAAM;AAAA,EACvC,QAAQ;AACN,UAAM,EAAE,cAAc,IAAI,MAAM,OAAO,sBAAoB;AAC3D,aAAS;AAAA,EACX;AAEA,SAAO,EAAE,QAAQ,QAAQ;AAC3B;;;AC5BA,OAAO,WAAW;AAEX,SAAS,WAAWA,UAA0B;AACnD,QAAM,QAAkB,CAAC;AAEzB,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,MAAM,KAAK,eAAK,CAAC;AAC5B,QAAM,KAAK,KAAK,MAAM,KAAK,KAAK,CAAC,IAAI,MAAM,IAAI,kBAAkB,CAAC,IAAI,MAAM,MAAM,WAAW,CAAC,IAAI,MAAM,IAAI,WAAW,CAAC,EAAE;AAC1H,QAAM,KAAK,EAAE;AAEb,QAAM,aAAaA,SAAQ,QAAQ;AAAA,IACjC,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,SAAS,eAAe,EAAE,SAAS;AAAA,EACrE;AACA,MAAI,WAAW,SAAS,GAAG;AACzB,UAAM,KAAK,MAAM,KAAK,2BAAO,CAAC;AAC9B,eAAW,OAAO,YAAY;AAC5B,YAAM,QAAQ,CAAC,IAAI,OAAO,IAAI,IAAI,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;AAC7D,YAAM,KAAK,KAAK,MAAM,KAAK,MAAM,OAAO,EAAE,CAAC,CAAC,IAAI,IAAI,eAAe,EAAE,EAAE;AAAA,IACzE;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,QAAM,KAAK,MAAM,KAAK,2BAAO,CAAC;AAC9B,aAAW,QAAQ,CAAC,cAAc,eAAe,GAAG;AAClD,UAAM,MAAMA,SAAQ,QAAQ;AAAA,MAC1B,CAAC,MAAM,EAAE,UAAU,KAAK,SAAS,MAAM,IAAI,WAAW;AAAA,IACxD;AACA,QAAI,KAAK;AACP,YAAM,KAAK,KAAK,MAAM,KAAK,KAAK,OAAO,EAAE,CAAC,CAAC,IAAI,IAAI,eAAe,EAAE,EAAE;AAAA,IACxE;AAAA,EACF;AACA,QAAM,KAAK,EAAE;AAEb,QAAM,OAAOA,SAAQ,SAAS,OAAO,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,WAAW,MAAM,CAAC;AACxE,MAAI,KAAK,SAAS,GAAG;AACnB,UAAM,KAAK,MAAM,KAAK,eAAK,CAAC;AAC5B,eAAW,OAAO,MAAM;AACtB,YAAM,KAAK,KAAK,MAAM,MAAM,IAAI,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,IAAI,YAAY,CAAC,EAAE;AAAA,IAC3E;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,QAAM,KAAK,MAAM,KAAK,eAAK,CAAC;AAC5B,QAAM,KAAK,KAAK,MAAM,IAAI,8CAAW,CAAC,EAAE;AACxC,QAAM,KAAK,YAAY;AACvB,QAAM,KAAK,KAAK,MAAM,IAAI,kDAAe,CAAC,EAAE;AAC5C,QAAM,KAAK,sDAAwB;AACnC,QAAM,KAAK,KAAK,MAAM,IAAI,wCAAU,CAAC,EAAE;AACvC,QAAM,KAAK,aAAa;AACxB,QAAM,KAAK,KAAK,MAAM,IAAI,+CAAiB,CAAC,EAAE;AAC9C,QAAM,KAAK,kBAAkB;AAC7B,QAAM,KAAK,EAAE;AAEb,SAAO,MAAM,KAAK,IAAI;AACxB;;;ACvDA,SAAS,cAAc;AAShB,SAAS,UAAU,MAAoC;AAC5D,QAAM,EAAE,eAAe,OAAO,QAAQ,IAAI,OAAO,IAAI;AACrD,SAAO,EAAE,eAAe,cAAc,GAAG,OAAO,QAAQ;AAC1D;;;ACZA,SAAS,YAAY;AACrB,OAAO,gBAAgB;AAWf,cAEO,YAFP;;;ACZR,SAAS,KAAK,QAAAC,aAAY;AAqBpB,iBAAAC,aAAA;;;ACrBN,SAAS,OAAAC,MAAK,QAAAC,aAAY;AAC1B,SAAS,WAAW,gBAAgB;AA6BxB,gBAAAC,YAAA;;;AC9BZ,SAAS,OAAAC,MAAK,QAAAC,aAAY;AAC1B,OAAO,eAAe;AACtB,SAAS,aAAAC,YAAW,YAAAC,WAAU,mBAAmB;AA4GjC,gBAAAC,MAUN,QAAAC,aAVM;AA1GhB,IAAM,gBAAgB,CAAC,WAAW,WAAW,WAAW,WAAW,SAAS;AAE5E,IAAM,aAAa;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,WAAoE;AAAA,EACxE,SAAS,EAAE,MAAM,4BAAQ,SAAS,MAAM,GAAG;AAAA,EAC3C,SAAS,EAAE,MAAM,4BAAQ,SAAS,MAAM,GAAG;AAAA,EAC3C,SAAS;AAAA,IACP,MAAM;AAAA,IACN,SAAS,MACP;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI;AAAA,EACf;AAAA,EACA,UAAU,EAAE,MAAM,wCAAU,SAAS,MAAM,GAAG;AAAA,EAC9C,YAAY,EAAE,MAAM,wCAAU,SAAS,MAAM,aAAa;AAC5D;AAaO,SAAS,YAAY,EAAE,eAAe,WAAW,QAAQ,GAAqB;AACnF,QAAM,CAAC,QAAQ,SAAS,IAAIF,UAAS,CAAC;AACtC,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAwB,CAAC,CAAC;AAC1D,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAS,EAAE;AAErC,EAAAD,WAAU,MAAM;AACd,UAAM,QAAQ,YAAY,MAAM;AAC9B,gBAAU,CAAC,UAAU,OAAO,KAAK,cAAc,MAAM;AAAA,IACvD,GAAG,GAAG;AACN,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,GAAG,CAAC,CAAC;AAEL,QAAM,eAAe,YAAY,CAAC,UAAkB;AAClD,UAAM,UAAU,MAAM,KAAK;AAC3B,QAAI,CAAC,QAAS;AAEd,QAAI,QAAQ,WAAW,GAAG,GAAG;AAC3B,YAAM,MAAM,SAAS,QAAQ,YAAY,CAAC;AAC1C,UAAI,KAAK;AACP,YAAI,QAAQ,YAAY,MAAM,WAAW,QAAQ,YAAY,MAAM,SAAS;AAC1E,kBAAQ,KAAK,CAAC;AACd;AAAA,QACF;AACA,YAAI,QAAQ,YAAY,MAAM,UAAU;AACtC,sBAAY,CAAC,CAAC;AACd,mBAAS,EAAE;AACX;AAAA,QACF;AACA,cAAM,SAAS,IAAI,QAAQ;AAC3B,YAAI,QAAQ;AACV,sBAAY,CAAC,SAAS;AAAA,YACpB,GAAG;AAAA,YACH,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,YACjC,EAAE,MAAM,aAAa,SAAS,OAAO;AAAA,UACvC,CAAC;AAAA,QACH;AACA,iBAAS,EAAE;AACX;AAAA,MACF;AACA,kBAAY,CAAC,SAAS;AAAA,QACpB,GAAG;AAAA,QACH,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,QACjC,EAAE,MAAM,aAAa,SAAS,iCAAQ,OAAO,8CAAgB;AAAA,MAC/D,CAAC;AACD,eAAS,EAAE;AACX;AAAA,IACF;AAEA,gBAAY,CAAC,SAAS;AAAA,MACpB,GAAG;AAAA,MACH,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,MACjC,EAAE,MAAM,aAAa,SAAS,oIAAqC;AAAA,IACrE,CAAC;AACD,aAAS,EAAE;AAAA,EACb,GAAG,CAAC,CAAC;AAEL,SACE,gBAAAG,MAACL,MAAA,EAAI,eAAc,UAAS,aAAa,GAAG,cAAc,GAExD;AAAA,oBAAAK,MAACL,MAAA,EAAI,eAAc,OAAM,cAAc,GAErC;AAAA,sBAAAI,KAACJ,MAAA,EAAI,eAAc,UAAS,aAAa,GACtC,qBAAW,IAAI,CAAC,MAAM,MAAM;AAC3B,cAAM,cAAc,IAAI,UAAU,cAAc;AAChD,eACE,gBAAAI,KAACJ,MAAA,EACC,0BAAAI,KAACH,OAAA,EAAK,MAAI,MAAC,OAAO,cAAc,UAAU,GACvC,gBACH,KAHQ,CAIV;AAAA,MAEJ,CAAC,GACH;AAAA,MAGA,gBAAAI,MAACL,MAAA,EAAI,eAAc,UAAS,gBAAe,UACzC;AAAA,wBAAAK,MAACJ,OAAA,EAAK,OAAM,WAAW;AAAA;AAAA,UAAO;AAAA,UAAK;AAAA,UAAc;AAAA,WAAW;AAAA,QAC5D,gBAAAI,MAACJ,OAAA,EAAK,OAAM,WAAW;AAAA;AAAA,UAAO;AAAA,UAAK;AAAA,UAAU;AAAA,WAAI;AAAA,QAChD,UAAU,gBAAAG,KAACH,OAAA,EAAK,OAAM,WAAW,8BAAc,IAAU;AAAA,SAC5D;AAAA,OACF;AAAA,IAGA,gBAAAG,KAACJ,MAAA,EAAI,eAAc,UAAS,WAAW,GACpC,mBAAS,IAAI,CAAC,KAAK,MAClB,gBAAAK,MAACL,MAAA,EAAY,WAAW,GACtB;AAAA,sBAAAI,KAACJ,MAAA,EAAI,OAAO,GAAG,YAAY,GACzB,0BAAAI,KAACH,OAAA,EAAK,MAAI,MAAC,OAAO,IAAI,SAAS,SAAS,YAAY,WACjD,cAAI,SAAS,SAAS,gBAAS,eAClC,GACF;AAAA,MACA,gBAAAG,KAACJ,MAAA,EAAI,UAAU,GACb,0BAAAI,KAACH,OAAA,EAAK,MAAK,QAAQ,cAAI,SAAQ,GACjC;AAAA,SARQ,CASV,CACD,GACH;AAAA,IAGA,gBAAAI,MAACL,MAAA,EAAI,WAAW,GACd;AAAA,sBAAAI,KAACJ,MAAA,EAAI,OAAO,GAAG,YAAY,GACzB,0BAAAI,KAACH,OAAA,EAAK,MAAI,MAAC,OAAM,WACd,sBACH,GACF;AAAA,MACA,gBAAAG,KAACJ,MAAA,EAAI,UAAU,GACb,0BAAAI;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,UACP,UAAU;AAAA,UACV,UAAU;AAAA,UACV,aAAY;AAAA;AAAA,MACd,GACF;AAAA,OACF;AAAA,IAEA,gBAAAA,KAACJ,MAAA,EAAI,WAAW,GACd,0BAAAI,KAACH,OAAA,EAAK,OAAM,WAAU,UAAQ,MAC3B,iBAAO,SAAI,OAAO,EAAE,GACvB,GACF;AAAA,KACF;AAEJ;;;AP9HQ,gBAAAK,YAAA;AAnCR,IAAM,cAAc,CAAC,QAAQ,OAAO,SAAS,QAAQ,YAAY;AAE1D,SAAS,YAAqB;AACnC,QAAMC,WAAU,IAAI,QAAQ;AAC5B,EAAAA,SAAQ,aAAa;AAErB,EAAAA,SACG,KAAK,KAAK,EACV,YAAY,kFAA2B,EACvC,QAAQ,SAAS,iBAAiB,gCAAO,EACzC,OAAO,aAAa,kDAAU,EAC9B,OAAO,mBAAmB,kDAAU;AAEvC,EAAAA,SAAQ,kBAAkB,MAAM,WAAWA,QAAO;AAElD,EAAAA,SAAQ,KAAK,aAAa,OAAO,gBAAgB;AAC/C,UAAM,MAAM,MAAM,qBAAqB,KAAK,WAAW;AACvD,IAAC,YAAmD,SAAS;AAAA,EAC/D,CAAC;AAGD,EAAAA,SACG,QAAQ,MAAM,EACd,YAAY,wDAAW,EACvB,OAAO,iBAAkB;AACxB,QAAI,CAAC,QAAQ,MAAM,OAAO;AACxB,cAAQ,MAAM,uJAAyC;AACvD,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,MAAO,KAA4C;AAIzD,UAAM,MAAM;AAAA,MACV,gBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,eAAe,KAAK,OAAO,UAAU,UAAU;AAAA,UAC/C,WAAW,KAAK,OAAO,MAAM,UAAU;AAAA,UACvC,SAAS,KAAK,WAAW;AAAA;AAAA,MAC3B;AAAA,IACF;AAEA,UAAM,IAAI;AAAA,EACZ,CAAC;AAGH,EAAAC,SACG,QAAQ,KAAK,EACb,YAAY,4CAAS,EACrB,SAAS,eAAe,0BAAM,EAC9B,OAAO,kBAAkB,4CAAS,EAClC,OAAO,eAAgB,SAAmB;AACzC,YAAQ,IAAI,6DAAqB;AAAA,EACnC,CAAC;AAGH,EAAAA,SACG,QAAQ,OAAO,EACf,YAAY,sCAAQ,EACpB,OAAO,YAAY,kDAAe,EAClC,OAAO,UAAU,yCAAgB,EACjC,OAAO,iBAAkB;AACxB,YAAQ,IAAI,+DAAuB;AAAA,EACrC,CAAC;AAGH,EAAAA,SACG,QAAQ,MAAM,EACd,YAAY,2GAA2B,EACvC,OAAO,iBAAkB;AACxB,YAAQ,IAAI,8DAAsB;AAAA,EACpC,CAAC;AAGH,EAAAA,SACG,QAAQ,YAAY,EACpB,YAAY,yFAA6B,EACzC,SAAS,WAAW,sBAAY,eAAe,EAC/C,OAAO,eAAgB,OAAgB;AACtC,QAAI,CAAC,OAAO;AACV,cAAQ,IAAI,0FAAuD;AACnE;AAAA,IACF;AACA,QAAI,UAAU,QAAQ;AACpB,cAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,gCAIY,YAAY,KAAK,GAAG,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,gCAKrB;AAAA,IAC1B,OAAO;AACL,cAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAYQ;AAAA,IACtB;AAAA,EACF,CAAC;AAEH,SAAOA;AACT;;;AQpHO,IAAM,WAAW;AAAA;AAAA,EAEtB,SAAS;AAAA;AAAA,EAET,eAAe;AAAA;AAAA,EAEf,cAAc;AAAA;AAAA,EAEd,QAAQ;AACV;;;ACLA,QAAQ,GAAG,UAAU,MAAM;AACzB,UAAQ,KAAK,SAAS,MAAM;AAC9B,CAAC;AAED,IAAM,UAAU,UAAU;AAE1B,IAAI;AACF,QAAM,QAAQ,WAAW,QAAQ,IAAI;AACvC,SAAS,KAAc;AACrB,QAAM,QAAQ;AAEd,MAAI,MAAM,SAAS,6BAA6B,MAAM,SAAS,qBAAqB;AAClF,YAAQ,KAAK,MAAM,YAAY,SAAS,OAAO;AAAA,EACjD;AAEA,MAAI,OAAO,MAAM,aAAa,UAAU;AACtC,YAAQ,KAAK,MAAM,QAAQ;AAAA,EAC7B;AAEA,UAAQ,MAAM,OAAO,GAAG,CAAC;AACzB,UAAQ,KAAK,SAAS,aAAa;AACrC;","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.
|
|
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
|
-
"
|
|
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":[]}
|
|
File without changes
|