dskcode 0.1.0

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 ADDED
@@ -0,0 +1,282 @@
1
+ # 从零开发 TypeScript 版 Agent CLI 系列博客
2
+
3
+ > 本系列基于 [Reasonix](https://github.com/esengine/DeepSeek-Reasonix) 的架构设计,从零开始用 TypeScript 构建一个生产级的 AI 编程助手 CLI 工具。贯穿单体 CLI 设计、LLM 集成、插件系统等核心主题。
4
+
5
+ ---
6
+
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、跨平台 |
26
+
27
+ ---
28
+
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` 字段
42
+
43
+ ## 02 CLI 框架搭建与子命令路由
44
+
45
+ 设计 CLI 的外壳:命令解析、全局选项、子命令分发。
46
+
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 + 预发布标签
278
+
279
+ ---
280
+
281
+ > **下篇预告**:各章节完成时将附带完整的代码仓库链接和可运行示例。
282
+ > 欢迎 Star & PR!
@@ -0,0 +1,61 @@
1
+ // src/config/loader.ts
2
+ import { readFile } from "fs/promises";
3
+ import { join } from "path";
4
+ import { parse } from "smol-toml";
5
+ var defaultConfig = {
6
+ defaultProvider: "deepseek",
7
+ providers: [
8
+ {
9
+ name: "deepseek",
10
+ baseUrl: "https://api.deepseek.com",
11
+ model: "deepseek-chat"
12
+ }
13
+ ],
14
+ tools: [
15
+ { name: "read_file", enabled: true },
16
+ { name: "write_file", enabled: true },
17
+ { name: "edit_file", enabled: true },
18
+ { name: "bash", enabled: true },
19
+ { name: "glob", enabled: true },
20
+ { name: "grep", enabled: true },
21
+ { name: "ls", enabled: true },
22
+ { name: "fetch", enabled: true }
23
+ ],
24
+ plugins: []
25
+ };
26
+ async function loadConfig(configPath) {
27
+ const candidates = [];
28
+ if (configPath) {
29
+ candidates.push(configPath);
30
+ } else {
31
+ candidates.push(
32
+ join(process.env.HOME ?? process.env.USERPROFILE ?? "~", ".config", "dsk.toml"),
33
+ join(process.cwd(), ".dsk.toml")
34
+ );
35
+ }
36
+ let config = structuredClone(defaultConfig);
37
+ for (const candidate of candidates) {
38
+ try {
39
+ const raw = await readFile(candidate, "utf-8");
40
+ const parsed = parse(raw);
41
+ config = mergeConfig(config, parsed);
42
+ } catch {
43
+ }
44
+ }
45
+ return config;
46
+ }
47
+ function mergeConfig(base, overlay) {
48
+ return {
49
+ ...base,
50
+ ...overlay.defaultProvider !== void 0 && { defaultProvider: overlay.defaultProvider },
51
+ ...overlay.providers !== void 0 && { providers: overlay.providers },
52
+ ...overlay.tools !== void 0 && { tools: overlay.tools },
53
+ ...overlay.plugins !== void 0 && { plugins: overlay.plugins }
54
+ };
55
+ }
56
+
57
+ export {
58
+ defaultConfig,
59
+ loadConfig
60
+ };
61
+ //# sourceMappingURL=chunk-URZUL36S.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/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":[]}
@@ -0,0 +1,9 @@
1
+ import {
2
+ defaultConfig,
3
+ loadConfig
4
+ } from "./chunk-URZUL36S.js";
5
+ export {
6
+ defaultConfig,
7
+ loadConfig
8
+ };
9
+ //# sourceMappingURL=config-57HYL75X.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/index.js ADDED
@@ -0,0 +1,321 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ loadConfig
4
+ } from "./chunk-URZUL36S.js";
5
+
6
+ // src/cli/index.tsx
7
+ import { Command } from "commander";
8
+
9
+ // src/cli/middleware.ts
10
+ async function loadConfigMiddleware() {
11
+ const opts = this.optsWithGlobals();
12
+ const verbose = opts.verbose ?? false;
13
+ let config;
14
+ try {
15
+ config = await loadConfig(opts.config);
16
+ } catch {
17
+ const { defaultConfig } = await import("./config-57HYL75X.js");
18
+ config = defaultConfig;
19
+ }
20
+ return { config, verbose };
21
+ }
22
+
23
+ // src/cli/help.ts
24
+ import chalk from "chalk";
25
+ function customHelp(program2) {
26
+ const lines = [];
27
+ lines.push("");
28
+ lines.push(chalk.bold("\u7528\u6CD5:"));
29
+ lines.push(` ${chalk.cyan("dsk")} ${chalk.dim("[global-options]")} ${chalk.green("<command>")} ${chalk.dim("[options]")}`);
30
+ lines.push("");
31
+ const globalOpts = program2.options.filter(
32
+ (o) => o.long !== "--help" && o.long !== "--version" && o.long !== "--config"
33
+ );
34
+ if (globalOpts.length > 0) {
35
+ lines.push(chalk.bold("\u5168\u5C40\u9009\u9879:"));
36
+ for (const opt of globalOpts) {
37
+ const flags = [opt.short, opt.long].filter(Boolean).join(", ");
38
+ lines.push(` ${chalk.cyan(flags.padEnd(24))} ${opt.description ?? ""}`);
39
+ }
40
+ lines.push("");
41
+ }
42
+ lines.push(chalk.bold("\u5185\u7F6E\u9009\u9879:"));
43
+ for (const flag of ["-h, --help", "-V, --version"]) {
44
+ const opt = program2.options.find(
45
+ (o) => o.long === (flag.includes("help") ? "--help" : "--version")
46
+ );
47
+ if (opt) {
48
+ lines.push(` ${chalk.cyan(flag.padEnd(24))} ${opt.description ?? ""}`);
49
+ }
50
+ }
51
+ lines.push("");
52
+ const cmds = program2.commands.filter((c) => !c.name().startsWith("help"));
53
+ if (cmds.length > 0) {
54
+ lines.push(chalk.bold("\u547D\u4EE4:"));
55
+ for (const cmd of cmds) {
56
+ lines.push(` ${chalk.green(cmd.name().padEnd(24))} ${cmd.description()}`);
57
+ }
58
+ lines.push("");
59
+ }
60
+ lines.push(chalk.bold("\u793A\u4F8B:"));
61
+ lines.push(` ${chalk.dim("# \u542F\u52A8\u4EA4\u4E92\u5F0F\u5BF9\u8BDD")}`);
62
+ lines.push(" dsk chat");
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");
65
+ lines.push(` ${chalk.dim("# \u8FD0\u884C\u914D\u7F6E\u5411\u5BFC")}`);
66
+ lines.push(" dsk setup");
67
+ lines.push(` ${chalk.dim("# \u751F\u6210 shell \u81EA\u52A8\u8865\u5168")}`);
68
+ lines.push(" dsk completion");
69
+ lines.push("");
70
+ return lines.join("\n");
71
+ }
72
+
73
+ // src/ui/RenderScope.tsx
74
+ import { render } from "ink";
75
+ function renderApp(node) {
76
+ const { waitUntilExit, clear, unmount } = render(node);
77
+ return { waitUntilExit: waitUntilExit(), clear, unmount };
78
+ }
79
+
80
+ // src/ui/Spinner.tsx
81
+ import { Text } from "ink";
82
+ import InkSpinner from "ink-spinner";
83
+ import { jsx, jsxs } from "react/jsx-runtime";
84
+
85
+ // src/ui/StatusMessage.tsx
86
+ import { Box, Text as Text2 } from "ink";
87
+ import { jsxs as jsxs2 } from "react/jsx-runtime";
88
+
89
+ // src/ui/DskSplash.tsx
90
+ import { Box as Box2, Text as Text3 } from "ink";
91
+ import { useEffect, useState } from "react";
92
+ import { jsx as jsx2 } from "react/jsx-runtime";
93
+
94
+ // src/ui/ChatSession.tsx
95
+ import { Box as Box3, Text as Text4 } from "ink";
96
+ import TextInput from "ink-text-input";
97
+ import { useEffect as useEffect2, useState as useState2, useCallback } from "react";
98
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
99
+ var CYBER_PALETTE = ["#00ffff", "#ff00ff", "#00ff41", "#ff1493", "#8b00ff"];
100
+ var LOGO_LINES = [
101
+ " \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557",
102
+ " \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2554\u255D",
103
+ " \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2554\u255D ",
104
+ " \u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2588\u2588\u2557 ",
105
+ " \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2557",
106
+ " \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D"
107
+ ];
108
+ var COMMANDS = {
109
+ "/exit": { desc: "\u9000\u51FA\u5BF9\u8BDD", handler: () => "" },
110
+ "/quit": { desc: "\u9000\u51FA\u5BF9\u8BDD", handler: () => "" },
111
+ "/help": {
112
+ desc: "\u663E\u793A\u5E2E\u52A9\u4FE1\u606F",
113
+ handler: () => [
114
+ "\u53EF\u7528\u547D\u4EE4\uFF1A",
115
+ " /exit, /quit \u9000\u51FA\u5BF9\u8BDD",
116
+ " /help \u663E\u793A\u6B64\u5E2E\u52A9",
117
+ " /clear \u6E05\u7A7A\u5BF9\u8BDD\u5386\u53F2",
118
+ " /version \u663E\u793A\u7248\u672C\u4FE1\u606F"
119
+ ].join("\n")
120
+ },
121
+ "/clear": { desc: "\u6E05\u7A7A\u5BF9\u8BDD\u5386\u53F2", handler: () => "" },
122
+ "/version": { desc: "\u663E\u793A\u7248\u672C\u4FE1\u606F", handler: () => "dsk v0.0.0" }
123
+ };
124
+ function ChatSession({ providerCount, toolCount, verbose }) {
125
+ const [offset, setOffset] = useState2(0);
126
+ const [messages, setMessages] = useState2([]);
127
+ const [input, setInput] = useState2("");
128
+ useEffect2(() => {
129
+ const timer = setInterval(() => {
130
+ setOffset((prev) => (prev + 1) % CYBER_PALETTE.length);
131
+ }, 500);
132
+ return () => clearInterval(timer);
133
+ }, []);
134
+ const handleSubmit = useCallback((value) => {
135
+ const trimmed = value.trim();
136
+ if (!trimmed) return;
137
+ if (trimmed.startsWith("/")) {
138
+ const cmd = COMMANDS[trimmed.toLowerCase()];
139
+ if (cmd) {
140
+ if (trimmed.toLowerCase() === "/exit" || trimmed.toLowerCase() === "/quit") {
141
+ process.exit(0);
142
+ return;
143
+ }
144
+ if (trimmed.toLowerCase() === "/clear") {
145
+ setMessages([]);
146
+ setInput("");
147
+ return;
148
+ }
149
+ const result = cmd.handler();
150
+ if (result) {
151
+ setMessages((prev) => [
152
+ ...prev,
153
+ { role: "user", content: trimmed },
154
+ { role: "assistant", content: result }
155
+ ]);
156
+ }
157
+ setInput("");
158
+ return;
159
+ }
160
+ setMessages((prev) => [
161
+ ...prev,
162
+ { role: "user", content: trimmed },
163
+ { role: "assistant", content: `\u672A\u77E5\u547D\u4EE4\uFF1A${trimmed}\u3002\u8F93\u5165 /help \u67E5\u770B\u3002` }
164
+ ]);
165
+ setInput("");
166
+ return;
167
+ }
168
+ setMessages((prev) => [
169
+ ...prev,
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" }
172
+ ]);
173
+ setInput("");
174
+ }, []);
175
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [
176
+ /* @__PURE__ */ jsxs3(Box3, { flexDirection: "row", marginBottom: 1, children: [
177
+ /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", marginRight: 4, children: LOGO_LINES.map((line, i) => {
178
+ const colorIndex = (i + offset) % CYBER_PALETTE.length;
179
+ return /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Text4, { bold: true, color: CYBER_PALETTE[colorIndex], children: line }) }, i);
180
+ }) }),
181
+ /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", justifyContent: "center", children: [
182
+ /* @__PURE__ */ jsxs3(Text4, { color: "#00ff41", children: [
183
+ " \u2714 ",
184
+ "\u5DF2\u52A0\u8F7D ",
185
+ providerCount,
186
+ " \u4E2A Provider"
187
+ ] }),
188
+ /* @__PURE__ */ jsxs3(Text4, { color: "#00ffff", children: [
189
+ " \u2139 ",
190
+ "\u5DF2\u5C31\u7EEA ",
191
+ toolCount,
192
+ " \u4E2A\u5DE5\u5177"
193
+ ] }),
194
+ verbose ? /* @__PURE__ */ jsx3(Text4, { color: "#ff1493", children: " \u26A1 Verbose" }) : null
195
+ ] })
196
+ ] }),
197
+ /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", marginTop: 1, children: messages.map((msg, i) => /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, children: [
198
+ /* @__PURE__ */ jsx3(Box3, { width: 8, flexShrink: 0, children: /* @__PURE__ */ jsx3(Text4, { bold: true, color: msg.role === "user" ? "#00ff41" : "#ff00ff", children: msg.role === "user" ? " \u{1F464}" : " \u{1F916}" }) }),
199
+ /* @__PURE__ */ jsx3(Box3, { flexGrow: 1, children: /* @__PURE__ */ jsx3(Text4, { wrap: "wrap", children: msg.content }) })
200
+ ] }, i)) }),
201
+ /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, children: [
202
+ /* @__PURE__ */ jsx3(Box3, { width: 8, flexShrink: 0, children: /* @__PURE__ */ jsx3(Text4, { bold: true, color: "#00ff41", children: " \u26A1" }) }),
203
+ /* @__PURE__ */ jsx3(Box3, { flexGrow: 1, children: /* @__PURE__ */ jsx3(
204
+ TextInput,
205
+ {
206
+ value: input,
207
+ onChange: setInput,
208
+ onSubmit: handleSubmit,
209
+ placeholder: "\u8F93\u5165\u4F60\u7684\u95EE\u9898..."
210
+ }
211
+ ) })
212
+ ] }),
213
+ /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text4, { color: "#00ffff", dimColor: true, children: " " + "\u2500".repeat(36) }) })
214
+ ] });
215
+ }
216
+
217
+ // src/cli/index.tsx
218
+ import { jsx as jsx4 } from "react/jsx-runtime";
219
+ var SUBCOMMANDS = ["chat", "run", "setup", "init", "completion"];
220
+ function createCli() {
221
+ const program2 = new Command();
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");
224
+ program2.helpInformation = () => customHelp(program2);
225
+ program2.hook("preAction", async (thisCommand) => {
226
+ const ctx = await loadConfigMiddleware.call(thisCommand);
227
+ thisCommand.dskCtx = ctx;
228
+ });
229
+ program2.command("chat").description("\u542F\u52A8\u4EA4\u4E92\u5F0F\u5BF9\u8BDD\u4F1A\u8BDD").action(async function() {
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");
232
+ process.exit(1);
233
+ }
234
+ const ctx = this.dskCtx;
235
+ const app = renderApp(
236
+ /* @__PURE__ */ jsx4(
237
+ ChatSession,
238
+ {
239
+ providerCount: ctx?.config.providers.length ?? 1,
240
+ toolCount: ctx?.config.tools.length ?? 0,
241
+ verbose: ctx?.verbose ?? false
242
+ }
243
+ )
244
+ );
245
+ await app.waitUntilExit;
246
+ });
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");
249
+ });
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");
252
+ });
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");
255
+ });
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
+ if (!shell) {
258
+ console.log("\u8BF7\u6307\u5B9A shell \u7C7B\u578B\uFF1Adsk completion bash \u6216 dsk completion zsh");
259
+ return;
260
+ }
261
+ if (shell === "bash") {
262
+ console.log(`# dsk bash \u81EA\u52A8\u8865\u5168
263
+ _dsk_completion() {
264
+ local cur=\${COMP_WORDS[COMP_CWORD]}
265
+ if [[ \${COMP_CWORD} -eq 1 ]]; then
266
+ COMPREPLY=( $(compgen -W "${SUBCOMMANDS.join(" ")}" -- "\${cur}") )
267
+ return 0
268
+ fi
269
+ COMPREPLY=( $(compgen -W "--verbose --config --model" -- "\${cur}") )
270
+ }
271
+ complete -F _dsk_completion dsk`);
272
+ } else {
273
+ console.log(`# dsk zsh \u81EA\u52A8\u8865\u5168
274
+ _dsk_completion() {
275
+ local -a commands
276
+ commands=(
277
+ "chat:\u542F\u52A8\u4EA4\u4E92\u5F0F\u5BF9\u8BDD\u4F1A\u8BDD"
278
+ "run:\u6267\u884C\u4E00\u6B21\u6027\u4EFB\u52A1"
279
+ "setup:\u8FD0\u884C\u914D\u7F6E\u5411\u5BFC"
280
+ "init:\u751F\u6210\u9879\u76EE\u8BB0\u5FC6\u6587\u4EF6"
281
+ "completion:\u8F93\u51FA shell \u81EA\u52A8\u8865\u5168\u8BF4\u660E"
282
+ )
283
+ _describe 'dsk commands' commands
284
+ }
285
+ compdef _dsk_completion dsk`);
286
+ }
287
+ });
288
+ return program2;
289
+ }
290
+
291
+ // src/cli/exit-codes.ts
292
+ var ExitCode = {
293
+ /** 正常执行完成 */
294
+ SUCCESS: 0,
295
+ /** 通用错误 */
296
+ GENERAL_ERROR: 1,
297
+ /** 配置错误 */
298
+ CONFIG_ERROR: 2,
299
+ /** 用户通过 Ctrl+C 中断 */
300
+ SIGINT: 130
301
+ };
302
+
303
+ // src/index.ts
304
+ process.on("SIGINT", () => {
305
+ process.exit(ExitCode.SIGINT);
306
+ });
307
+ var program = createCli();
308
+ try {
309
+ await program.parseAsync(process.argv);
310
+ } catch (err) {
311
+ const error = err;
312
+ if (error.code === "commander.helpDisplayed" || error.code === "commander.version") {
313
+ process.exit(error.exitCode ?? ExitCode.SUCCESS);
314
+ }
315
+ if (typeof error.exitCode === "number") {
316
+ process.exit(error.exitCode);
317
+ }
318
+ console.error(String(err));
319
+ process.exit(ExitCode.GENERAL_ERROR);
320
+ }
321
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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"]}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "dskcode",
3
+ "version": "0.1.0",
4
+ "description": "A DeepSeek-native AI coding agent for your terminal",
5
+ "type": "module",
6
+ "bin": {
7
+ "dsk": "dist/index.js"
8
+ },
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "scripts": {
21
+ "dev": "tsup --watch",
22
+ "build": "tsup",
23
+ "type-check": "tsc --noEmit",
24
+ "lint": "eslint src/ tests/",
25
+ "lint:fix": "eslint src/ tests/ --fix",
26
+ "format": "prettier --write src/ tests/",
27
+ "format:check": "prettier --check src/ tests/",
28
+ "test": "vitest run",
29
+ "test:watch": "vitest",
30
+ "test:coverage": "vitest run --coverage",
31
+ "prepublishOnly": "npm run build && npm run test"
32
+ },
33
+ "dependencies": {
34
+ "chalk": "^5.6.2",
35
+ "commander": "^13.1.0",
36
+ "ink": "^7.1.0",
37
+ "ink-spinner": "^5.0.0",
38
+ "ink-text-input": "^6.0.0",
39
+ "react": "^19.2.7",
40
+ "smol-toml": "^1.3.1"
41
+ },
42
+ "devDependencies": {
43
+ "@eslint/js": "^9.24.0",
44
+ "@types/node": "^22.14.0",
45
+ "@types/react": "^19.2.17",
46
+ "eslint": "^9.24.0",
47
+ "prettier": "^3.5.3",
48
+ "tsup": "^8.4.0",
49
+ "typescript": "^5.8.3",
50
+ "typescript-eslint": "^8.30.0",
51
+ "vitest": "^3.1.1"
52
+ },
53
+ "engines": {
54
+ "node": ">=22.0.0"
55
+ },
56
+ "license": "MIT"
57
+ }