@itradingai/aiwiki 0.2.20 → 0.2.22
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 +245 -172
- package/README.zh-CN.md +323 -0
- package/dist/src/app.js +120 -10
- package/docs/AGENT_HANDOFF.md +133 -177
- package/docs/AGENT_HANDOFF.zh-CN.md +150 -0
- package/docs/FAQ.md +80 -42
- package/docs/FAQ.zh-CN.md +103 -0
- package/docs/README.md +34 -23
- package/docs/README.zh-CN.md +34 -0
- package/docs/RELEASE.md +46 -16
- package/docs/RELEASE.zh-CN.md +84 -0
- package/docs/ROADMAP.md +51 -29
- package/docs/ROADMAP.zh-CN.md +65 -0
- package/docs/SHOWCASE.md +59 -45
- package/docs/SHOWCASE.zh-CN.md +81 -0
- package/docs/TRIAL_FEEDBACK_TEMPLATE.md +49 -0
- package/docs/USAGE.md +172 -358
- package/docs/USAGE.zh-CN.md +243 -0
- package/docs/development-log.md +80 -0
- package/package.json +11 -2
- package/skill/SKILL.md +33 -2
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/iTradingAI/aiwiki/main/docs/assets/aiwiki-hero.png" alt="AIWiki" width="100%" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="./README.md">English</a> |
|
|
7
|
+
<a href="./docs/README.zh-CN.md">中文文档</a> |
|
|
8
|
+
<a href="./docs/USAGE.zh-CN.md">使用指南</a> |
|
|
9
|
+
<a href="./docs/FAQ.zh-CN.md">常见问题</a> |
|
|
10
|
+
<a href="https://www.npmjs.com/package/@itradingai/aiwiki">npm</a>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
# AIWiki
|
|
14
|
+
|
|
15
|
+
[](https://www.npmjs.com/package/@itradingai/aiwiki)
|
|
16
|
+
[](https://nodejs.org/)
|
|
17
|
+
[](LICENSE)
|
|
18
|
+
|
|
19
|
+
**把 AI 助手读过的资料,变成以后可以查询、复用、整理的本地知识库。**
|
|
20
|
+
|
|
21
|
+
AIWiki 是给 AI 助手使用的本地 Markdown 知识库。
|
|
22
|
+
|
|
23
|
+
你把链接、文章、文件或笔记交给 AI 助手;AI 助手负责读取和理解;AIWiki 负责把结果写成结构化、可追踪、可复用的 Markdown 知识文件。
|
|
24
|
+
|
|
25
|
+
## 快速开始
|
|
26
|
+
|
|
27
|
+
先选一个本地文件夹作为 AIWiki 知识库。示例:
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
Windows: D:\AIWiki
|
|
31
|
+
macOS/Linux: ~/AIWiki
|
|
32
|
+
项目内测试: ./aiwiki-test
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
把下面这段复制给 Codex、Claude Code、QClaw、OpenClaw 或其他本地 AI 编程助手:
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
请帮我安装并配置 AIWiki。
|
|
39
|
+
|
|
40
|
+
请先检查 Node.js 是否已安装,并确认 node --version >=20。
|
|
41
|
+
如果没有安装 Node.js,或版本低于 20,请先停止,并告诉我如何升级,不要继续运行 npm install。
|
|
42
|
+
|
|
43
|
+
我的知识库路径是:
|
|
44
|
+
|
|
45
|
+
<替换成我的 AIWiki 知识库路径>
|
|
46
|
+
|
|
47
|
+
请运行这些命令:
|
|
48
|
+
|
|
49
|
+
npm install -g @itradingai/aiwiki@latest
|
|
50
|
+
aiwiki setup --path "<替换成我的 AIWiki 知识库路径>" --yes
|
|
51
|
+
aiwiki agent sync --yes
|
|
52
|
+
aiwiki agent sync --path "<替换成我的 AIWiki 知识库路径>" --yes
|
|
53
|
+
aiwiki agent check --json
|
|
54
|
+
aiwiki agent check --path "<替换成我的 AIWiki 知识库路径>" --json
|
|
55
|
+
aiwiki doctor --path "<替换成我的 AIWiki 知识库路径>"
|
|
56
|
+
aiwiki status --path "<替换成我的 AIWiki 知识库路径>"
|
|
57
|
+
|
|
58
|
+
最后请告诉我:
|
|
59
|
+
|
|
60
|
+
1. AIWiki 是否安装成功
|
|
61
|
+
2. 哪些 AI 助手目标已经同步
|
|
62
|
+
3. 知识库根目录指导是否已经写入
|
|
63
|
+
4. 我是否需要重启或重新加载 AI 助手
|
|
64
|
+
5. 下一步应该怎么用
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
AIWiki 有两层接入:
|
|
68
|
+
|
|
69
|
+
- `aiwiki agent sync --yes`:把 AIWiki 的 skill / command 同步到本机支持的 AI 助手环境。
|
|
70
|
+
- `aiwiki agent sync --path "<workspace>" --yes`:在知识库根目录写入指导,让以后进入这个目录的 Agent 也知道要优先使用 `aiwiki` 命令。
|
|
71
|
+
|
|
72
|
+
同步后,如果助手没有立刻识别 AIWiki,需要重启或重新加载助手。
|
|
73
|
+
|
|
74
|
+
### 安装成功后应该看到什么
|
|
75
|
+
|
|
76
|
+
完成后,AI 助手应该能确认:
|
|
77
|
+
|
|
78
|
+
- `aiwiki` 已安装,并能输出版本号
|
|
79
|
+
- 知识库路径已经创建,并通过 `aiwiki doctor`
|
|
80
|
+
- Agent 接入状态是 `installed`、`updated` 或 `current`
|
|
81
|
+
- `aiwiki agent sync --path` 已经写入知识库根指导
|
|
82
|
+
- `aiwiki status` 能返回当前知识库状态和下一步动作
|
|
83
|
+
|
|
84
|
+
## 第一次使用
|
|
85
|
+
|
|
86
|
+
### 入库资料
|
|
87
|
+
|
|
88
|
+
对 AI 助手说:
|
|
89
|
+
|
|
90
|
+
```text
|
|
91
|
+
把这个资料入库到 AIWiki:
|
|
92
|
+
<url>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
或者:
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
把这段笔记保存到 AIWiki:
|
|
99
|
+
<粘贴你的笔记>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
AI 助手读取资料后,会调用 AIWiki 写入本地知识库。
|
|
103
|
+
|
|
104
|
+
### 从知识库提问
|
|
105
|
+
|
|
106
|
+
对 AI 助手说:
|
|
107
|
+
|
|
108
|
+
```text
|
|
109
|
+
AIWiki 里关于 <主题> 有什么?
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
助手应该优先调用:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
aiwiki context "<主题>"
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
如果人在终端里直接查询,可以用:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
aiwiki query "<主题>"
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### 检查知识库
|
|
125
|
+
|
|
126
|
+
对 AI 助手说:
|
|
127
|
+
|
|
128
|
+
```text
|
|
129
|
+
帮我检查并整理 AIWiki 知识库。
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
助手应该先调用:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
aiwiki lint --json
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
如果只包含安全修复,并且你允许整理,可以继续调用:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
aiwiki lint --fix-empty-dirs --json
|
|
142
|
+
aiwiki lint --json
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## AIWiki 会生成什么
|
|
146
|
+
|
|
147
|
+
一次成功入库会生成一组可追踪的知识文件:
|
|
148
|
+
|
|
149
|
+
```text
|
|
150
|
+
02-raw/articles/ 原始资料记录
|
|
151
|
+
03-sources/article-cards/ 资料卡
|
|
152
|
+
05-wiki/source-knowledge/ 可复用 Wiki 条目
|
|
153
|
+
09-runs/<run-id>/ 本次处理记录
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
当 AI 助手提供了更丰富的结构化内容时,还可能生成:
|
|
157
|
+
|
|
158
|
+
```text
|
|
159
|
+
04-claims/_suggestions/ Claim 候选
|
|
160
|
+
06-assets/_suggestions/ 可复用素材或写作资产
|
|
161
|
+
07-topics/ready/ 选题候选
|
|
162
|
+
08-outputs/outlines/ 大纲草稿
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Wiki Entry 是主要复用层;Raw 和 Source Card 保留来源和追踪关系,方便以后回查。
|
|
166
|
+
|
|
167
|
+
可直接查看:
|
|
168
|
+
|
|
169
|
+
- [`examples/demo-run/`](examples/demo-run/)
|
|
170
|
+
- [`examples/obsidian-vault-sample/`](examples/obsidian-vault-sample/)
|
|
171
|
+
|
|
172
|
+
## AIWiki 解决什么问题
|
|
173
|
+
|
|
174
|
+
很多资料最后都死在三个地方:
|
|
175
|
+
|
|
176
|
+
- 收藏夹里,再也没有打开
|
|
177
|
+
- AI 聊天记录里,后面无法复用
|
|
178
|
+
- 笔记软件里,保存了但没有变成产出
|
|
179
|
+
|
|
180
|
+
AIWiki 的作用是让 AI 助手把读过的资料整理成本地 Markdown 知识库。
|
|
181
|
+
|
|
182
|
+
你不只是保存链接,而是在沉淀以后能查询、能复用、能持续整理的知识资产。
|
|
183
|
+
|
|
184
|
+
## 典型场景
|
|
185
|
+
|
|
186
|
+
- **阅读时顺手沉淀资料**:把文章发给 AI 助手,让 AIWiki 生成资料卡、Wiki 条目和处理记录。
|
|
187
|
+
- **为后续写作做准备**:当助手提供了足够结构化内容时,把观点、概念、选题和大纲素材一起沉淀下来。
|
|
188
|
+
- **从自己的资料库里追问**:围绕某个主题提问,让助手先从 AIWiki 取回本地上下文,再组织回答。
|
|
189
|
+
|
|
190
|
+
## 工作原理
|
|
191
|
+
|
|
192
|
+
```text
|
|
193
|
+
用户给 URL / 文件 / 笔记 / 正文
|
|
194
|
+
-> AI 助手读取并理解
|
|
195
|
+
-> AIWiki 写入结构化 Markdown 文件
|
|
196
|
+
-> 助手以后用 aiwiki context 取回上下文
|
|
197
|
+
-> aiwiki lint 检查结构和一致性
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
AIWiki 分清职责:
|
|
201
|
+
|
|
202
|
+
- AI 助手负责读取和理解资料
|
|
203
|
+
- AIWiki 负责写入、链接、查询和检查本地知识库
|
|
204
|
+
- Markdown 保持可读、可编辑、可迁移、可版本管理
|
|
205
|
+
|
|
206
|
+
## 灵感来源
|
|
207
|
+
|
|
208
|
+
AIWiki 受两类思路启发:
|
|
209
|
+
|
|
210
|
+
- **LLM Wiki**:把原始资料编译成一个持续维护的 Wiki,而不是每次提问都重新从原文找答案。
|
|
211
|
+
- **内容工作流**:好的资料不应该只停留在摘要里,还应该变成素材、选题、大纲和后续表达的积木。
|
|
212
|
+
|
|
213
|
+
AIWiki 不是简单拼接两套方法。
|
|
214
|
+
|
|
215
|
+
它把这些思路整理成一条更容易执行的 AI 助手工作流:
|
|
216
|
+
|
|
217
|
+
```text
|
|
218
|
+
资料
|
|
219
|
+
-> 资料卡
|
|
220
|
+
-> Wiki 条目
|
|
221
|
+
-> 可复用素材
|
|
222
|
+
-> 选题
|
|
223
|
+
-> 大纲
|
|
224
|
+
-> 后续创作 / 研究 / 决策
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Agent 接入
|
|
228
|
+
|
|
229
|
+
AIWiki 面向 AI 助手驱动的工作流。
|
|
230
|
+
|
|
231
|
+
目前支持自动同步的本地助手目标包括:
|
|
232
|
+
|
|
233
|
+
- Codex
|
|
234
|
+
- Claude Code
|
|
235
|
+
- QClaw
|
|
236
|
+
- OpenClaw
|
|
237
|
+
|
|
238
|
+
让 AI 助手运行:
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
aiwiki agent sync --yes
|
|
242
|
+
aiwiki agent sync --path "<workspace>" --yes
|
|
243
|
+
aiwiki agent check --json
|
|
244
|
+
aiwiki agent check --path "<workspace>" --json
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
不支持自动写入的宿主,可以输出通用协议:
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
aiwiki prompt agent
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
`npm install` 不会偷偷修改 AI 助手配置。同步是显式动作,可重复执行,并会在覆盖已变化的 skill 前创建备份。
|
|
254
|
+
|
|
255
|
+
## Obsidian / Dataview
|
|
256
|
+
|
|
257
|
+
AIWiki 写的是普通 Markdown 和 frontmatter。
|
|
258
|
+
|
|
259
|
+
Obsidian 是推荐查看界面,但不是硬依赖。Dataview 是可选 dashboard 增强。
|
|
260
|
+
|
|
261
|
+
AIWiki 不会自动安装 Dataview,也不会修改 `.obsidian`。
|
|
262
|
+
|
|
263
|
+
## 设计边界
|
|
264
|
+
|
|
265
|
+
AIWiki 不是:
|
|
266
|
+
|
|
267
|
+
- 网页爬虫
|
|
268
|
+
- 微信公众号读取器
|
|
269
|
+
- 浏览器插件
|
|
270
|
+
- 内置 LLM
|
|
271
|
+
- 向量数据库
|
|
272
|
+
- 所有 RAG 系统的替代品
|
|
273
|
+
- Obsidian 插件
|
|
274
|
+
- 默认人工审核队列
|
|
275
|
+
- 多知识库管理器
|
|
276
|
+
- RSS 或定时采集系统
|
|
277
|
+
|
|
278
|
+
AIWiki 接收 AI 助手已经读到的内容,并把它变成本地 Markdown 知识库。
|
|
279
|
+
|
|
280
|
+
## 社区
|
|
281
|
+
|
|
282
|
+
AIWiki 由 iTradingAI 开源维护。
|
|
283
|
+
|
|
284
|
+
中文用户可以扫码加入交流群,或关注公众号获取更新、案例和使用讨论。
|
|
285
|
+
|
|
286
|
+
| 微信交流群 | 公众号 |
|
|
287
|
+
| --- | --- |
|
|
288
|
+
|  |  |
|
|
289
|
+
|
|
290
|
+
## 文档
|
|
291
|
+
|
|
292
|
+
- [中文文档首页](docs/README.zh-CN.md)
|
|
293
|
+
- [使用指南](docs/USAGE.zh-CN.md)
|
|
294
|
+
- [Agent 接入说明](docs/AGENT_HANDOFF.zh-CN.md)
|
|
295
|
+
- [常见问题](docs/FAQ.zh-CN.md)
|
|
296
|
+
- [案例展示](docs/SHOWCASE.zh-CN.md)
|
|
297
|
+
- [路线图](docs/ROADMAP.zh-CN.md)
|
|
298
|
+
- [发布说明](docs/RELEASE.zh-CN.md)
|
|
299
|
+
|
|
300
|
+
## 本地开发
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
npm install
|
|
304
|
+
npm run build
|
|
305
|
+
npm test
|
|
306
|
+
npm link
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
使用临时知识库测试:
|
|
310
|
+
|
|
311
|
+
```bash
|
|
312
|
+
aiwiki setup --path "./aiwiki-test" --yes
|
|
313
|
+
aiwiki doctor --path "./aiwiki-test"
|
|
314
|
+
aiwiki status --path "./aiwiki-test"
|
|
315
|
+
aiwiki ingest-agent --payload tests/fixtures/agent_payload.url.valid.json --path "./aiwiki-test"
|
|
316
|
+
aiwiki context "AI Agent" --path "./aiwiki-test"
|
|
317
|
+
aiwiki query "AI Agent" --path "./aiwiki-test"
|
|
318
|
+
aiwiki lint --path "./aiwiki-test"
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## License
|
|
322
|
+
|
|
323
|
+
MIT. See [LICENSE](LICENSE).
|
package/dist/src/app.js
CHANGED
|
@@ -65,6 +65,7 @@ export async function runCli(argv, streams = { stdout: process.stdout, stderr: p
|
|
|
65
65
|
if (command === "agent" && subcommand === "sync") {
|
|
66
66
|
const result = await syncAgentSkills({
|
|
67
67
|
agentId: flagString(args, "agent"),
|
|
68
|
+
workspaceRoot: flagString(args, "path"),
|
|
68
69
|
yes: flagBool(args, "yes"),
|
|
69
70
|
dryRun: flagBool(args, "dry-run"),
|
|
70
71
|
json: flagBool(args, "json"),
|
|
@@ -79,7 +80,7 @@ export async function runCli(argv, streams = { stdout: process.stdout, stderr: p
|
|
|
79
80
|
return 0;
|
|
80
81
|
}
|
|
81
82
|
if (command === "agent" && subcommand === "check") {
|
|
82
|
-
await printAgentCheckDetailed(streams.stdout, await discoverAgentTargets(), flagBool(args, "json"));
|
|
83
|
+
await printAgentCheckDetailed(streams.stdout, await discoverAgentTargets(flagString(args, "path")), flagBool(args, "json"));
|
|
83
84
|
return 0;
|
|
84
85
|
}
|
|
85
86
|
if (command === "agent" && (subcommand === "list" || !subcommand)) {
|
|
@@ -287,10 +288,12 @@ function printAgentHelp(stream) {
|
|
|
287
288
|
writeLine(stream, " aiwiki agent sync --yes");
|
|
288
289
|
writeLine(stream, " aiwiki agent sync --agent codex --yes");
|
|
289
290
|
writeLine(stream, " aiwiki agent sync --agent codex --dry-run");
|
|
291
|
+
writeLine(stream, " aiwiki agent sync --path <workspace> --yes");
|
|
290
292
|
writeLine(stream, " aiwiki agent sync --json --yes");
|
|
291
293
|
writeLine(stream, "");
|
|
292
294
|
writeLine(stream, "Status:");
|
|
293
295
|
writeLine(stream, " aiwiki agent check");
|
|
296
|
+
writeLine(stream, " aiwiki agent check --path <workspace> --json");
|
|
294
297
|
writeLine(stream, " aiwiki agent check --json");
|
|
295
298
|
writeLine(stream, "");
|
|
296
299
|
writeLine(stream, "Compatibility:");
|
|
@@ -324,7 +327,21 @@ function parseLintSeverity(value) {
|
|
|
324
327
|
}
|
|
325
328
|
throw new CliError("lint --severity must be error, warning, or info");
|
|
326
329
|
}
|
|
327
|
-
|
|
330
|
+
const AIWIKI_AGENT_GUIDANCE_START = "<!-- AIWIKI:AGENT-GUIDANCE:START -->";
|
|
331
|
+
const AIWIKI_AGENT_GUIDANCE_END = "<!-- AIWIKI:AGENT-GUIDANCE:END -->";
|
|
332
|
+
const REQUIRED_AGENT_GUIDANCE_TERMS = [
|
|
333
|
+
"aiwiki setup",
|
|
334
|
+
"aiwiki agent sync",
|
|
335
|
+
"aiwiki agent check",
|
|
336
|
+
"aiwiki lint --json",
|
|
337
|
+
"aiwiki lint --fix-empty-dirs --json",
|
|
338
|
+
"aiwiki ingest-file",
|
|
339
|
+
"aiwiki ingest-agent",
|
|
340
|
+
"aiwiki status",
|
|
341
|
+
"aiwiki query",
|
|
342
|
+
"aiwiki context"
|
|
343
|
+
];
|
|
344
|
+
async function discoverAgentTargets(workspaceRoot) {
|
|
328
345
|
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
329
346
|
const skillSource = path.join(packageRoot, "skill", "SKILL.md");
|
|
330
347
|
const promptSource = path.join(packageRoot, "docs", "AGENT_HANDOFF.md");
|
|
@@ -334,7 +351,8 @@ async function discoverAgentTargets() {
|
|
|
334
351
|
const claudeHome = process.env.CLAUDE_HOME ? path.resolve(process.env.CLAUDE_HOME) : path.join(os.homedir(), ".claude");
|
|
335
352
|
const opencodeHome = process.env.OPENCODE_HOME ? path.resolve(process.env.OPENCODE_HOME) : path.join(os.homedir(), ".opencode");
|
|
336
353
|
const hermesHome = process.env.HERMES_HOME ? path.resolve(process.env.HERMES_HOME) : path.join(process.env.LOCALAPPDATA ?? path.join(os.homedir(), "AppData", "Local"), "hermes");
|
|
337
|
-
|
|
354
|
+
const workspace = workspaceRoot ? path.resolve(workspaceRoot) : undefined;
|
|
355
|
+
const targets = [
|
|
338
356
|
{
|
|
339
357
|
id: "codex",
|
|
340
358
|
name: "Codex",
|
|
@@ -392,6 +410,18 @@ async function discoverAgentTargets() {
|
|
|
392
410
|
note: "已检测到,但暂未确认稳定的 skill 目录。请先使用 aiwiki prompt agent。"
|
|
393
411
|
}
|
|
394
412
|
];
|
|
413
|
+
if (workspace) {
|
|
414
|
+
targets.unshift({
|
|
415
|
+
id: "workspace",
|
|
416
|
+
name: "Workspace AGENTS.md",
|
|
417
|
+
detected: await exists(workspace),
|
|
418
|
+
installable: true,
|
|
419
|
+
kind: "root_guidance",
|
|
420
|
+
target: path.join(workspace, "AGENTS.md"),
|
|
421
|
+
note: "安装 marker-bounded 根指导,要求宿主 Agent 在整理、检查、入库、查询、复用时优先调用 aiwiki CLI。"
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
return targets;
|
|
395
425
|
}
|
|
396
426
|
function printAgentList(stream, targets) {
|
|
397
427
|
writeLine(stream, "AIWiki 宿主 Agent 目标");
|
|
@@ -432,6 +462,7 @@ async function printAgentCheckDetailed(stream, targets, json = false) {
|
|
|
432
462
|
installable: target.installable,
|
|
433
463
|
installed: target.installed,
|
|
434
464
|
state: target.state,
|
|
465
|
+
suggested_action: suggestedAgentAction(target),
|
|
435
466
|
source: target.source,
|
|
436
467
|
target: target.target
|
|
437
468
|
}))
|
|
@@ -441,14 +472,21 @@ async function printAgentCheckDetailed(stream, targets, json = false) {
|
|
|
441
472
|
writeLine(stream, "AIWiki Agent check");
|
|
442
473
|
for (const target of checked) {
|
|
443
474
|
writeLine(stream, `${target.id}: ${target.name} | detected=${target.detected ? "yes" : "no"} | installed=${target.installed ? "yes" : "no"} | installable=${target.installable ? "yes" : "no"} | state=${target.state}`);
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
else if (target.detected && !target.installable) {
|
|
448
|
-
writeLine(stream, " suggested: aiwiki prompt agent");
|
|
475
|
+
const suggested = suggestedAgentAction(target);
|
|
476
|
+
if (suggested) {
|
|
477
|
+
writeLine(stream, ` suggested: ${suggested}`);
|
|
449
478
|
}
|
|
450
479
|
}
|
|
451
480
|
}
|
|
481
|
+
function suggestedAgentAction(target) {
|
|
482
|
+
if (target.detected && target.installable && (target.state === "missing" || target.state === "different")) {
|
|
483
|
+
return target.id === "workspace" ? `aiwiki agent sync --path "${path.dirname(target.target ?? ".")}" --yes` : `aiwiki agent sync --agent ${target.id} --yes`;
|
|
484
|
+
}
|
|
485
|
+
if (target.detected && !target.installable) {
|
|
486
|
+
return "aiwiki prompt agent";
|
|
487
|
+
}
|
|
488
|
+
return undefined;
|
|
489
|
+
}
|
|
452
490
|
async function installAgentSkill(options) {
|
|
453
491
|
const targets = await discoverAgentTargets();
|
|
454
492
|
const installable = targets.filter((target) => target.detected && target.installable);
|
|
@@ -503,7 +541,7 @@ async function askQuestion(streams, question) {
|
|
|
503
541
|
}
|
|
504
542
|
}
|
|
505
543
|
async function syncAgentSkills(options) {
|
|
506
|
-
const targets = await discoverAgentTargets();
|
|
544
|
+
const targets = await discoverAgentTargets(options.workspaceRoot);
|
|
507
545
|
const selected = options.agentId ? targets.find((target) => target.id === options.agentId) : undefined;
|
|
508
546
|
if (!selected && options.agentId) {
|
|
509
547
|
throw new CliError(`未知宿主 Agent: ${options.agentId}`);
|
|
@@ -546,6 +584,9 @@ async function copyInstallFileSafe(source, target, force) {
|
|
|
546
584
|
return { action: targetExists ? "updated" : "installed", backupPath };
|
|
547
585
|
}
|
|
548
586
|
async function inspectAgentTarget(target) {
|
|
587
|
+
if (target.kind === "root_guidance") {
|
|
588
|
+
return inspectWorkspaceGuidanceTarget(target);
|
|
589
|
+
}
|
|
549
590
|
if (!target.installable || !target.source || !target.target) {
|
|
550
591
|
return "unsupported";
|
|
551
592
|
}
|
|
@@ -567,7 +608,7 @@ async function syncAgentTarget(target, dryRun) {
|
|
|
567
608
|
changed: false,
|
|
568
609
|
dryRun
|
|
569
610
|
};
|
|
570
|
-
if (state === "unsupported" || !target.
|
|
611
|
+
if (state === "unsupported" || !target.target || (!target.source && target.kind !== "root_guidance")) {
|
|
571
612
|
return { ...base, action: "unsupported", note: target.note };
|
|
572
613
|
}
|
|
573
614
|
if (state === "current") {
|
|
@@ -576,9 +617,78 @@ async function syncAgentTarget(target, dryRun) {
|
|
|
576
617
|
if (dryRun) {
|
|
577
618
|
return { ...base, action: state === "missing" ? "would_install" : "would_update", changed: true };
|
|
578
619
|
}
|
|
620
|
+
if (target.kind === "root_guidance") {
|
|
621
|
+
const result = await syncWorkspaceGuidanceTarget(target);
|
|
622
|
+
return { ...base, action: result.action, backupPath: result.backupPath, changed: result.action !== "current" };
|
|
623
|
+
}
|
|
579
624
|
const result = await copyInstallFileSafe(target.source, target.target, true);
|
|
580
625
|
return { ...base, action: result.action, backupPath: result.backupPath, changed: result.action !== "current" };
|
|
581
626
|
}
|
|
627
|
+
async function inspectWorkspaceGuidanceTarget(target) {
|
|
628
|
+
if (!target.target || !target.detected) {
|
|
629
|
+
return "missing";
|
|
630
|
+
}
|
|
631
|
+
if (!(await exists(target.target))) {
|
|
632
|
+
return "missing";
|
|
633
|
+
}
|
|
634
|
+
const content = await fs.readFile(target.target, "utf8");
|
|
635
|
+
const block = extractWorkspaceGuidanceBlock(content);
|
|
636
|
+
if (!block) {
|
|
637
|
+
return "different";
|
|
638
|
+
}
|
|
639
|
+
return block.trim() === workspaceGuidanceBlock().trim() && REQUIRED_AGENT_GUIDANCE_TERMS.every((term) => block.includes(term)) ? "current" : "different";
|
|
640
|
+
}
|
|
641
|
+
async function syncWorkspaceGuidanceTarget(target) {
|
|
642
|
+
if (!target.target) {
|
|
643
|
+
return { action: "unsupported" };
|
|
644
|
+
}
|
|
645
|
+
const targetExists = await exists(target.target);
|
|
646
|
+
const existing = targetExists ? await fs.readFile(target.target, "utf8") : "";
|
|
647
|
+
const next = mergeWorkspaceGuidance(existing);
|
|
648
|
+
if (targetExists && existing === next) {
|
|
649
|
+
return { action: "current" };
|
|
650
|
+
}
|
|
651
|
+
const backupPath = targetExists ? await backupFile(target.target) : undefined;
|
|
652
|
+
await fs.mkdir(path.dirname(target.target), { recursive: true });
|
|
653
|
+
await fs.writeFile(target.target, next, "utf8");
|
|
654
|
+
return { action: targetExists ? "updated" : "installed", backupPath };
|
|
655
|
+
}
|
|
656
|
+
function extractWorkspaceGuidanceBlock(content) {
|
|
657
|
+
const start = content.indexOf(AIWIKI_AGENT_GUIDANCE_START);
|
|
658
|
+
const end = content.indexOf(AIWIKI_AGENT_GUIDANCE_END);
|
|
659
|
+
if (start === -1 || end === -1 || end < start) {
|
|
660
|
+
return undefined;
|
|
661
|
+
}
|
|
662
|
+
return content.slice(start, end + AIWIKI_AGENT_GUIDANCE_END.length);
|
|
663
|
+
}
|
|
664
|
+
function mergeWorkspaceGuidance(existing) {
|
|
665
|
+
const block = workspaceGuidanceBlock();
|
|
666
|
+
const start = existing.indexOf(AIWIKI_AGENT_GUIDANCE_START);
|
|
667
|
+
const end = existing.indexOf(AIWIKI_AGENT_GUIDANCE_END);
|
|
668
|
+
if (start !== -1 && end !== -1 && end > start) {
|
|
669
|
+
return `${existing.slice(0, start)}${block}${existing.slice(end + AIWIKI_AGENT_GUIDANCE_END.length)}`;
|
|
670
|
+
}
|
|
671
|
+
const trimmed = existing.trimEnd();
|
|
672
|
+
return `${trimmed}${trimmed ? "\n\n" : ""}${block}\n`;
|
|
673
|
+
}
|
|
674
|
+
function workspaceGuidanceBlock() {
|
|
675
|
+
return `${AIWIKI_AGENT_GUIDANCE_START}
|
|
676
|
+
# AIWiki Agent Command Contract
|
|
677
|
+
|
|
678
|
+
When a user asks to organize, inspect, ingest, query, reuse, or maintain this AIWiki workspace, call the AIWiki CLI first. Do not start with generic file search, grep/find scans, or ad hoc note edits unless the AIWiki command cannot answer the request.
|
|
679
|
+
|
|
680
|
+
Required command-first loop:
|
|
681
|
+
|
|
682
|
+
1. Ensure the workspace exists with \`aiwiki setup --path <workspace> --yes\`.
|
|
683
|
+
2. Keep host-Agent guidance current with \`aiwiki agent sync --path <workspace> --yes\` and verify with \`aiwiki agent check --path <workspace> --json\`.
|
|
684
|
+
3. Inspect structure with \`aiwiki lint --json --path <workspace>\`; apply only safe fixes with \`aiwiki lint --fix-empty-dirs --json --path <workspace>\` when allowed, then rerun \`aiwiki lint --json --path <workspace>\`.
|
|
685
|
+
4. Ingest local material with \`aiwiki ingest-file --file <file> --path <workspace>\` or structured Agent material with \`aiwiki ingest-agent --stdin --path <workspace>\`.
|
|
686
|
+
5. Check progress with \`aiwiki status --path <workspace>\`.
|
|
687
|
+
6. Retrieve reusable knowledge with \`aiwiki query <topic> --path <workspace>\` for human-readable output or \`aiwiki context <topic> --path <workspace>\` for Agent JSON.
|
|
688
|
+
|
|
689
|
+
Use fallback shell/file search only after the relevant AIWiki command has been tried or when the command is unavailable. If you fall back, say which AIWiki command was insufficient and why.
|
|
690
|
+
${AIWIKI_AGENT_GUIDANCE_END}`;
|
|
691
|
+
}
|
|
582
692
|
async function sameFileContent(source, target) {
|
|
583
693
|
try {
|
|
584
694
|
const [sourceText, targetText] = await Promise.all([fs.readFile(source, "utf8"), fs.readFile(target, "utf8")]);
|