codegrunt 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 +351 -0
- package/dist/cli/at-resolver.d.ts +10 -0
- package/dist/cli/at-resolver.js +138 -0
- package/dist/cli/at-resolver.js.map +1 -0
- package/dist/cli/banner.d.ts +1 -0
- package/dist/cli/banner.js +111 -0
- package/dist/cli/banner.js.map +1 -0
- package/dist/cli/commands.d.ts +25 -0
- package/dist/cli/commands.js +799 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +142 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/input.d.ts +14 -0
- package/dist/cli/input.js +742 -0
- package/dist/cli/input.js.map +1 -0
- package/dist/cli/repl.d.ts +2 -0
- package/dist/cli/repl.js +217 -0
- package/dist/cli/repl.js.map +1 -0
- package/dist/cli/setup.d.ts +7 -0
- package/dist/cli/setup.js +82 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/cli/skills.d.ts +28 -0
- package/dist/cli/skills.js +299 -0
- package/dist/cli/skills.js.map +1 -0
- package/dist/cli/update.d.ts +7 -0
- package/dist/cli/update.js +135 -0
- package/dist/cli/update.js.map +1 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.js +93 -0
- package/dist/config.js.map +1 -0
- package/dist/core/agent/loop.d.ts +17 -0
- package/dist/core/agent/loop.js +353 -0
- package/dist/core/agent/loop.js.map +1 -0
- package/dist/core/context/manager.d.ts +26 -0
- package/dist/core/context/manager.js +98 -0
- package/dist/core/context/manager.js.map +1 -0
- package/dist/core/context/project-guide.d.ts +1 -0
- package/dist/core/context/project-guide.js +17 -0
- package/dist/core/context/project-guide.js.map +1 -0
- package/dist/core/tools/edit_file.d.ts +2 -0
- package/dist/core/tools/edit_file.js +54 -0
- package/dist/core/tools/edit_file.js.map +1 -0
- package/dist/core/tools/execute_shell.d.ts +2 -0
- package/dist/core/tools/execute_shell.js +67 -0
- package/dist/core/tools/execute_shell.js.map +1 -0
- package/dist/core/tools/executor.d.ts +3 -0
- package/dist/core/tools/executor.js +86 -0
- package/dist/core/tools/executor.js.map +1 -0
- package/dist/core/tools/list_directory.d.ts +2 -0
- package/dist/core/tools/list_directory.js +74 -0
- package/dist/core/tools/list_directory.js.map +1 -0
- package/dist/core/tools/read_file.d.ts +2 -0
- package/dist/core/tools/read_file.js +40 -0
- package/dist/core/tools/read_file.js.map +1 -0
- package/dist/core/tools/registry.d.ts +4 -0
- package/dist/core/tools/registry.js +24 -0
- package/dist/core/tools/registry.js.map +1 -0
- package/dist/core/tools/search_files.d.ts +2 -0
- package/dist/core/tools/search_files.js +88 -0
- package/dist/core/tools/search_files.js.map +1 -0
- package/dist/core/tools/write_file.d.ts +2 -0
- package/dist/core/tools/write_file.js +43 -0
- package/dist/core/tools/write_file.js.map +1 -0
- package/dist/providers/deepseek/client.d.ts +8 -0
- package/dist/providers/deepseek/client.js +27 -0
- package/dist/providers/deepseek/client.js.map +1 -0
- package/dist/providers/deepseek/provider.d.ts +8 -0
- package/dist/providers/deepseek/provider.js +165 -0
- package/dist/providers/deepseek/provider.js.map +1 -0
- package/dist/types.d.ts +111 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/billing.d.ts +40 -0
- package/dist/utils/billing.js +165 -0
- package/dist/utils/billing.js.map +1 -0
- package/dist/utils/confirm.d.ts +3 -0
- package/dist/utils/confirm.js +242 -0
- package/dist/utils/confirm.js.map +1 -0
- package/dist/utils/display.d.ts +10 -0
- package/dist/utils/display.js +121 -0
- package/dist/utils/display.js.map +1 -0
- package/dist/utils/interrupt.d.ts +5 -0
- package/dist/utils/interrupt.js +13 -0
- package/dist/utils/interrupt.js.map +1 -0
- package/dist/utils/markdown.d.ts +18 -0
- package/dist/utils/markdown.js +223 -0
- package/dist/utils/markdown.js.map +1 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
# CodeGrunt <small>(代码民工)</small>
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="./assets/logo.png" alt="CodeGrunt Logo" width="50%" />
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
> **代码民工** — 终端原生的 AI 命令行编程助手,基于 DeepSeek 构建。
|
|
8
|
+
|
|
9
|
+
[](https://www.npmjs.com/package/codegrunt)
|
|
10
|
+
[](https://opensource.org/licenses/MIT)
|
|
11
|
+
[](https://www.typescriptlang.org/)
|
|
12
|
+
[](https://nodejs.org/)
|
|
13
|
+
|
|
14
|
+
CodeGrunt 是一个开源的终端原生 AI 编程助手。它能读懂你的代码库、理解上下文,帮助你在命令行中编写、重构、调试和交付代码。
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# 交互式 REPL
|
|
18
|
+
codegrunt
|
|
19
|
+
|
|
20
|
+
# 单次任务
|
|
21
|
+
codegrunt "把 auth 模块重构为 async/await"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 特性
|
|
27
|
+
|
|
28
|
+
- **🤖 智能代理** — 使用 ReAct(推理 + 行动)循环,自主执行多步骤任务:读取文件、编辑代码、运行 Shell、搜索代码库
|
|
29
|
+
- **📂 理解代码库** — 通过 `@` 文件引用和项目指南文件(`CODEGRUNT.md` / `CLAUDE.md`)理解你的项目结构、模块导入和编码约定
|
|
30
|
+
- **🔌 DeepSeek 驱动** — 内置支持 DeepSeek Chat、V4 Flash、V4 Pro 和 R1 推理模型
|
|
31
|
+
- **🛠️ 工具调用** — 6 个内置工具:文件读写/编辑、Shell 执行、目录列表、代码搜索,破坏性操作会显示 diff 预览并请求用户确认
|
|
32
|
+
- **⚡ 流式输出** — 实时 Token 流式传输,支持 Markdown 渲染和推理过程可见,终端体验流畅
|
|
33
|
+
- **📎 @-引用** — 使用 `@file.ts`、`@src/` 或 `@https://example.com` 将文件内容、目录列表或网页内容直接注入提示词
|
|
34
|
+
- **🎯 斜杠命令** — `/init` 自动生成项目指南、`/model` 切换模型、`/compact` 压缩对话历史、`/review` 审查变更、`/skills` 管理技能等
|
|
35
|
+
- **🔒 默认安全** — 破坏性操作(写入/编辑/Shell)显示 diff 预览并要求用户确认后执行
|
|
36
|
+
- **🔧 技能系统** — 从 `.zip` 文件安装可复用的提示词模板,作为斜杠命令运行
|
|
37
|
+
- **💲 费用追踪** — 使用 `/cost` 和 `/balance` 命令实时查看会话 Token 用量和费用
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 快速开始
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# 全局安装
|
|
45
|
+
npm install -g codegrunt
|
|
46
|
+
|
|
47
|
+
# 设置 API 密钥
|
|
48
|
+
export DEEPSEEK_API_KEY=your_key_here
|
|
49
|
+
|
|
50
|
+
# 启动交互式会话
|
|
51
|
+
codegrunt
|
|
52
|
+
|
|
53
|
+
# 单次任务
|
|
54
|
+
codegrunt "解释这个项目的架构"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
首次运行且未配置 API 密钥时,CodeGrunt 会启动交互式设置向导引导你完成配置。
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 安装
|
|
62
|
+
|
|
63
|
+
**环境要求:** Node.js 18+
|
|
64
|
+
|
|
65
|
+
### npm(推荐)
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm install -g codegrunt
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### pnpm
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pnpm add -g codegrunt
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 从源码构建
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
git clone https://github.com/your-org/codegrunt.git
|
|
81
|
+
cd codegrunt
|
|
82
|
+
npm install
|
|
83
|
+
npm run build
|
|
84
|
+
npm link
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 使用方法
|
|
90
|
+
|
|
91
|
+
### 交互式 REPL
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
codegrunt
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
启动交互式会话,提供:
|
|
98
|
+
|
|
99
|
+
- 显示当前模型的 ASCII 艺术横幅
|
|
100
|
+
- `>` 提示符用于输入任务
|
|
101
|
+
- 文件路径(`@`)和斜杠命令(`/`)的 Tab 补全
|
|
102
|
+
- 多行输入支持
|
|
103
|
+
- 方向键历史记录导航
|
|
104
|
+
|
|
105
|
+
### 单次任务模式
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
codegrunt "你的任务描述"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
执行单个任务后退出。适用于脚本编写和快速查询。
|
|
112
|
+
|
|
113
|
+
### 斜杠命令
|
|
114
|
+
|
|
115
|
+
| 命令 | 描述 |
|
|
116
|
+
|---|---|
|
|
117
|
+
| `/help` | 显示帮助信息和所有可用命令 |
|
|
118
|
+
| `/model` | 交互式切换模型(方向键选择器) |
|
|
119
|
+
| `/model <id>` | 切换到指定模型(例如 `/model deepseek-v4-pro`) |
|
|
120
|
+
| `/init` | 分析代码库并生成 `CODEGRUNT.md` 项目指南 |
|
|
121
|
+
| `/clear` | 清除对话上下文 |
|
|
122
|
+
| `/compact` | 总结并压缩对话历史以节省 Token |
|
|
123
|
+
| `/review` | 审查本次会话的变更是否有逻辑问题 |
|
|
124
|
+
| `/cost` | 显示会话 Token 使用量和预估费用 |
|
|
125
|
+
| `/balance` | 显示账户余额和用量(今日 / 本月) |
|
|
126
|
+
| `/config` | 显示或修改配置设置 |
|
|
127
|
+
| `/reasoning` / `/effort` | 设置 R1 模型的推理强度(low/medium/high) |
|
|
128
|
+
| `/token` | 更新 DeepSeek API 密钥 |
|
|
129
|
+
| `/skills` | 列出和管理技能(创建、列表) |
|
|
130
|
+
| `/exit` | 退出 CodeGrunt |
|
|
131
|
+
|
|
132
|
+
### @-引用
|
|
133
|
+
|
|
134
|
+
在提示词中直接引用文件、目录或 URL:
|
|
135
|
+
|
|
136
|
+
| 语法 | 描述 | 示例 |
|
|
137
|
+
|---|---|---|
|
|
138
|
+
| `@<文件>` | 注入文件内容 | `@src/index.ts` |
|
|
139
|
+
| `@<目录>` | 注入目录列表(最多 20 条) | `@src/components/` |
|
|
140
|
+
| `@<网址>` | 获取并注入网页内容 | `@https://example.com` |
|
|
141
|
+
|
|
142
|
+
支持文件和目录路径的 Tab 补全。
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 配置
|
|
147
|
+
|
|
148
|
+
CodeGrunt 通过环境变量或 `~/.codegrunt/config.json` 文件配置。
|
|
149
|
+
|
|
150
|
+
### 环境变量
|
|
151
|
+
|
|
152
|
+
| 变量 | 描述 | 默认值 |
|
|
153
|
+
|---|---|---|
|
|
154
|
+
| `DEEPSEEK_API_KEY` | DeepSeek API 密钥 | — |
|
|
155
|
+
| `CODEGRUNT_MODEL` | 使用的模型 ID | `deepseek-v4-pro` |
|
|
156
|
+
| `CODEGRUNT_PROVIDER` | LLM 提供商 | `deepseek` |
|
|
157
|
+
| `CODEGRUNT_MAX_TOKENS` | 每次响应的最大 Token 数 | `8192` |
|
|
158
|
+
| `CODEGRUNT_TEMPERATURE` | 响应温度 (0-2) | `0.2` |
|
|
159
|
+
| `CODEGRUNT_BASE_URL` | 自定义 API 基础 URL | `https://api.deepseek.com` |
|
|
160
|
+
| `CODEGRUNT_REASONING_EFFORT` | R1 推理强度:`low` \| `medium` \| `high` | `medium` |
|
|
161
|
+
| `CODEGRUNT_TOP_P` | 核采样 (0-1) | `1` |
|
|
162
|
+
| `CODEGRUNT_FREQUENCY_PENALTY` | 重复惩罚 (-2 到 2) | `0` |
|
|
163
|
+
| `CODEGRUNT_PRESENCE_PENALTY` | 主题多样性惩罚 (-2 到 2) | `0` |
|
|
164
|
+
|
|
165
|
+
### 配置文件 (`~/.codegrunt/config.json`)
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
{
|
|
169
|
+
"apiKey": "sk-xxxxxxxx",
|
|
170
|
+
"model": "deepseek-v4-pro",
|
|
171
|
+
"maxTokens": 8192,
|
|
172
|
+
"temperature": 0.2,
|
|
173
|
+
"reasoningEffort": "medium",
|
|
174
|
+
"topP": 1,
|
|
175
|
+
"frequencyPenalty": 0,
|
|
176
|
+
"presencePenalty": 0
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
配置文件在首次运行时通过设置向导自动生成。环境变量优先级高于配置文件。
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## 支持的模型
|
|
185
|
+
|
|
186
|
+
| 提供商 | 模型 | 状态 |
|
|
187
|
+
|---|---|---|
|
|
188
|
+
| [DeepSeek](https://platform.deepseek.com/) | `deepseek-chat`、`deepseek-v4-flash`、`deepseek-v4-pro`、`deepseek-reasoner` | ✅ 支持 |
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## 架构
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
codegrunt/
|
|
196
|
+
├── src/
|
|
197
|
+
│ ├── cli/ # CLI 入口、REPL、参数解析
|
|
198
|
+
│ │ ├── index.ts # 入口(commander 驱动)
|
|
199
|
+
│ │ ├── repl.ts # 交互式 REPL 循环
|
|
200
|
+
│ │ ├── input.ts # 多行输入、Tab 补全、列表选择器
|
|
201
|
+
│ │ ├── commands.ts # 斜杠命令(/help, /model, /init 等)
|
|
202
|
+
│ │ ├── setup.ts # 首次运行设置向导
|
|
203
|
+
│ │ ├── skills.ts # 技能加载和管理
|
|
204
|
+
│ │ ├── update.ts # 版本检查和升级
|
|
205
|
+
│ │ ├── banner.ts # ASCII 艺术横幅
|
|
206
|
+
│ │ └── at-resolver.ts # @文件/@URL 引用展开
|
|
207
|
+
│ ├── core/
|
|
208
|
+
│ │ ├── agent/
|
|
209
|
+
│ │ │ └── loop.ts # 代理循环 — 核心 ReAct 推理/行动循环
|
|
210
|
+
│ │ ├── tools/
|
|
211
|
+
│ │ │ ├── registry.ts # 工具注册和查找
|
|
212
|
+
│ │ │ ├── executor.ts # 工具执行(含用户确认)
|
|
213
|
+
│ │ │ ├── read_file.ts # 读取文件内容
|
|
214
|
+
│ │ │ ├── write_file.ts # 写入内容到文件
|
|
215
|
+
│ │ │ ├── edit_file.ts # 替换文件中的精确字符串
|
|
216
|
+
│ │ │ ├── execute_shell.ts # 运行 Shell 命令
|
|
217
|
+
│ │ │ ├── list_directory.ts # 列出目录树
|
|
218
|
+
│ │ │ └── search_files.ts # 在文件中搜索文本
|
|
219
|
+
│ │ └── context/
|
|
220
|
+
│ │ ├── manager.ts # 上下文窗口管理(Token 预算、裁剪)
|
|
221
|
+
│ │ └── project-guide.ts # 加载 CODEGRUNT.md / CLAUDE.md 项目指南
|
|
222
|
+
│ ├── providers/
|
|
223
|
+
│ │ └── deepseek/
|
|
224
|
+
│ │ ├── provider.ts # DeepSeek LLM 提供商实现
|
|
225
|
+
│ │ └── client.ts # OpenAI 兼容客户端工厂
|
|
226
|
+
│ ├── utils/
|
|
227
|
+
│ │ ├── display.ts # 终端输出格式化
|
|
228
|
+
│ │ ├── confirm.ts # Diff 预览和用户确认
|
|
229
|
+
│ │ ├── billing.ts # 余额/用量查询和费用展示
|
|
230
|
+
│ │ ├── markdown.ts # 流式 Markdown 转终端渲染器
|
|
231
|
+
│ │ └── interrupt.ts # SIGINT 处理
|
|
232
|
+
│ ├── config.ts # 配置加载(环境变量、配置文件)
|
|
233
|
+
│ └── types.ts # 共享 TypeScript 类型和接口
|
|
234
|
+
├── tests/
|
|
235
|
+
│ ├── tools/
|
|
236
|
+
│ │ ├── read_file.test.ts
|
|
237
|
+
│ │ ├── write_file.test.ts
|
|
238
|
+
│ │ └── execute_shell.test.ts
|
|
239
|
+
├── docs/
|
|
240
|
+
│ ├── development-guide.md # 开发指南(英文)
|
|
241
|
+
│ ├── development-guide.zh-CN.md # 开发者指南(中文)
|
|
242
|
+
│ └── VERSION.md # 发版流程指南
|
|
243
|
+
├── package.json
|
|
244
|
+
├── tsconfig.json
|
|
245
|
+
├── vitest.config.ts
|
|
246
|
+
├── CODEGRUNT.md # CodeGrunt 项目指南
|
|
247
|
+
├── CLAUDE.md # AI 编码助手项目指南
|
|
248
|
+
└── README.md # 本文件
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### 整体流程
|
|
252
|
+
|
|
253
|
+
```
|
|
254
|
+
用户输入 (CLI / REPL)
|
|
255
|
+
│
|
|
256
|
+
▼
|
|
257
|
+
┌─────────────┐
|
|
258
|
+
│ 代理循环 │ ◄──── LLM 提供商(流式)
|
|
259
|
+
│ (loop.ts) │ ────► 工具执行
|
|
260
|
+
└──────┬──────┘
|
|
261
|
+
│
|
|
262
|
+
┌────┴────┐
|
|
263
|
+
│ 工具 │
|
|
264
|
+
│ (6 个) │
|
|
265
|
+
└─────────┘
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### 代理循环 (`src/core/agent/loop.ts`)
|
|
269
|
+
|
|
270
|
+
代理循环是 CodeGrunt 的核心,遵循 ReAct(推理 + 行动)模式:
|
|
271
|
+
|
|
272
|
+
1. **系统提示**在每次会话中构建一次(保持稳定以最大化提示缓存命中率)
|
|
273
|
+
2. **用户消息**附加 `[cwd]` 和 `[date]` 前缀以提供上下文
|
|
274
|
+
3. **流式响应**来自 LLM — 处理文本增量、推理增量和工具调用增量
|
|
275
|
+
4. **如果收到工具调用**,执行每个工具并将结果反馈给 LLM
|
|
276
|
+
5. **如果是文本响应**(finish_reason = "stop"),输出给用户并结束
|
|
277
|
+
6. **循环**最多 30 次迭代以处理多步骤任务
|
|
278
|
+
|
|
279
|
+
关键设计决策:
|
|
280
|
+
|
|
281
|
+
- **系统提示稳定性**:系统提示只构建一次,会话期间不会更改。这最大化 DeepSeek 的提示缓存命中率。
|
|
282
|
+
- **上下文管理**:`ContextManager` 跟踪 Token 使用情况,超出预算时裁剪旧消息。
|
|
283
|
+
- **流式优先**:所有 LLM 通信通过 `AsyncIterable` 流式传输,实现实时终端输出。
|
|
284
|
+
|
|
285
|
+
### 工具系统
|
|
286
|
+
|
|
287
|
+
工具是 LLM 与用户环境交互的方式。每个工具实现 `Tool` 接口。
|
|
288
|
+
|
|
289
|
+
| 工具 | 描述 |
|
|
290
|
+
|---|---|
|
|
291
|
+
| `read_file` | 读取文件内容(截断至 30,000 字符) |
|
|
292
|
+
| `write_file` | 写入内容到文件(自动创建目录) |
|
|
293
|
+
| `edit_file` | 替换文件中的精确字符串 |
|
|
294
|
+
| `execute_shell` | 运行 Shell 命令(带超时) |
|
|
295
|
+
| `list_directory` | 列出目录树(可配置深度) |
|
|
296
|
+
| `search_files` | 在文件中搜索文本模式 |
|
|
297
|
+
|
|
298
|
+
**安全机制**:在执行破坏性操作(`write_file`、`edit_file`、`execute_shell`)之前,执行器会显示 diff 预览并请求用户确认,提供三个选项:是、本次会话全部允许、否。
|
|
299
|
+
|
|
300
|
+
### 上下文管理 (`src/core/context/manager.ts`)
|
|
301
|
+
|
|
302
|
+
`ContextManager` 维护对话历史:
|
|
303
|
+
|
|
304
|
+
- **Token 估算**:使用简单的 4:1 字符与 Token 比率
|
|
305
|
+
- **裁剪**:当估算 Token 数超过预算时,移除最旧的非系统消息
|
|
306
|
+
- **预算**:聊天模型默认 90,000 Token,推理模型 100,000 Token(1M 上下文窗口)
|
|
307
|
+
|
|
308
|
+
### 提供商系统
|
|
309
|
+
|
|
310
|
+
DeepSeek 提供商实现 `LLMProvider` 接口。`StreamChunk` 判别联合类型支持:
|
|
311
|
+
|
|
312
|
+
- `text_delta` — 增量文本输出
|
|
313
|
+
- `reasoning_delta` — 思维链推理(显示为 "Thinking...")
|
|
314
|
+
- `tool_call_delta` — 流式工具调用参数
|
|
315
|
+
- `finish` — 流结束,包含结束原因
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## 开发
|
|
320
|
+
|
|
321
|
+
### 命令
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
npm run dev # 开发模式,热重载 (tsx)
|
|
325
|
+
npm run build # 编译 TypeScript 到 dist/
|
|
326
|
+
npm run typecheck # 仅类型检查,不输出文件
|
|
327
|
+
npm test # 运行 vitest 测试套件
|
|
328
|
+
npm start # 运行编译后的 dist/cli/index.js
|
|
329
|
+
|
|
330
|
+
# 运行单个测试文件
|
|
331
|
+
npx vitest run tests/tools/read_file.test.ts
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### 项目结构
|
|
335
|
+
|
|
336
|
+
- `src/cli/` — 入口、REPL 循环、参数解析、技能、更新
|
|
337
|
+
- `src/core/agent/` — 代理循环和任务规划
|
|
338
|
+
- `src/core/tools/` — 文件读写、Shell 执行、搜索工具实现
|
|
339
|
+
- `src/core/context/` — 上下文窗口管理和项目指南加载
|
|
340
|
+
- `src/providers/` — LLM 提供商适配器,实现共享的 `LLMProvider` 接口
|
|
341
|
+
- `src/utils/` — 共享工具(显示、确认、计费、Markdown、中断)
|
|
342
|
+
|
|
343
|
+
详细开发说明请参阅:
|
|
344
|
+
- [开发指南(英文)](docs/development-guide.md)
|
|
345
|
+
- [开发者指南(中文)](docs/development-guide.zh-CN.md)
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## 许可证
|
|
350
|
+
|
|
351
|
+
MIT
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { readFile, readdir, stat } from 'fs/promises';
|
|
2
|
+
import { resolve, relative, join } from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
const MAX_FILE_CHARS = 8000;
|
|
5
|
+
const MAX_DIR_FILES = 20;
|
|
6
|
+
const SKIP_DIRS = new Set(['node_modules', '.git', 'dist', '.next', '__pycache__', '.cache']);
|
|
7
|
+
// Match @something — file path, directory, or URL
|
|
8
|
+
// Stops at whitespace. Supports quoted paths: @"path with spaces"
|
|
9
|
+
const AT_PATTERN = /@(?:"([^"]+)"|(\S+))/g;
|
|
10
|
+
export async function resolveAtReferences(input, cwd) {
|
|
11
|
+
const matches = [...input.matchAll(AT_PATTERN)];
|
|
12
|
+
if (matches.length === 0)
|
|
13
|
+
return { expanded: input, refs: [] };
|
|
14
|
+
const refs = [];
|
|
15
|
+
for (const match of matches) {
|
|
16
|
+
const raw = match[0];
|
|
17
|
+
const target = match[1] ?? match[2]; // quoted or unquoted
|
|
18
|
+
const ref = await resolveOne(raw, target, cwd);
|
|
19
|
+
if (ref)
|
|
20
|
+
refs.push(ref);
|
|
21
|
+
}
|
|
22
|
+
// Replace @tokens with a placeholder; append expanded content at the end
|
|
23
|
+
let expanded = input;
|
|
24
|
+
const attachments = [];
|
|
25
|
+
for (const ref of refs) {
|
|
26
|
+
expanded = expanded.replace(ref.raw, chalk.cyan(ref.raw));
|
|
27
|
+
attachments.push(formatAttachment(ref));
|
|
28
|
+
}
|
|
29
|
+
// Strip chalk codes for the actual message sent to the model
|
|
30
|
+
const plainExpanded = stripAnsi(expanded);
|
|
31
|
+
const fullMessage = attachments.length > 0
|
|
32
|
+
? plainExpanded + '\n\n' + attachments.join('\n\n')
|
|
33
|
+
: plainExpanded;
|
|
34
|
+
return { expanded: fullMessage, refs };
|
|
35
|
+
}
|
|
36
|
+
async function resolveOne(raw, target, cwd) {
|
|
37
|
+
// URL
|
|
38
|
+
if (target.startsWith('http://') || target.startsWith('https://')) {
|
|
39
|
+
const content = await fetchUrl(target);
|
|
40
|
+
return { raw, type: 'url', target, content };
|
|
41
|
+
}
|
|
42
|
+
const absPath = resolve(cwd, target);
|
|
43
|
+
try {
|
|
44
|
+
const info = await stat(absPath);
|
|
45
|
+
if (info.isDirectory()) {
|
|
46
|
+
const content = await readDirContents(absPath, cwd);
|
|
47
|
+
return { raw, type: 'directory', target: absPath, content };
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
const content = await readFileContents(absPath);
|
|
51
|
+
return { raw, type: 'file', target: absPath, content };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// path doesn't exist — leave as-is, don't inject
|
|
56
|
+
process.stderr.write(chalk.yellow(` @ warning: "${target}" not found, skipping\n`));
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async function readFileContents(filePath) {
|
|
61
|
+
const raw = await readFile(filePath, 'utf-8');
|
|
62
|
+
if (raw.length > MAX_FILE_CHARS) {
|
|
63
|
+
return raw.slice(0, MAX_FILE_CHARS) + `\n\n[truncated — ${raw.length} total chars]`;
|
|
64
|
+
}
|
|
65
|
+
return raw;
|
|
66
|
+
}
|
|
67
|
+
async function readDirContents(dirPath, cwd) {
|
|
68
|
+
const lines = [];
|
|
69
|
+
await collectFiles(dirPath, dirPath, lines);
|
|
70
|
+
const rel = relative(cwd, dirPath) || '.';
|
|
71
|
+
return `Directory: ${rel}\n` + lines.join('\n');
|
|
72
|
+
}
|
|
73
|
+
async function collectFiles(root, dir, lines, depth = 0) {
|
|
74
|
+
if (lines.length >= MAX_DIR_FILES)
|
|
75
|
+
return;
|
|
76
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
77
|
+
entries.sort((a, b) => {
|
|
78
|
+
if (a.isDirectory() !== b.isDirectory())
|
|
79
|
+
return a.isDirectory() ? -1 : 1;
|
|
80
|
+
return a.name.localeCompare(b.name);
|
|
81
|
+
});
|
|
82
|
+
for (const entry of entries) {
|
|
83
|
+
if (lines.length >= MAX_DIR_FILES)
|
|
84
|
+
break;
|
|
85
|
+
if (entry.name.startsWith('.'))
|
|
86
|
+
continue;
|
|
87
|
+
const full = join(dir, entry.name);
|
|
88
|
+
const rel = relative(root, full);
|
|
89
|
+
const indent = ' '.repeat(depth);
|
|
90
|
+
if (entry.isDirectory()) {
|
|
91
|
+
if (SKIP_DIRS.has(entry.name))
|
|
92
|
+
continue;
|
|
93
|
+
lines.push(`${indent}${entry.name}/`);
|
|
94
|
+
await collectFiles(root, full, lines, depth + 1);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
lines.push(`${indent}${entry.name}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function fetchUrl(url) {
|
|
102
|
+
try {
|
|
103
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(10_000) });
|
|
104
|
+
if (!res.ok)
|
|
105
|
+
return `[HTTP ${res.status} fetching ${url}]`;
|
|
106
|
+
const text = await res.text();
|
|
107
|
+
// Strip HTML tags for readability
|
|
108
|
+
const stripped = text.replace(/<[^>]+>/g, ' ').replace(/\s{2,}/g, ' ').trim();
|
|
109
|
+
return stripped.length > MAX_FILE_CHARS
|
|
110
|
+
? stripped.slice(0, MAX_FILE_CHARS) + '\n[truncated]'
|
|
111
|
+
: stripped;
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
return `[Failed to fetch ${url}: ${err instanceof Error ? err.message : String(err)}]`;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function formatAttachment(ref) {
|
|
118
|
+
const label = ref.type === 'url' ? `URL: ${ref.target}` :
|
|
119
|
+
ref.type === 'directory' ? `Directory: ${ref.target}` :
|
|
120
|
+
`File: ${ref.target}`;
|
|
121
|
+
const fence = ref.type === 'url' ? '' : detectLanguage(ref.target);
|
|
122
|
+
return `<attachment ${label}>\n\`\`\`${fence}\n${ref.content}\n\`\`\`\n</attachment>`;
|
|
123
|
+
}
|
|
124
|
+
function detectLanguage(filePath) {
|
|
125
|
+
const ext = filePath.split('.').pop()?.toLowerCase() ?? '';
|
|
126
|
+
const map = {
|
|
127
|
+
ts: 'typescript', tsx: 'tsx', js: 'javascript', jsx: 'jsx',
|
|
128
|
+
py: 'python', rs: 'rust', go: 'go', java: 'java',
|
|
129
|
+
json: 'json', md: 'markdown', yaml: 'yaml', yml: 'yaml',
|
|
130
|
+
sh: 'bash', css: 'css', html: 'html', sql: 'sql',
|
|
131
|
+
};
|
|
132
|
+
return map[ext] ?? '';
|
|
133
|
+
}
|
|
134
|
+
function stripAnsi(str) {
|
|
135
|
+
// eslint-disable-next-line no-control-regex
|
|
136
|
+
return str.replace(/\x1B\[[0-9;]*m/g, '');
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=at-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"at-resolver.js","sourceRoot":"","sources":["../../src/cli/at-resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function printBanner(model: string): void;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
// ── CODEGRUNT wordmark — pixel art ──────────────────────────────────────────
|
|
3
|
+
const GLYPHS = {
|
|
4
|
+
C: [
|
|
5
|
+
' ▄███▄ ',
|
|
6
|
+
' ██ ',
|
|
7
|
+
' ██ ',
|
|
8
|
+
' ██ ',
|
|
9
|
+
' ▀███▀ ',
|
|
10
|
+
],
|
|
11
|
+
O: [
|
|
12
|
+
' ▄███▄ ',
|
|
13
|
+
' ██ █ ',
|
|
14
|
+
' ██ █ ',
|
|
15
|
+
' ██ █ ',
|
|
16
|
+
' ▀███▀ ',
|
|
17
|
+
],
|
|
18
|
+
D: [
|
|
19
|
+
' ████▄ ',
|
|
20
|
+
' ██ █ ',
|
|
21
|
+
' ██ █ ',
|
|
22
|
+
' ██ █ ',
|
|
23
|
+
' ████▀ ',
|
|
24
|
+
],
|
|
25
|
+
E: [
|
|
26
|
+
' █████ ',
|
|
27
|
+
' ██ ',
|
|
28
|
+
' ████ ',
|
|
29
|
+
' ██ ',
|
|
30
|
+
' █████ ',
|
|
31
|
+
],
|
|
32
|
+
G: [
|
|
33
|
+
' ▄███▄ ',
|
|
34
|
+
' ██ ',
|
|
35
|
+
' ██ ██ ',
|
|
36
|
+
' ██ █ ',
|
|
37
|
+
' ▀███▀ ',
|
|
38
|
+
],
|
|
39
|
+
R: [
|
|
40
|
+
' ████▄ ',
|
|
41
|
+
' ██ █ ',
|
|
42
|
+
' ████▀ ',
|
|
43
|
+
' ██▄ ',
|
|
44
|
+
' ██ █ ',
|
|
45
|
+
],
|
|
46
|
+
U: [
|
|
47
|
+
' █ █ ',
|
|
48
|
+
' █ █ ',
|
|
49
|
+
' █ █ ',
|
|
50
|
+
' █ █ ',
|
|
51
|
+
' ▀███▀ ',
|
|
52
|
+
],
|
|
53
|
+
N: [
|
|
54
|
+
' █ █ ',
|
|
55
|
+
' ██ █ ',
|
|
56
|
+
' █ █ █ ',
|
|
57
|
+
' █ ██ ',
|
|
58
|
+
' █ █ ',
|
|
59
|
+
],
|
|
60
|
+
T: [
|
|
61
|
+
' █████ ',
|
|
62
|
+
' █ ',
|
|
63
|
+
' █ ',
|
|
64
|
+
' █ ',
|
|
65
|
+
' █ ',
|
|
66
|
+
],
|
|
67
|
+
' ': [
|
|
68
|
+
' ',
|
|
69
|
+
' ',
|
|
70
|
+
' ',
|
|
71
|
+
' ',
|
|
72
|
+
' ',
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
const WORD = 'CODEGRUNT';
|
|
76
|
+
export function printBanner(model) {
|
|
77
|
+
const cols = process.stdout.columns || 80;
|
|
78
|
+
const termH = process.stdout.rows || 24;
|
|
79
|
+
// Blue accent
|
|
80
|
+
const blue = (s) => chalk.hex('#4A90D9')(s);
|
|
81
|
+
const dim = chalk.gray;
|
|
82
|
+
const bold = chalk.bold;
|
|
83
|
+
// Skip glyphs when terminal is too short
|
|
84
|
+
const skipGlyphs = termH < 14;
|
|
85
|
+
if (!skipGlyphs) {
|
|
86
|
+
// Build the wordmark row by row
|
|
87
|
+
const rows = Array.from({ length: 5 }, () => '');
|
|
88
|
+
for (let i = 0; i < WORD.length; i++) {
|
|
89
|
+
const ch = WORD[i];
|
|
90
|
+
const glyph = GLYPHS[ch] ?? GLYPHS[' '];
|
|
91
|
+
for (let r = 0; r < 5; r++) {
|
|
92
|
+
rows[r] += (i > 0 ? ' ' : '') + glyph[r];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Centre the wordmark
|
|
96
|
+
process.stdout.write('\n');
|
|
97
|
+
for (const row of rows) {
|
|
98
|
+
const pad = Math.max(0, Math.floor((cols - row.length) / 2));
|
|
99
|
+
process.stdout.write(' '.repeat(pad) + blue(row) + '\n');
|
|
100
|
+
}
|
|
101
|
+
process.stdout.write('\n');
|
|
102
|
+
}
|
|
103
|
+
// ── Version/model info line ────────────────────────────────────────────
|
|
104
|
+
process.stdout.write(' ' +
|
|
105
|
+
bold.white('CodeGrunt') +
|
|
106
|
+
dim(' v0.1.0 · model: ') +
|
|
107
|
+
blue(model) +
|
|
108
|
+
dim(' · /help for commands') +
|
|
109
|
+
'\n\n');
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=banner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"banner.js","sourceRoot":"","sources":["../../src/cli/banner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { LLMProvider, CodeGruntConfig } from '../types.js';
|
|
2
|
+
import type { ContextManager } from '../core/context/manager.js';
|
|
3
|
+
import type { Skill } from './skills.js';
|
|
4
|
+
export type SlashCommandResult = {
|
|
5
|
+
type: 'handled';
|
|
6
|
+
} | {
|
|
7
|
+
type: 'clear';
|
|
8
|
+
} | {
|
|
9
|
+
type: 'config_changed';
|
|
10
|
+
config: CodeGruntConfig;
|
|
11
|
+
} | {
|
|
12
|
+
type: 'model_changed';
|
|
13
|
+
config: CodeGruntConfig;
|
|
14
|
+
} | {
|
|
15
|
+
type: 'exit';
|
|
16
|
+
} | {
|
|
17
|
+
type: 'skill_run';
|
|
18
|
+
prompt: string;
|
|
19
|
+
system?: string;
|
|
20
|
+
} | {
|
|
21
|
+
type: 'skills_reload';
|
|
22
|
+
} | {
|
|
23
|
+
type: 'not_a_command';
|
|
24
|
+
};
|
|
25
|
+
export declare function handleSlashCommand(input: string, cwd: string, config: CodeGruntConfig, provider: LLMProvider, context: ContextManager, skills?: Skill[]): Promise<SlashCommandResult>;
|