@jiangyuan1209/yuan-claw 0.1.2 → 0.1.3
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 +390 -150
- package/dist/agent/build-system-prompt.js +5 -1
- package/dist/agent/run-local-agent-loop.js +6 -1
- package/dist/cli/main.js +0 -1
- package/dist/config/load-config.js +1 -16
- package/dist/model/providers/openai-compatible.js +3 -14
- package/dist/security/path-guards.js +5 -0
- package/dist/security/shell-policy.js +14 -1
- package/dist/skills/discover.js +24 -0
- package/dist/skills/index.js +7 -0
- package/dist/skills/match.js +59 -0
- package/dist/skills/parse.js +8 -0
- package/dist/skills/paths.js +14 -0
- package/dist/skills/prompt.js +26 -0
- package/dist/skills/registry.js +43 -0
- package/dist/skills/runtime.js +19 -0
- package/dist/skills/types.js +1 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
```md
|
|
1
2
|
# yuan-claw
|
|
2
3
|
|
|
3
4
|
一个基于 **Node.js + TypeScript** 的本地 CLI Agent。
|
|
@@ -14,7 +15,8 @@
|
|
|
14
15
|
- 🖥️ 支持交互式 REPL
|
|
15
16
|
- ✅ 支持工具执行前确认(approval)
|
|
16
17
|
- ⌨️ 支持 `↑ / ↓ / Enter` 选择确认项
|
|
17
|
-
- 🔁
|
|
18
|
+
- 🔁 支持会话级”总是允许”模式
|
|
19
|
+
- 📦 支持本地 Skill 插件扩展能力
|
|
18
20
|
- 🔌 支持代理配置
|
|
19
21
|
- 🛠️ 基于 TypeScript,便于二次开发
|
|
20
22
|
|
|
@@ -29,80 +31,236 @@
|
|
|
29
31
|
|
|
30
32
|
## Installation
|
|
31
33
|
|
|
34
|
+
### 方式一:通过 npm 全局安装(推荐)
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install -g @jiangyuan1209/yuan-claw
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
安装完成后可直接使用:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
yuan-claw
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
卸载命令:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm uninstall -g @jiangyuan1209/yuan-claw
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
如需安装指定版本:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npm install -g @jiangyuan1209/yuan-claw@0.1.2
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
升级到最新版:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm install -g @jiangyuan1209/yuan-claw@latest
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
### 方式二:从源码安装
|
|
67
|
+
|
|
32
68
|
```bash
|
|
33
69
|
git clone https://github.com/your-name/yuan-claw.git
|
|
34
70
|
cd yuan-claw
|
|
35
71
|
npm install
|
|
72
|
+
npm run build
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
源码模式下运行:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npm run dev
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
或:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
npm run start
|
|
36
85
|
```
|
|
37
86
|
|
|
38
87
|
---
|
|
39
88
|
|
|
40
89
|
## Quick Start
|
|
41
90
|
|
|
42
|
-
### 1.
|
|
91
|
+
### 1. 首次运行初始化配置
|
|
43
92
|
|
|
44
|
-
|
|
93
|
+
安装完成后,先执行一次:
|
|
45
94
|
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
MODEL_NAME=
|
|
95
|
+
```bash
|
|
96
|
+
yuan-claw
|
|
97
|
+
```
|
|
50
98
|
|
|
51
|
-
|
|
99
|
+
程序会在你的用户目录下自动初始化配置文件:
|
|
52
100
|
|
|
53
|
-
|
|
54
|
-
|
|
101
|
+
```bash
|
|
102
|
+
~/.yuan-claw/settings.json
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
在 macOS / Linux 上通常类似:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
/Users/你的用户名/.yuan-claw/settings.json
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
如果你使用的是 Windows,则通常位于用户目录下对应的 `.yuan-claw` 文件夹中。
|
|
112
|
+
|
|
113
|
+
> 注意:第一次运行通常只是创建配置文件。
|
|
114
|
+
> 你需要手动填写配置后,再次执行 `yuan-claw` 才会真正生效。
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
### 2. 编辑 `~/.yuan-claw/settings.json`
|
|
119
|
+
|
|
120
|
+
示例:
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"MODEL_API_KEY": "your_api_key",
|
|
125
|
+
"MODEL_BASE_URL": "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
126
|
+
"MODEL_NAME": "qwen3-max-2026-01-23",
|
|
127
|
+
"BRAVE_SEARCH_API_KEY": "your_brave_search_api_key",
|
|
128
|
+
"HTTP_PROXY": "http://127.0.0.1:33210",
|
|
129
|
+
"HTTPS_PROXY": "http://127.0.0.1:33210"
|
|
130
|
+
}
|
|
55
131
|
```
|
|
56
132
|
|
|
57
133
|
最小可用配置通常只需要:
|
|
58
134
|
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"MODEL_API_KEY": "your_api_key",
|
|
138
|
+
"MODEL_BASE_URL": "https://api.openai.com/v1",
|
|
139
|
+
"MODEL_NAME": "gpt-4o-mini"
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
如果你希望启用网页搜索,还可以配置:
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"BRAVE_SEARCH_API_KEY": "your_brave_search_api_key"
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
如需使用代理,可以配置:
|
|
152
|
+
|
|
153
|
+
```json
|
|
154
|
+
{
|
|
155
|
+
"HTTP_PROXY": "http://127.0.0.1:33210",
|
|
156
|
+
"HTTPS_PROXY": "http://127.0.0.1:33210"
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
保存后,重新执行:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
yuan-claw
|
|
63
164
|
```
|
|
64
165
|
|
|
65
166
|
---
|
|
66
167
|
|
|
67
|
-
###
|
|
168
|
+
### 3. 开始使用
|
|
169
|
+
|
|
170
|
+
#### 交互式 REPL
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
yuan-claw
|
|
174
|
+
```
|
|
68
175
|
|
|
69
176
|
#### 单次命令
|
|
70
177
|
|
|
71
178
|
```bash
|
|
72
|
-
|
|
179
|
+
yuan-claw "帮我搜索 OpenAI 最新消息"
|
|
73
180
|
```
|
|
74
181
|
|
|
75
|
-
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Configuration
|
|
185
|
+
|
|
186
|
+
项目优先从用户目录中的配置文件读取配置:
|
|
76
187
|
|
|
77
188
|
```bash
|
|
78
|
-
|
|
189
|
+
~/.yuan-claw/settings.json
|
|
79
190
|
```
|
|
80
191
|
|
|
81
|
-
|
|
192
|
+
### 支持的配置项
|
|
82
193
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
194
|
+
- `MODEL_API_KEY`
|
|
195
|
+
- `MODEL_BASE_URL`
|
|
196
|
+
- `MODEL_NAME`
|
|
197
|
+
- `OPENAI_API_KEY`
|
|
198
|
+
- `OPENAI_BASE_URL`
|
|
199
|
+
- `OPENAI_MODEL`
|
|
200
|
+
- `BRAVE_SEARCH_API_KEY`
|
|
201
|
+
- `HTTP_PROXY`
|
|
202
|
+
- `HTTPS_PROXY`
|
|
203
|
+
- `http_proxy`
|
|
204
|
+
- `https_proxy`
|
|
87
205
|
|
|
88
|
-
|
|
206
|
+
### 推荐配置示例
|
|
207
|
+
|
|
208
|
+
#### OpenAI-compatible 通用配置
|
|
209
|
+
|
|
210
|
+
```json
|
|
211
|
+
{
|
|
212
|
+
"MODEL_API_KEY": "your_api_key",
|
|
213
|
+
"MODEL_BASE_URL": "https://api.openai.com/v1",
|
|
214
|
+
"MODEL_NAME": "gpt-4o-mini"
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
#### DashScope 示例
|
|
219
|
+
|
|
220
|
+
```json
|
|
221
|
+
{
|
|
222
|
+
"MODEL_API_KEY": "your_dashscope_api_key",
|
|
223
|
+
"MODEL_BASE_URL": "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
224
|
+
"MODEL_NAME": "qwen3-max-2026-01-23",
|
|
225
|
+
"BRAVE_SEARCH_API_KEY": "your_brave_search_api_key",
|
|
226
|
+
"HTTP_PROXY": "http://127.0.0.1:33210",
|
|
227
|
+
"HTTPS_PROXY": "http://127.0.0.1:33210"
|
|
228
|
+
}
|
|
89
229
|
```
|
|
90
230
|
|
|
91
231
|
---
|
|
92
232
|
|
|
93
|
-
|
|
233
|
+
## Environment Variables(兼容方式)
|
|
94
234
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
235
|
+
除了 `~/.yuan-claw/settings.json` 外,开发者也可以在项目根目录通过 `.env` 配置:
|
|
236
|
+
|
|
237
|
+
```env
|
|
238
|
+
MODEL_API_KEY=
|
|
239
|
+
MODEL_BASE_URL=
|
|
240
|
+
MODEL_NAME=
|
|
241
|
+
|
|
242
|
+
BRAVE_SEARCH_API_KEY=
|
|
243
|
+
|
|
244
|
+
HTTP_PROXY=
|
|
245
|
+
HTTPS_PROXY=
|
|
98
246
|
```
|
|
99
247
|
|
|
100
|
-
|
|
248
|
+
也支持 OpenAI 风格变量名:
|
|
101
249
|
|
|
102
|
-
```
|
|
103
|
-
|
|
250
|
+
```env
|
|
251
|
+
OPENAI_API_KEY=
|
|
252
|
+
OPENAI_BASE_URL=
|
|
253
|
+
OPENAI_MODEL=
|
|
104
254
|
```
|
|
105
255
|
|
|
256
|
+
> 普通 CLI 用户更推荐使用:
|
|
257
|
+
>
|
|
258
|
+
> ```bash
|
|
259
|
+
> ~/.yuan-claw/settings.json
|
|
260
|
+
> ```
|
|
261
|
+
>
|
|
262
|
+
> `.env` 更适合源码开发或本地调试。
|
|
263
|
+
|
|
106
264
|
---
|
|
107
265
|
|
|
108
266
|
## REPL Usage
|
|
@@ -163,86 +321,37 @@ yuan-claw[always]>
|
|
|
163
321
|
|
|
164
322
|
---
|
|
165
323
|
|
|
166
|
-
## Scripts
|
|
167
|
-
|
|
168
|
-
```json
|
|
169
|
-
{
|
|
170
|
-
"scripts": {
|
|
171
|
-
"dev": "tsx src/cli/main.ts",
|
|
172
|
-
"build": "tsc -p tsconfig.json",
|
|
173
|
-
"start": "node dist/cli/main.js",
|
|
174
|
-
"check": "tsc --noEmit"
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
### 脚本说明
|
|
180
|
-
|
|
181
|
-
- `npm run dev`:开发模式运行源码
|
|
182
|
-
- `npm run build`:编译到 `dist/`
|
|
183
|
-
- `npm run start`:运行编译后的 CLI
|
|
184
|
-
- `npm run check`:执行 TypeScript 类型检查
|
|
185
|
-
|
|
186
|
-
说明:
|
|
187
|
-
|
|
188
|
-
- `npm run dev` / `npm run start`
|
|
189
|
-
- 不传参数:进入 REPL
|
|
190
|
-
- 传入参数:执行单次命令
|
|
191
|
-
|
|
192
|
-
---
|
|
193
|
-
|
|
194
324
|
## CLI Usage
|
|
195
325
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
```json
|
|
199
|
-
"bin": {
|
|
200
|
-
"yuan-claw": "./dist/cli/main.js"
|
|
201
|
-
}
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
构建并安装后,可以直接使用:
|
|
326
|
+
全局安装后可直接运行:
|
|
205
327
|
|
|
206
328
|
```bash
|
|
207
329
|
yuan-claw
|
|
208
330
|
```
|
|
209
331
|
|
|
210
|
-
|
|
332
|
+
单次命令模式:
|
|
211
333
|
|
|
212
334
|
```bash
|
|
213
335
|
yuan-claw "帮我搜索 AI 新闻"
|
|
214
336
|
```
|
|
215
337
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
## Environment Variables
|
|
219
|
-
|
|
220
|
-
项目使用 **OpenAI-compatible API**,按以下优先级读取配置:
|
|
338
|
+
如果你是源码模式开发,则可以使用:
|
|
221
339
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
2. `OPENAI_API_KEY`
|
|
226
|
-
|
|
227
|
-
### Base URL
|
|
228
|
-
|
|
229
|
-
1. `MODEL_BASE_URL`
|
|
230
|
-
2. `OPENAI_BASE_URL`
|
|
231
|
-
|
|
232
|
-
### Model Name
|
|
340
|
+
```bash
|
|
341
|
+
npm run dev
|
|
342
|
+
```
|
|
233
343
|
|
|
234
|
-
|
|
235
|
-
2. `OPENAI_MODEL`
|
|
236
|
-
3. 默认值:`gpt-4o-mini`
|
|
344
|
+
或:
|
|
237
345
|
|
|
238
|
-
|
|
346
|
+
```bash
|
|
347
|
+
npm run dev -- "帮我总结这个项目的功能"
|
|
348
|
+
```
|
|
239
349
|
|
|
240
|
-
|
|
350
|
+
编译后运行:
|
|
241
351
|
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
OPENAI_MODEL=
|
|
352
|
+
```bash
|
|
353
|
+
npm run build
|
|
354
|
+
npm run start
|
|
246
355
|
```
|
|
247
356
|
|
|
248
357
|
---
|
|
@@ -251,8 +360,10 @@ OPENAI_MODEL=
|
|
|
251
360
|
|
|
252
361
|
如果你希望启用网页搜索工具,请配置:
|
|
253
362
|
|
|
254
|
-
```
|
|
255
|
-
|
|
363
|
+
```json
|
|
364
|
+
{
|
|
365
|
+
"BRAVE_SEARCH_API_KEY": "your_brave_search_api_key"
|
|
366
|
+
}
|
|
256
367
|
```
|
|
257
368
|
|
|
258
369
|
未配置时,`web_search` 工具会被禁用。
|
|
@@ -263,12 +374,14 @@ BRAVE_SEARCH_API_KEY=your_brave_search_api_key
|
|
|
263
374
|
|
|
264
375
|
如需通过代理访问模型服务或外部网站,可以配置:
|
|
265
376
|
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
|
|
377
|
+
```json
|
|
378
|
+
{
|
|
379
|
+
"HTTP_PROXY": "http://127.0.0.1:33210",
|
|
380
|
+
"HTTPS_PROXY": "http://127.0.0.1:33210"
|
|
381
|
+
}
|
|
269
382
|
```
|
|
270
383
|
|
|
271
|
-
|
|
384
|
+
程序会自动读取以下配置项:
|
|
272
385
|
|
|
273
386
|
- `HTTP_PROXY`
|
|
274
387
|
- `HTTPS_PROXY`
|
|
@@ -277,41 +390,124 @@ HTTPS_PROXY=http://127.0.0.1:33210
|
|
|
277
390
|
|
|
278
391
|
---
|
|
279
392
|
|
|
280
|
-
##
|
|
393
|
+
## Skills / 本地插件扩展
|
|
281
394
|
|
|
282
|
-
|
|
395
|
+
yuan-claw 支持通过本地 Skill(技能)文件扩展 Agent 的能力。Skill 是存放在固定目录下的 Markdown 文件,Agent 会在运行时根据用户输入自动匹配并加载相关 Skill 的提示词,从而获得领域知识或操作指引。
|
|
283
396
|
|
|
284
|
-
|
|
285
|
-
# =========================
|
|
286
|
-
# 模型服务配置(推荐)
|
|
287
|
-
# =========================
|
|
397
|
+
### Skill 目录结构
|
|
288
398
|
|
|
289
|
-
|
|
290
|
-
MODEL_BASE_URL=
|
|
291
|
-
MODEL_NAME=
|
|
399
|
+
所有 Skill 文件存放在:
|
|
292
400
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
401
|
+
```bash
|
|
402
|
+
~/.yuan-claw/skills/
|
|
403
|
+
```
|
|
296
404
|
|
|
297
|
-
|
|
298
|
-
OPENAI_BASE_URL=
|
|
299
|
-
OPENAI_MODEL=
|
|
405
|
+
每个 Skill 是一个子目录,其中必须包含一个 `SKILL.md` 文件:
|
|
300
406
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
407
|
+
```
|
|
408
|
+
~/.yuan-claw/skills/
|
|
409
|
+
├── pdf/
|
|
410
|
+
│ └── SKILL.md
|
|
411
|
+
├── frontend-design/
|
|
412
|
+
│ └── SKILL.md
|
|
413
|
+
└── my-skill/
|
|
414
|
+
└── SKILL.md
|
|
415
|
+
```
|
|
304
416
|
|
|
305
|
-
|
|
417
|
+
### SKILL.md 文件格式
|
|
306
418
|
|
|
307
|
-
|
|
308
|
-
# 代理配置(可选)
|
|
309
|
-
# =========================
|
|
419
|
+
`SKILL.md` 使用 YAML frontmatter + Markdown body 的格式:
|
|
310
420
|
|
|
311
|
-
|
|
312
|
-
|
|
421
|
+
```markdown
|
|
422
|
+
---
|
|
423
|
+
name: pdf
|
|
424
|
+
description: PDF 文件处理技能,支持提取文本、表格、OCR 等
|
|
425
|
+
tags: [pdf, document, ocr]
|
|
426
|
+
license: MIT
|
|
427
|
+
version: 1.0.0
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
## 使用指南
|
|
431
|
+
|
|
432
|
+
当用户需要处理 PDF 文件时:
|
|
433
|
+
1. 使用 pdfplumber 提取文本...
|
|
434
|
+
2. 对于扫描件,使用 OCR...
|
|
313
435
|
```
|
|
314
436
|
|
|
437
|
+
Frontmatter 字段说明:
|
|
438
|
+
|
|
439
|
+
- `name`(可选):Skill 名称,用于匹配和展示。未填写时使用目录名
|
|
440
|
+
- `description`(可选):简短描述,用于匹配和展示
|
|
441
|
+
- `tags`(可选):标签数组,用于关键词匹配
|
|
442
|
+
- `license`(可选):许可证
|
|
443
|
+
- `version`(可选):版本号
|
|
444
|
+
|
|
445
|
+
Markdown body 是 Skill 的实际提示词内容,会被注入到 system prompt 中指导 Agent 行为。
|
|
446
|
+
|
|
447
|
+
### 匹配机制
|
|
448
|
+
|
|
449
|
+
Agent 会根据用户输入自动匹配最相关的 Skill(最多匹配 3 个),匹配依据包括:
|
|
450
|
+
|
|
451
|
+
- 用户输入中是否包含 Skill 名称
|
|
452
|
+
- 用户输入中是否包含 Skill 的标签
|
|
453
|
+
- 用户输入分词后与 Skill 描述的关键词重合度
|
|
454
|
+
|
|
455
|
+
匹配到的 Skill 内容会被组装到 system prompt 中,指导 Agent 使用相应的知识和流程。
|
|
456
|
+
|
|
457
|
+
### 添加自定义 Skill
|
|
458
|
+
|
|
459
|
+
1. 在 `~/.yuan-claw/skills/` 下创建新目录:
|
|
460
|
+
|
|
461
|
+
```bash
|
|
462
|
+
mkdir -p ~/.yuan-claw/skills/my-skill
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
2. 创建 `SKILL.md` 文件:
|
|
466
|
+
|
|
467
|
+
```bash
|
|
468
|
+
cat > ~/.yuan-claw/skills/my-skill/SKILL.md << 'EOF'
|
|
469
|
+
---
|
|
470
|
+
name: my-skill
|
|
471
|
+
description: 我的自定义技能
|
|
472
|
+
tags: [custom]
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
当用户问到 XXX 时,请按以下步骤操作:
|
|
476
|
+
1. ...
|
|
477
|
+
2. ...
|
|
478
|
+
EOF
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
3. 下次运行 `yuan-claw` 时,该 Skill 会被自动发现和加载。
|
|
482
|
+
|
|
483
|
+
---
|
|
484
|
+
|
|
485
|
+
## Scripts
|
|
486
|
+
|
|
487
|
+
```json
|
|
488
|
+
{
|
|
489
|
+
"scripts": {
|
|
490
|
+
"dev": "tsx src/cli/main.ts",
|
|
491
|
+
"build": "tsc -p tsconfig.json",
|
|
492
|
+
"start": "node dist/cli/main.js",
|
|
493
|
+
"check": "tsc --noEmit"
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
### 脚本说明
|
|
499
|
+
|
|
500
|
+
- `npm run dev`:开发模式运行源码
|
|
501
|
+
- `npm run build`:编译到 `dist/`
|
|
502
|
+
- `npm run start`:运行编译后的 CLI
|
|
503
|
+
- `npm run check`:执行 TypeScript 类型检查
|
|
504
|
+
|
|
505
|
+
说明:
|
|
506
|
+
|
|
507
|
+
- `npm run dev` / `npm run start`
|
|
508
|
+
- 不传参数:进入 REPL
|
|
509
|
+
- 传入参数:执行单次命令
|
|
510
|
+
|
|
315
511
|
---
|
|
316
512
|
|
|
317
513
|
## Examples
|
|
@@ -319,17 +515,23 @@ HTTPS_PROXY=
|
|
|
319
515
|
### 普通问答
|
|
320
516
|
|
|
321
517
|
```bash
|
|
322
|
-
|
|
518
|
+
yuan-claw "帮我总结这个项目的功能"
|
|
323
519
|
```
|
|
324
520
|
|
|
325
521
|
### 搜索最新消息
|
|
326
522
|
|
|
327
523
|
```bash
|
|
328
|
-
|
|
524
|
+
yuan-claw "帮我搜索 OpenAI 最新消息"
|
|
329
525
|
```
|
|
330
526
|
|
|
331
527
|
### 进入 REPL
|
|
332
528
|
|
|
529
|
+
```bash
|
|
530
|
+
yuan-claw
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
### 源码开发模式
|
|
534
|
+
|
|
333
535
|
```bash
|
|
334
536
|
npm run dev
|
|
335
537
|
```
|
|
@@ -345,18 +547,42 @@ npm run start -- "帮我搜索 AI 新闻"
|
|
|
345
547
|
|
|
346
548
|
## Troubleshooting
|
|
347
549
|
|
|
348
|
-
###
|
|
550
|
+
### 第一次运行后为什么没有立即生效?
|
|
349
551
|
|
|
350
|
-
|
|
552
|
+
因为第一次执行:
|
|
351
553
|
|
|
352
|
-
```
|
|
353
|
-
|
|
554
|
+
```bash
|
|
555
|
+
yuan-claw
|
|
354
556
|
```
|
|
355
557
|
|
|
356
|
-
|
|
558
|
+
通常只是为了初始化配置文件:
|
|
357
559
|
|
|
358
|
-
```
|
|
359
|
-
|
|
560
|
+
```bash
|
|
561
|
+
~/.yuan-claw/settings.json
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
你需要手动填写配置项并保存,然后再次执行:
|
|
565
|
+
|
|
566
|
+
```bash
|
|
567
|
+
yuan-claw
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
---
|
|
571
|
+
|
|
572
|
+
### `Missing MODEL_API_KEY / OPENAI_API_KEY in environment variables.`
|
|
573
|
+
|
|
574
|
+
说明模型 API Key 尚未正确配置。请至少在以下任一位置填写:
|
|
575
|
+
|
|
576
|
+
- `~/.yuan-claw/settings.json`
|
|
577
|
+
- 系统环境变量
|
|
578
|
+
- 项目根目录 `.env`
|
|
579
|
+
|
|
580
|
+
例如:
|
|
581
|
+
|
|
582
|
+
```json
|
|
583
|
+
{
|
|
584
|
+
"MODEL_API_KEY": "your_api_key"
|
|
585
|
+
}
|
|
360
586
|
```
|
|
361
587
|
|
|
362
588
|
---
|
|
@@ -365,19 +591,23 @@ OPENAI_API_KEY=your_api_key
|
|
|
365
591
|
|
|
366
592
|
说明未配置 Brave Search API Key。请添加:
|
|
367
593
|
|
|
368
|
-
```
|
|
369
|
-
|
|
594
|
+
```json
|
|
595
|
+
{
|
|
596
|
+
"BRAVE_SEARCH_API_KEY": "your_brave_search_api_key"
|
|
597
|
+
}
|
|
370
598
|
```
|
|
371
599
|
|
|
372
600
|
---
|
|
373
601
|
|
|
374
602
|
### 无法访问外部服务 / 请求超时
|
|
375
603
|
|
|
376
|
-
|
|
604
|
+
请检查是否需要代理,例如:
|
|
377
605
|
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
|
|
606
|
+
```json
|
|
607
|
+
{
|
|
608
|
+
"HTTP_PROXY": "http://127.0.0.1:33210",
|
|
609
|
+
"HTTPS_PROXY": "http://127.0.0.1:33210"
|
|
610
|
+
}
|
|
381
611
|
```
|
|
382
612
|
|
|
383
613
|
---
|
|
@@ -405,13 +635,33 @@ dist/cli/main.js
|
|
|
405
635
|
|
|
406
636
|
---
|
|
407
637
|
|
|
638
|
+
### 全局命令 `yuan-claw` 不存在
|
|
639
|
+
|
|
640
|
+
如果你是通过 npm 全局安装,请确认已成功安装:
|
|
641
|
+
|
|
642
|
+
```bash
|
|
643
|
+
npm install -g @jiangyuan1209/yuan-claw
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
如仍有问题,可尝试重新安装:
|
|
647
|
+
|
|
648
|
+
```bash
|
|
649
|
+
npm uninstall -g @jiangyuan1209/yuan-claw
|
|
650
|
+
npm install -g @jiangyuan1209/yuan-claw@latest
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
---
|
|
654
|
+
|
|
408
655
|
## Development
|
|
409
656
|
|
|
410
657
|
```bash
|
|
658
|
+
npm install
|
|
411
659
|
npm run check
|
|
412
660
|
npm run dev
|
|
413
661
|
```
|
|
414
662
|
|
|
663
|
+
如果你使用源码开发方式,也可以在项目根目录创建 `.env` 文件辅助调试。
|
|
664
|
+
|
|
415
665
|
---
|
|
416
666
|
|
|
417
667
|
## Roadmap
|
|
@@ -428,14 +678,4 @@ npm run dev
|
|
|
428
678
|
|
|
429
679
|
## License
|
|
430
680
|
|
|
431
|
-
MIT
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
## `.gitignore`
|
|
436
|
-
|
|
437
|
-
```gitignore
|
|
438
|
-
node_modules
|
|
439
|
-
dist
|
|
440
|
-
.env
|
|
441
|
-
```
|
|
681
|
+
MIT
|
|
@@ -9,7 +9,10 @@ function formatToolsForPrompt(tools) {
|
|
|
9
9
|
})
|
|
10
10
|
.join("\n");
|
|
11
11
|
}
|
|
12
|
-
export function buildSystemPrompt(tools) {
|
|
12
|
+
export function buildSystemPrompt(tools, skillsPrompt) {
|
|
13
|
+
const skillsSection = skillsPrompt
|
|
14
|
+
? ["", "LOCAL SKILLS:", skillsPrompt].join("\n")
|
|
15
|
+
: "";
|
|
13
16
|
return [
|
|
14
17
|
"You are a local CLI coding agent.",
|
|
15
18
|
"You do not have direct filesystem, shell, git, or network access unless you use the provided tools.",
|
|
@@ -37,6 +40,7 @@ export function buildSystemPrompt(tools) {
|
|
|
37
40
|
"",
|
|
38
41
|
"AVAILABLE TOOLS:",
|
|
39
42
|
formatToolsForPrompt(tools),
|
|
43
|
+
skillsSection,
|
|
40
44
|
"",
|
|
41
45
|
"Examples:",
|
|
42
46
|
'{"type":"tool_call","toolName":"read_file","args":{"path":"package.json"}}',
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { buildSystemPrompt } from "./build-system-prompt.js";
|
|
2
2
|
import { parseAgentResponse } from "./parse-agent-response.js";
|
|
3
|
+
import { SkillsRuntime } from "../skills/runtime.js";
|
|
3
4
|
function stringifyForModel(value) {
|
|
4
5
|
try {
|
|
5
6
|
return JSON.stringify(value);
|
|
@@ -30,11 +31,15 @@ export async function runLocalAgentLoop(params) {
|
|
|
30
31
|
type: "run_start",
|
|
31
32
|
input: userInput,
|
|
32
33
|
});
|
|
34
|
+
// Load and match skills based on user input
|
|
35
|
+
const skillsRuntime = new SkillsRuntime();
|
|
36
|
+
await skillsRuntime.reload();
|
|
37
|
+
const skillsPrompt = skillsRuntime.buildPromptForInput(userInput);
|
|
33
38
|
const historyMessages = previousMessages.filter((message) => message.role !== "system");
|
|
34
39
|
const messages = [
|
|
35
40
|
{
|
|
36
41
|
role: "system",
|
|
37
|
-
content: buildSystemPrompt(Array.from(tools.values())),
|
|
42
|
+
content: buildSystemPrompt(Array.from(tools.values()), skillsPrompt),
|
|
38
43
|
},
|
|
39
44
|
...historyMessages,
|
|
40
45
|
{
|
package/dist/cli/main.js
CHANGED
|
@@ -27,20 +27,5 @@ async function loadSettingsFile() {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
export async function loadAppConfig() {
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
...fileConfig,
|
|
33
|
-
MODEL_API_KEY: process.env.MODEL_API_KEY ?? fileConfig.MODEL_API_KEY,
|
|
34
|
-
MODEL_BASE_URL: process.env.MODEL_BASE_URL ?? fileConfig.MODEL_BASE_URL,
|
|
35
|
-
MODEL_NAME: process.env.MODEL_NAME ?? fileConfig.MODEL_NAME,
|
|
36
|
-
OPENAI_API_KEY: process.env.OPENAI_API_KEY ?? fileConfig.OPENAI_API_KEY,
|
|
37
|
-
OPENAI_BASE_URL: process.env.OPENAI_BASE_URL ?? fileConfig.OPENAI_BASE_URL,
|
|
38
|
-
OPENAI_MODEL: process.env.OPENAI_MODEL ?? fileConfig.OPENAI_MODEL,
|
|
39
|
-
BRAVE_SEARCH_API_KEY: process.env.BRAVE_SEARCH_API_KEY ?? fileConfig.BRAVE_SEARCH_API_KEY,
|
|
40
|
-
BRAVE_API_KEY: process.env.BRAVE_API_KEY ?? fileConfig.BRAVE_API_KEY,
|
|
41
|
-
HTTP_PROXY: process.env.HTTP_PROXY ?? fileConfig.HTTP_PROXY,
|
|
42
|
-
HTTPS_PROXY: process.env.HTTPS_PROXY ?? fileConfig.HTTPS_PROXY,
|
|
43
|
-
http_proxy: process.env.http_proxy ?? fileConfig.http_proxy,
|
|
44
|
-
https_proxy: process.env.https_proxy ?? fileConfig.https_proxy,
|
|
45
|
-
};
|
|
30
|
+
return await loadSettingsFile();
|
|
46
31
|
}
|
|
@@ -1,20 +1,9 @@
|
|
|
1
1
|
import OpenAI from "openai";
|
|
2
2
|
export function createOpenAICompatibleClient(options = {}) {
|
|
3
3
|
const config = options.config ?? {};
|
|
4
|
-
const apiKey = config.MODEL_API_KEY
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
process.env.OPENAI_API_KEY;
|
|
8
|
-
const baseURL = config.MODEL_BASE_URL ??
|
|
9
|
-
config.OPENAI_BASE_URL ??
|
|
10
|
-
process.env.MODEL_BASE_URL ??
|
|
11
|
-
process.env.OPENAI_BASE_URL;
|
|
12
|
-
const model = options.model ??
|
|
13
|
-
config.MODEL_NAME ??
|
|
14
|
-
config.OPENAI_MODEL ??
|
|
15
|
-
process.env.MODEL_NAME ??
|
|
16
|
-
process.env.OPENAI_MODEL ??
|
|
17
|
-
"gpt-4o-mini";
|
|
4
|
+
const apiKey = config.MODEL_API_KEY;
|
|
5
|
+
const baseURL = config.MODEL_BASE_URL;
|
|
6
|
+
const model = config.MODEL_NAME ?? "gpt-4o-mini";
|
|
18
7
|
if (!apiKey) {
|
|
19
8
|
throw new Error("Missing MODEL_API_KEY / OPENAI_API_KEY in environment variables or ~/.my-agent/settings.json.");
|
|
20
9
|
}
|
|
@@ -3,6 +3,11 @@ export function resolveWorkspaceRoot(workspaceRoot) {
|
|
|
3
3
|
return path.resolve(workspaceRoot ?? process.cwd());
|
|
4
4
|
}
|
|
5
5
|
export function resolveSafePath(workspaceRoot, targetPath) {
|
|
6
|
+
// If the target is an absolute path, allow reading it directly (anywhere on disk)
|
|
7
|
+
if (path.isAbsolute(targetPath)) {
|
|
8
|
+
return path.resolve(targetPath);
|
|
9
|
+
}
|
|
10
|
+
// Relative paths are resolved against the workspace root
|
|
6
11
|
const root = path.resolve(workspaceRoot);
|
|
7
12
|
const fullPath = path.resolve(root, targetPath);
|
|
8
13
|
const relative = path.relative(root, fullPath);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from "node:path";
|
|
1
2
|
const DEFAULT_DENY_PATTERNS = [
|
|
2
3
|
/\brm\s+-rf\s+\//i,
|
|
3
4
|
/\bsudo\b/i,
|
|
@@ -22,6 +23,14 @@ const DEFAULT_ALLOW_PREFIXES = [
|
|
|
22
23
|
"npm",
|
|
23
24
|
"node",
|
|
24
25
|
"git",
|
|
26
|
+
"python3",
|
|
27
|
+
"python",
|
|
28
|
+
"pip3",
|
|
29
|
+
"pip",
|
|
30
|
+
"pdftotext",
|
|
31
|
+
"qpdf",
|
|
32
|
+
"pandoc",
|
|
33
|
+
"which",
|
|
25
34
|
];
|
|
26
35
|
export function validateShellCommand(command) {
|
|
27
36
|
const trimmed = command.trim();
|
|
@@ -34,7 +43,11 @@ export function validateShellCommand(command) {
|
|
|
34
43
|
}
|
|
35
44
|
}
|
|
36
45
|
const firstToken = trimmed.split(/\s+/)[0];
|
|
37
|
-
|
|
46
|
+
// Also allow absolute paths ending with known commands (e.g. /usr/bin/python3)
|
|
47
|
+
const commandName = firstToken.includes(path.sep)
|
|
48
|
+
? path.basename(firstToken)
|
|
49
|
+
: firstToken;
|
|
50
|
+
if (!DEFAULT_ALLOW_PREFIXES.includes(commandName)) {
|
|
38
51
|
throw new Error(`Shell command not allowed by policy. Command prefix: ${firstToken}`);
|
|
39
52
|
}
|
|
40
53
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { ensureSkillsDir } from "./paths.js";
|
|
4
|
+
export async function discoverSkillDirs() {
|
|
5
|
+
const skillsDir = await ensureSkillsDir();
|
|
6
|
+
const entries = await fs.readdir(skillsDir, { withFileTypes: true });
|
|
7
|
+
const result = [];
|
|
8
|
+
for (const entry of entries) {
|
|
9
|
+
if (!entry.isDirectory())
|
|
10
|
+
continue;
|
|
11
|
+
const dir = path.join(skillsDir, entry.name);
|
|
12
|
+
const skillFile = path.join(dir, "SKILL.md");
|
|
13
|
+
try {
|
|
14
|
+
const stat = await fs.stat(skillFile);
|
|
15
|
+
if (stat.isFile()) {
|
|
16
|
+
result.push({ dir, skillFile });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
// ignore dirs without SKILL.md
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
function tokenize(input) {
|
|
2
|
+
return input
|
|
3
|
+
.toLowerCase()
|
|
4
|
+
.split(/[\s,,。.!?!?::;;"'`“”‘’()(){}\[\]<>\/\\|+-]+/g)
|
|
5
|
+
.map((x) => x.trim())
|
|
6
|
+
.filter(Boolean);
|
|
7
|
+
}
|
|
8
|
+
function includesAny(text, keywords) {
|
|
9
|
+
const lower = text.toLowerCase();
|
|
10
|
+
return keywords.some((k) => lower.includes(k.toLowerCase()));
|
|
11
|
+
}
|
|
12
|
+
export function matchSkills(input, skills) {
|
|
13
|
+
const q = input.trim().toLowerCase();
|
|
14
|
+
if (!q)
|
|
15
|
+
return { matched: [] };
|
|
16
|
+
const tokens = tokenize(q);
|
|
17
|
+
const scored = [];
|
|
18
|
+
for (const skill of skills) {
|
|
19
|
+
let score = 0;
|
|
20
|
+
const name = skill.name.toLowerCase();
|
|
21
|
+
const desc = skill.description.toLowerCase();
|
|
22
|
+
const tags = skill.tags.map((t) => t.toLowerCase());
|
|
23
|
+
if (q.includes(name))
|
|
24
|
+
score += 10;
|
|
25
|
+
if (tokens.includes(name))
|
|
26
|
+
score += 8;
|
|
27
|
+
for (const tag of tags) {
|
|
28
|
+
if (q.includes(tag))
|
|
29
|
+
score += 6;
|
|
30
|
+
if (tokens.includes(tag))
|
|
31
|
+
score += 4;
|
|
32
|
+
}
|
|
33
|
+
const descKeywords = desc
|
|
34
|
+
.split(/[\s,,。.!?!?::;;"'`“”‘’()(){}\[\]<>\/\\|+-]+/g)
|
|
35
|
+
.map((x) => x.trim())
|
|
36
|
+
.filter((x) => x.length >= 3);
|
|
37
|
+
let descHitCount = 0;
|
|
38
|
+
for (const token of tokens) {
|
|
39
|
+
if (descKeywords.includes(token)) {
|
|
40
|
+
descHitCount += 1;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
score += Math.min(descHitCount, 3);
|
|
44
|
+
// 特判:pdf skill
|
|
45
|
+
if (name === "pdf") {
|
|
46
|
+
if (includesAny(q, [".pdf", "pdf", "ocr", "表单", "合并pdf", "拆分pdf", "提取pdf"])) {
|
|
47
|
+
score += 8;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (score > 0) {
|
|
51
|
+
scored.push({ skill, score });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
scored.sort((a, b) => b.score - a.score);
|
|
55
|
+
return {
|
|
56
|
+
matched: scored.slice(0, 3).map((x) => x.skill),
|
|
57
|
+
reason: scored.length ? `matched ${scored.length} skill(s)` : undefined,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
export function getYuanClawHomeDir() {
|
|
5
|
+
return path.join(os.homedir(), ".yuan-claw");
|
|
6
|
+
}
|
|
7
|
+
export function getSkillsDir() {
|
|
8
|
+
return path.join(getYuanClawHomeDir(), "skills");
|
|
9
|
+
}
|
|
10
|
+
export async function ensureSkillsDir() {
|
|
11
|
+
const dir = getSkillsDir();
|
|
12
|
+
await fs.mkdir(dir, { recursive: true });
|
|
13
|
+
return dir;
|
|
14
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function buildSkillsPrompt(skills) {
|
|
2
|
+
if (!skills.length)
|
|
3
|
+
return "";
|
|
4
|
+
const parts = skills.map((skill) => {
|
|
5
|
+
const lines = [];
|
|
6
|
+
lines.push(`Skill Name: ${skill.name}`);
|
|
7
|
+
if (skill.description)
|
|
8
|
+
lines.push(`Description: ${skill.description}`);
|
|
9
|
+
if (skill.tags.length)
|
|
10
|
+
lines.push(`Tags: ${skill.tags.join(", ")}`);
|
|
11
|
+
if (skill.license)
|
|
12
|
+
lines.push(`License: ${skill.license}`);
|
|
13
|
+
lines.push("");
|
|
14
|
+
lines.push("[SKILL CONTENT BEGIN]");
|
|
15
|
+
lines.push(skill.body);
|
|
16
|
+
lines.push("[SKILL CONTENT END]");
|
|
17
|
+
return lines.join("\n");
|
|
18
|
+
});
|
|
19
|
+
return [
|
|
20
|
+
"You have access to the following local skills.",
|
|
21
|
+
"Use them when they are relevant to the user's request.",
|
|
22
|
+
"If a skill contains procedures, best practices, or tool suggestions, follow them.",
|
|
23
|
+
"",
|
|
24
|
+
...parts,
|
|
25
|
+
].join("\n");
|
|
26
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { discoverSkillDirs } from "./discover.js";
|
|
4
|
+
import { parseSkillFile } from "./parse.js";
|
|
5
|
+
function normalizeTags(input) {
|
|
6
|
+
if (!Array.isArray(input))
|
|
7
|
+
return [];
|
|
8
|
+
return input
|
|
9
|
+
.filter((x) => typeof x === "string")
|
|
10
|
+
.map((x) => x.trim())
|
|
11
|
+
.filter(Boolean);
|
|
12
|
+
}
|
|
13
|
+
function fallbackNameFromDir(dir) {
|
|
14
|
+
return path.basename(dir);
|
|
15
|
+
}
|
|
16
|
+
export async function loadSkills() {
|
|
17
|
+
const discovered = await discoverSkillDirs();
|
|
18
|
+
const skills = [];
|
|
19
|
+
for (const item of discovered) {
|
|
20
|
+
try {
|
|
21
|
+
const raw = await fs.readFile(item.skillFile, "utf8");
|
|
22
|
+
const { meta, body } = parseSkillFile(raw);
|
|
23
|
+
const name = (meta.name?.trim() || fallbackNameFromDir(item.dir)).trim();
|
|
24
|
+
const description = (meta.description?.trim() || "").trim();
|
|
25
|
+
skills.push({
|
|
26
|
+
name,
|
|
27
|
+
description,
|
|
28
|
+
license: meta.license?.trim(),
|
|
29
|
+
version: meta.version?.trim(),
|
|
30
|
+
tags: normalizeTags(meta.tags),
|
|
31
|
+
dir: item.dir,
|
|
32
|
+
skillFile: item.skillFile,
|
|
33
|
+
body,
|
|
34
|
+
raw,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// ignore invalid skill files
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
skills.sort((a, b) => a.name.localeCompare(b.name));
|
|
42
|
+
return skills;
|
|
43
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { loadSkills } from "./registry.js";
|
|
2
|
+
import { matchSkills } from "./match.js";
|
|
3
|
+
import { buildSkillsPrompt } from "./prompt.js";
|
|
4
|
+
export class SkillsRuntime {
|
|
5
|
+
skills = [];
|
|
6
|
+
async reload() {
|
|
7
|
+
this.skills = await loadSkills();
|
|
8
|
+
}
|
|
9
|
+
list() {
|
|
10
|
+
return this.skills;
|
|
11
|
+
}
|
|
12
|
+
match(input) {
|
|
13
|
+
return matchSkills(input, this.skills).matched;
|
|
14
|
+
}
|
|
15
|
+
buildPromptForInput(input) {
|
|
16
|
+
const matched = this.match(input);
|
|
17
|
+
return buildSkillsPrompt(matched);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jiangyuan1209/yuan-claw",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "A local CLI agent with tools, search, and interactive approval.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"@clack/prompts": "^1.3.0",
|
|
28
28
|
"@mozilla/readability": "^0.6.0",
|
|
29
29
|
"dotenv": "^17.4.2",
|
|
30
|
+
"gray-matter": "^4.0.3",
|
|
30
31
|
"jsdom": "^29.0.2",
|
|
31
32
|
"openai": "^4.56.0",
|
|
32
33
|
"zod": "^3.23.8"
|
|
@@ -37,4 +38,4 @@
|
|
|
37
38
|
"tsx": "^4.19.1",
|
|
38
39
|
"typescript": "^5.6.2"
|
|
39
40
|
}
|
|
40
|
-
}
|
|
41
|
+
}
|