@itradingai/aiwiki 0.2.20 → 0.2.21

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 CHANGED
@@ -64,7 +64,9 @@ aiwiki lint
64
64
  我的知识库路径:F:\knowledges
65
65
 
66
66
  请检查 Node.js >=20,执行 aiwiki setup --path "我的知识库路径" --yes,
67
- 然后运行 aiwiki agent sync --yesaiwiki agent check --json 完成宿主 Agent 对接。
67
+ 然后运行 aiwiki agent sync --yesaiwiki agent check --json
68
+ aiwiki agent sync --path "我的知识库路径" --yes 和 aiwiki agent check --path "我的知识库路径" --json,
69
+ 完成宿主 Agent 与知识库根指导对接。
68
70
  最后执行 aiwiki doctor 和 aiwiki status,告诉我实际执行了哪些命令和还差什么手动步骤。
69
71
  ```
70
72
 
@@ -81,6 +83,8 @@ aiwiki agent check --json
81
83
  ```bash
82
84
  aiwiki agent sync --yes
83
85
  aiwiki agent check --json
86
+ aiwiki agent sync --path "F:\knowledges" --yes
87
+ aiwiki agent check --path "F:\knowledges" --json
84
88
  ```
85
89
 
86
90
  也可以直接输出通用协议:
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
- async function discoverAgentTargets() {
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
- return [
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
- if (target.detected && target.installable && (target.state === "missing" || target.state === "different")) {
445
- writeLine(stream, ` suggested: aiwiki agent sync --agent ${target.id} --yes`);
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.source || !target.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")]);
@@ -24,6 +24,32 @@ AIWiki CLI 不做通用网页抓取,也不调用 LLM。网页读取失败时
24
24
 
25
25
  证据和疑似风险要分开:`source_quote` 等能回到原文的字段是宿主提供的 evidence/provenance;`coverage_suspected_incomplete`、`unsupported_claims`、`needs_review` 是 AIWiki 的复核提示,不代表已经证明遗漏或错误。
26
26
 
27
+ ## 命令优先契约
28
+
29
+ 宿主 Agent 处理 AIWiki 任务时,必须先调用 AIWiki 自带命令,再考虑通用文件搜索或临时脚本。否则只是绕过了知识库能力,用户看不到 lint、status、query/context 和入库记录的价值。
30
+
31
+ 项目或知识库根目录先同步根指导:
32
+
33
+ ```bash
34
+ aiwiki agent sync --path <workspace> --yes
35
+ aiwiki agent check --path <workspace> --json
36
+ ```
37
+
38
+ 日常闭环按这个顺序执行:
39
+
40
+ ```bash
41
+ aiwiki setup --path <workspace> --yes
42
+ aiwiki lint --json --path <workspace>
43
+ aiwiki lint --fix-empty-dirs --json --path <workspace>
44
+ aiwiki ingest-file --file <file> --path <workspace>
45
+ aiwiki ingest-agent --stdin --path <workspace>
46
+ aiwiki status --path <workspace>
47
+ aiwiki query <topic> --path <workspace>
48
+ aiwiki context <topic> --path <workspace>
49
+ ```
50
+
51
+ 只有当对应 AIWiki 命令无法回答问题时,才退回到 `rg`、`find`、`grep` 或手工读取文件;退回时要说明哪个 AIWiki 命令不够用。
52
+
27
53
  ## 标准流程
28
54
 
29
55
  1. 读取用户给的 URL、正文、附件或消息。
package/docs/FAQ.md CHANGED
@@ -55,6 +55,8 @@ AIWiki 是一个开源的 Agent-first 本地 LLM-wiki CLI。宿主 Agent 负责
55
55
  npx @itradingai/aiwiki@latest setup
56
56
  aiwiki agent sync --yes
57
57
  aiwiki agent check --json
58
+ aiwiki agent sync --path <workspace> --yes
59
+ aiwiki agent check --path <workspace> --json
58
60
  ```
59
61
 
60
62
  然后去看:
@@ -84,3 +86,15 @@ aiwiki lint
84
86
  ```
85
87
 
86
88
  如果只是清理旧版本留下的空可选增强目录,可以让 Agent 先运行 `aiwiki lint --json`,确认只有 safe fix 后再运行 `aiwiki lint --fix-empty-dirs --json`。
89
+
90
+ ## 为什么我的 Agent 还是在直接翻文件?
91
+
92
+ 通常是它没有加载 AIWiki skill,或者知识库根目录缺少 AIWiki 的 `AGENTS.md` 指导。先运行:
93
+
94
+ ```bash
95
+ aiwiki agent sync --yes
96
+ aiwiki agent sync --path <workspace> --yes
97
+ aiwiki agent check --path <workspace> --json
98
+ ```
99
+
100
+ 之后再让 Agent 处理 AIWiki 任务。它应该优先调用 `aiwiki lint --json`、`aiwiki status`、`aiwiki query`、`aiwiki context`、`aiwiki ingest-file` 或 `aiwiki ingest-agent`。只有这些命令不够用时,才退回到通用文件搜索。
package/docs/USAGE.md CHANGED
@@ -61,8 +61,12 @@ aiwiki status
61
61
  ```bash
62
62
  aiwiki agent sync --yes
63
63
  aiwiki agent check --json
64
+ aiwiki agent sync --path <workspace> --yes
65
+ aiwiki agent check --path <workspace> --json
64
66
  ```
65
67
 
68
+ 前两条同步本机宿主 Agent 的 skill/command,后两条在知识库根目录写入或刷新 marker-bounded `AGENTS.md` 指导。这样新的宿主 Agent 进入项目时,会先看到“必须调用 aiwiki CLI”的约束,而不是直接走默认文件搜索流程。
69
+
66
70
  旧的安装向导仍然兼容,但不再作为首选路径:
67
71
 
68
72
  ```bash
@@ -93,7 +97,9 @@ aiwiki prompt agent
93
97
 
94
98
  把输出内容安装成宿主 Agent 的 skill,或粘贴到宿主 Agent 的项目/会话说明里。不同 Agent 的安装入口不同,所以 AIWiki 提供自动安装向导和通用协议两条路径。
95
99
 
96
- `aiwiki agent check --json` 用来确认本机检测到哪些宿主 Agent、哪些已经安装 AIWiki 对接文件、哪些还需要运行 `aiwiki agent sync --agent <id> --yes`。
100
+ `aiwiki agent check --json` 用来确认本机检测到哪些宿主 Agent、哪些已经安装 AIWiki 对接文件、哪些还需要运行 `aiwiki agent sync --agent <id> --yes`。`aiwiki agent check --path <workspace> --json` 会同时检查知识库根目录的 `AGENTS.md` 是否包含当前 AIWiki 命令优先指导,缺失或过期时会提示运行 `aiwiki agent sync --path <workspace> --yes`。
101
+
102
+ 如果宿主 Agent 只用 `rg`、`find`、`grep` 或手工读文件来回答 AIWiki 问题,而没有先运行 `aiwiki lint --json`、`aiwiki status`、`aiwiki query` 或 `aiwiki context`,就说明它没有真正使用 AIWiki 的知识库闭环。
97
103
 
98
104
  ## 3. 日常使用
99
105
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itradingai/aiwiki",
3
- "version": "0.2.20",
3
+ "version": "0.2.21",
4
4
  "type": "module",
5
5
  "description": "Agent-first AI knowledge base CLI for turning articles, links and notes into Obsidian-ready source cards, topics, outlines and reusable knowledge assets.",
6
6
  "license": "MIT",
package/skill/SKILL.md CHANGED
@@ -3,7 +3,7 @@ name: aiwiki
3
3
  description: Agent-first AIWiki workflow for turning one URL/body into local Wiki knowledge files.
4
4
  ---
5
5
 
6
- <!-- aiwiki-skill-version: 0.2.19 -->
6
+ <!-- aiwiki-skill-version: 0.2.21 -->
7
7
 
8
8
  # AIWiki Skill
9
9
 
@@ -34,15 +34,42 @@ aiwiki agent sync --json --yes
34
34
  aiwiki agent check --json
35
35
  ```
36
36
 
37
+ For a project or vault workspace, also sync root guidance so future Agents see the command contract before they inspect files:
38
+
39
+ ```bash
40
+ aiwiki agent sync --path <workspace> --yes
41
+ aiwiki agent check --path <workspace> --json
42
+ ```
43
+
37
44
  Sync behavior:
38
45
 
39
46
  - missing installed skill: install the packaged AIWiki skill
40
47
  - current installed skill: leave unchanged
41
48
  - different installed skill: backup the old file, then overwrite with the packaged skill
49
+ - workspace `AGENTS.md`: append or refresh the marker-bounded AIWiki block without removing user instructions
42
50
  - unsupported host: do not write; use `aiwiki prompt agent` as a manual fallback
43
51
 
44
52
  After sync, tell the user the target path, any backup path, and that the target Agent may need to restart or reload before the new skill is active. Never edit Agent config during `npm install`; sync is the explicit safe step.
45
53
 
54
+ ## Required Command-First Loop
55
+
56
+ When the user asks you to organize, inspect, ingest, query, reuse, or maintain an AIWiki workspace, run the AIWiki command surface before generic file search or ad hoc note edits:
57
+
58
+ ```bash
59
+ aiwiki setup --path <workspace> --yes
60
+ aiwiki agent sync --path <workspace> --yes
61
+ aiwiki agent check --path <workspace> --json
62
+ aiwiki lint --json --path <workspace>
63
+ aiwiki lint --fix-empty-dirs --json --path <workspace>
64
+ aiwiki ingest-file --file <file> --path <workspace>
65
+ aiwiki ingest-agent --stdin --path <workspace>
66
+ aiwiki status --path <workspace>
67
+ aiwiki query <topic> --path <workspace>
68
+ aiwiki context <topic> --path <workspace>
69
+ ```
70
+
71
+ Use fallback shell/file search only after the relevant AIWiki command has been tried or when the command is unavailable. If you fall back, explain which AIWiki command was insufficient and why. If you skip the AIWiki commands entirely, the knowledge-base features are not being exercised.
72
+
46
73
  ## Knowledge Base Purpose
47
74
 
48
75
  Before ingesting, querying, linting, or reorganizing material, read `_system/purpose.md` in the target AIWiki workspace when it exists. Treat it as the local contract for: