@itradingai/aiwiki 0.2.16 → 0.2.19

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
@@ -23,7 +23,7 @@ AIWiki 是一个开源的 Agent-first 本地 LLM-wiki CLI。
23
23
  用户给 URL / 正文 / 文件
24
24
  -> 宿主 Agent 读取内容并尽量生成 analysis / wiki_entry
25
25
  -> aiwiki ingest-agent --stdin
26
- -> AIWiki 写入 Raw / Source Card / Wiki Entry / Claim / Topic / Outline / Run Log
26
+ -> AIWiki 写入 Raw / Source Card / Wiki Entry / Run Summary;有明确内容或请求时再写 Claim / Topic / Outline / Asset
27
27
  ```
28
28
 
29
29
  ### Query:从 Wiki 调度知识
@@ -55,7 +55,7 @@ aiwiki lint
55
55
  我的知识库路径:F:\knowledges
56
56
 
57
57
  请检查 Node.js >=20,执行 aiwiki setup --path "我的知识库路径" --yes,
58
- 然后运行 aiwiki agent list / aiwiki agent install 完成宿主 Agent 对接。
58
+ 然后运行 aiwiki agent sync --yes aiwiki agent check --json 完成宿主 Agent 对接。
59
59
  最后执行 aiwiki doctor 和 aiwiki status,告诉我实际执行了哪些命令和还差什么手动步骤。
60
60
  ```
61
61
 
@@ -63,16 +63,15 @@ aiwiki lint
63
63
 
64
64
  ```bash
65
65
  npx @itradingai/aiwiki@latest setup
66
- aiwiki agent list
67
- aiwiki agent install
66
+ aiwiki agent sync --yes
67
+ aiwiki agent check --json
68
68
  ```
69
69
 
70
70
  ### 第二步:接入宿主 Agent
71
71
 
72
72
  ```bash
73
- aiwiki agent list
74
- aiwiki agent install
75
- aiwiki agent check
73
+ aiwiki agent sync --yes
74
+ aiwiki agent check --json
76
75
  ```
77
76
 
78
77
  也可以直接输出通用协议:
@@ -118,15 +117,11 @@ aiwiki query "xxx"
118
117
  ```text
119
118
  02-raw/articles/
120
119
  03-sources/article-cards/
121
- 04-claims/_suggestions/
122
120
  05-wiki/source-knowledge/
123
- 06-assets/_suggestions/
124
- 07-topics/ready/
125
- 08-outputs/outlines/
126
121
  09-runs/<run-id>/
127
122
  ```
128
123
 
129
- 其中 `05-wiki/source-knowledge/<slug>.md` 是默认 Wiki Entry。
124
+ 其中 `02-raw/articles/`、`03-sources/article-cards/`、`05-wiki/source-knowledge/` 和 `09-runs/<run-id>/` 是核心产物。`04-claims/_suggestions/`、`06-assets/_suggestions/`、`07-topics/ready/`、`08-outputs/outlines/` 只在 payload 有对应内容或 `request.outputs` 明确请求时生成。
130
125
 
131
126
  ### Agent-Enriched Wiki Entry
132
127
 
@@ -247,3 +242,37 @@ aiwiki lint --path "F:\knowledge_data\aiwiki-test"
247
242
  ## License
248
243
 
249
244
  MIT. See [LICENSE](LICENSE).
245
+
246
+ ## Query Filters
247
+
248
+ AIWiki retrieval is local Markdown/frontmatter search. It is intentionally lightweight: no vector search, no database, no external search, and no RAG-over-wiki.
249
+
250
+ ```bash
251
+ aiwiki context "AI Agent" --type wiki_entries --source-role input --wiki-type source_knowledge --status active --limit 5
252
+ aiwiki query "AI Agent" --type source_cards --status to-review --limit 3
253
+ ```
254
+
255
+ `context` returns Agent-readable JSON with `query_scope`, `result_quality`, `recommended_next_action`, `match_reasons`, `quality_signals`, and `related_refs`. `query` uses the same retrieval path and shows the match reasons and quality hints for humans.
256
+
257
+ ## Agent Skill Sync and Upgrade
258
+
259
+ AIWiki is Agent-first: after installing or upgrading the npm package, sync the packaged AIWiki skill into the local Agent environment.
260
+
261
+ First install and later upgrades use the same safe command:
262
+
263
+ ```bash
264
+ npm install -g @itradingai/aiwiki@latest
265
+ aiwiki agent sync --yes
266
+ aiwiki agent check
267
+ ```
268
+
269
+ For one Agent:
270
+
271
+ ```bash
272
+ aiwiki agent sync --agent codex --yes
273
+ aiwiki agent sync --agent claude --yes
274
+ ```
275
+
276
+ `agent sync` is idempotent. Missing targets are installed, current targets are left unchanged, and changed old skill files are backed up before overwrite. Use `--dry-run` to preview and `--json` when an AI Agent needs stable machine-readable status.
277
+
278
+ After sync, restart or reload the target Agent so it reads the new AIWiki skill. To roll back, copy the generated `.bak-<timestamp>` file back over the target skill file.
package/dist/src/app.js CHANGED
@@ -6,7 +6,7 @@ import { fileURLToPath } from "node:url";
6
6
  import { flagBool, flagString, parseArgs } from "./args.js";
7
7
  import { buildContext } from "./context.js";
8
8
  import { deriveFileTitle, ingestFile, ingestPayload } from "./ingest.js";
9
- import { filterLintReport, lintWorkspace, renderLintReport, renderLintSummary, writeLintReport } from "./lint.js";
9
+ import { attachAppliedSafeFixes, filterLintReport, lintWorkspace, removeEmptyOptionalDirs, renderLintReport, renderLintSummary, writeLintReport } from "./lint.js";
10
10
  import { CliError, writeLine } from "./output.js";
11
11
  import { confirmInit, directorySummary, doctor, exists, initWorkspace, promptForSetup, promptForInitPath, readConfig, resolveWorkspace, setDefaultWorkspace, statusSummary } from "./workspace.js";
12
12
  export async function runCli(argv, streams = { stdout: process.stdout, stderr: process.stderr }) {
@@ -17,6 +17,14 @@ export async function runCli(argv, streams = { stdout: process.stdout, stderr: p
17
17
  writeLine(streams.stdout, `aiwiki ${await packageVersion()}`);
18
18
  return 0;
19
19
  }
20
+ if (command === "agent" && (subcommand === "help" || args.flags.has("help"))) {
21
+ printAgentHelp(streams.stdout);
22
+ return 0;
23
+ }
24
+ if ((command === "context" || command === "query") && args.flags.has("help")) {
25
+ printContextHelp(streams.stdout);
26
+ return 0;
27
+ }
20
28
  if (args.flags.has("help") || !command || command === "help" || command === "-h") {
21
29
  printHelp(streams.stdout);
22
30
  return 0;
@@ -36,8 +44,8 @@ export async function runCli(argv, streams = { stdout: process.stdout, stderr: p
36
44
  writeLine(streams.stdout, `默认知识库: ${defaultConfig.defaultPath}`);
37
45
  writeLine(streams.stdout, `用户配置: ${defaultConfig.configPath}`);
38
46
  writeLine(streams.stdout, "Obsidian 入口: dashboards/AIWiki Home.md");
39
- writeLine(streams.stdout, "下一步: 运行 `aiwiki agent install`,把 AIWiki 安装到宿主 Agent");
40
- writeLine(streams.stdout, "Agent 设置完成后: Agent 发送 `入库 <url>`");
47
+ writeLine(streams.stdout, "下一步: 运行 `aiwiki agent sync --yes`,同步 AIWiki 宿主 Agent 对接文件。");
48
+ writeLine(streams.stdout, "Agent 设置完成后: 让宿主 Agent 提供正文并调用 `aiwiki ingest-agent --stdin`,或运行 `aiwiki ingest-file --file <file>`。");
41
49
  return 0;
42
50
  }
43
51
  if (command === "agent" && subcommand === "install") {
@@ -54,8 +62,24 @@ export async function runCli(argv, streams = { stdout: process.stdout, stderr: p
54
62
  }
55
63
  return 0;
56
64
  }
65
+ if (command === "agent" && subcommand === "sync") {
66
+ const result = await syncAgentSkills({
67
+ agentId: flagString(args, "agent"),
68
+ yes: flagBool(args, "yes"),
69
+ dryRun: flagBool(args, "dry-run"),
70
+ json: flagBool(args, "json"),
71
+ streams
72
+ });
73
+ if (flagBool(args, "json")) {
74
+ writeLine(streams.stdout, JSON.stringify(result, null, 2));
75
+ }
76
+ else {
77
+ printAgentSyncResult(streams.stdout, result);
78
+ }
79
+ return 0;
80
+ }
57
81
  if (command === "agent" && subcommand === "check") {
58
- await printAgentCheck(streams.stdout, await discoverAgentTargets());
82
+ await printAgentCheckDetailed(streams.stdout, await discoverAgentTargets(), flagBool(args, "json"));
59
83
  return 0;
60
84
  }
61
85
  if (command === "agent" && (subcommand === "list" || !subcommand)) {
@@ -137,7 +161,7 @@ export async function runCli(argv, streams = { stdout: process.stdout, stderr: p
137
161
  if (!query) {
138
162
  throw new CliError("请提供查询主题。");
139
163
  }
140
- writeLine(streams.stdout, JSON.stringify(await buildContext(root, query), null, 2));
164
+ writeLine(streams.stdout, JSON.stringify(await buildContext(root, query, contextOptions(args)), null, 2));
141
165
  return 0;
142
166
  }
143
167
  if (command === "query") {
@@ -146,7 +170,7 @@ export async function runCli(argv, streams = { stdout: process.stdout, stderr: p
146
170
  if (!query) {
147
171
  throw new CliError("请提供查询主题。");
148
172
  }
149
- writeLine(streams.stdout, renderQuery(await buildContext(root, query)));
173
+ writeLine(streams.stdout, renderQuery(await buildContext(root, query, contextOptions(args))));
150
174
  return 0;
151
175
  }
152
176
  if (command === "next") {
@@ -160,7 +184,8 @@ export async function runCli(argv, streams = { stdout: process.stdout, stderr: p
160
184
  if (command === "lint") {
161
185
  const root = await resolveWorkspace(flagString(args, "path"));
162
186
  const severity = parseLintSeverity(flagString(args, "severity"));
163
- const report = filterLintReport(await lintWorkspace(root), severity);
187
+ const appliedSafeFixes = flagBool(args, "fix-empty-dirs") ? await removeEmptyOptionalDirs(root) : [];
188
+ const report = filterLintReport(attachAppliedSafeFixes(await lintWorkspace(root), appliedSafeFixes), severity);
164
189
  if (flagBool(args, "json")) {
165
190
  writeLine(streams.stdout, JSON.stringify(report, null, 2));
166
191
  return 0;
@@ -244,23 +269,51 @@ function printHelp(stream) {
244
269
  writeLine(stream, "用法:");
245
270
  writeLine(stream, " aiwiki setup");
246
271
  writeLine(stream, " aiwiki setup --path <path> --yes");
247
- writeLine(stream, " aiwiki agent list");
248
- writeLine(stream, " aiwiki agent install");
249
- writeLine(stream, " aiwiki agent install --agent codex --yes");
250
- writeLine(stream, " aiwiki prompt agent");
272
+ writeLine(stream, " aiwiki agent sync --yes");
273
+ writeLine(stream, " aiwiki agent check --json");
274
+ writeLine(stream, " aiwiki ingest-agent --stdin");
275
+ writeLine(stream, " aiwiki ingest-file --file <file>");
251
276
  writeLine(stream, " aiwiki doctor");
252
277
  writeLine(stream, " aiwiki status");
253
278
  writeLine(stream, " aiwiki context <query>");
254
279
  writeLine(stream, " aiwiki query <query>");
255
- writeLine(stream, " aiwiki next");
256
280
  writeLine(stream, " aiwiki lint");
257
- writeLine(stream, " aiwiki ingest-agent --stdin");
258
- writeLine(stream, " aiwiki ingest-file --file <file>");
259
- writeLine(stream, " aiwiki init --path <path> --yes --set-default");
260
- writeLine(stream, " aiwiki config show");
261
- writeLine(stream, " aiwiki ingest-agent --payload <file>");
262
- writeLine(stream, " aiwiki ingest-url <url> --content-file <file>");
281
+ writeLine(stream, " aiwiki lint --fix-empty-dirs --json");
282
+ }
283
+ function printAgentHelp(stream) {
284
+ writeLine(stream, "AIWiki Agent commands");
285
+ writeLine(stream, "");
286
+ writeLine(stream, "Agent-first setup and upgrade:");
287
+ writeLine(stream, " aiwiki agent sync --yes");
288
+ writeLine(stream, " aiwiki agent sync --agent codex --yes");
289
+ writeLine(stream, " aiwiki agent sync --agent codex --dry-run");
290
+ writeLine(stream, " aiwiki agent sync --json --yes");
291
+ writeLine(stream, "");
292
+ writeLine(stream, "Status:");
263
293
  writeLine(stream, " aiwiki agent check");
294
+ writeLine(stream, " aiwiki agent check --json");
295
+ writeLine(stream, "");
296
+ writeLine(stream, "Compatibility:");
297
+ writeLine(stream, " aiwiki agent install --agent codex --yes");
298
+ writeLine(stream, " aiwiki agent install --agent codex --yes --force");
299
+ writeLine(stream, "");
300
+ writeLine(stream, "sync is idempotent: missing targets are installed, current targets are left unchanged, and different targets are backed up before overwrite.");
301
+ }
302
+ function printContextHelp(stream) {
303
+ writeLine(stream, "AIWiki context/query");
304
+ writeLine(stream, "");
305
+ writeLine(stream, "Local Markdown/frontmatter retrieval for host Agents and humans:");
306
+ writeLine(stream, " aiwiki context <topic> --limit 10");
307
+ writeLine(stream, " aiwiki query <topic> --type wiki_entries --status active");
308
+ writeLine(stream, "");
309
+ writeLine(stream, "Filters:");
310
+ writeLine(stream, " --type wiki_entries|source_cards|claims|topics|outlines|raw_refs");
311
+ writeLine(stream, " --source-role input|processing|output");
312
+ writeLine(stream, " --wiki-type source_knowledge|personal_knowledge");
313
+ writeLine(stream, " --status active|to-review|ready|draft");
314
+ writeLine(stream, " --limit <1-50>");
315
+ writeLine(stream, "");
316
+ writeLine(stream, "context JSON includes query_scope, result_quality, recommended_next_action, match_reasons, quality_signals, and related_refs.");
264
317
  }
265
318
  function parseLintSeverity(value) {
266
319
  if (value === undefined) {
@@ -362,6 +415,40 @@ async function printAgentCheck(stream, targets) {
362
415
  }
363
416
  }
364
417
  }
418
+ async function printAgentCheckDetailed(stream, targets, json = false) {
419
+ const checked = await Promise.all(targets.map(async (target) => ({
420
+ ...target,
421
+ state: await inspectAgentTarget(target),
422
+ installed: target.target ? await exists(target.target) : false
423
+ })));
424
+ if (json) {
425
+ writeLine(stream, JSON.stringify({
426
+ schema_version: "aiwiki.agent_check.v1",
427
+ generated_at: new Date().toISOString(),
428
+ targets: checked.map((target) => ({
429
+ id: target.id,
430
+ name: target.name,
431
+ detected: target.detected,
432
+ installable: target.installable,
433
+ installed: target.installed,
434
+ state: target.state,
435
+ source: target.source,
436
+ target: target.target
437
+ }))
438
+ }, null, 2));
439
+ return;
440
+ }
441
+ writeLine(stream, "AIWiki Agent check");
442
+ for (const target of checked) {
443
+ 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");
449
+ }
450
+ }
451
+ }
365
452
  async function installAgentSkill(options) {
366
453
  const targets = await discoverAgentTargets();
367
454
  const installable = targets.filter((target) => target.detected && target.installable);
@@ -400,8 +487,8 @@ async function installAgentSkill(options) {
400
487
  return undefined;
401
488
  }
402
489
  }
403
- await copyInstallFile(selected.source, selected.target, options.force);
404
- return selected;
490
+ const result = await copyInstallFileSafe(selected.source, selected.target, options.force);
491
+ return { ...selected, ...result };
405
492
  }
406
493
  async function askQuestion(streams, question) {
407
494
  if (!process.stdin.isTTY) {
@@ -415,7 +502,28 @@ async function askQuestion(streams, question) {
415
502
  rl.close();
416
503
  }
417
504
  }
505
+ async function syncAgentSkills(options) {
506
+ const targets = await discoverAgentTargets();
507
+ const selected = options.agentId ? targets.find((target) => target.id === options.agentId) : undefined;
508
+ if (!selected && options.agentId) {
509
+ throw new CliError(`未知宿主 Agent: ${options.agentId}`);
510
+ }
511
+ const syncTargets = selected ? [selected] : targets.filter((target) => target.detected && target.installable);
512
+ if (!syncTargets.length) {
513
+ throw new CliError("No detected installable Agent targets. Run aiwiki agent list or aiwiki prompt agent.");
514
+ }
515
+ if (!options.yes && !options.dryRun) {
516
+ throw new CliError("agent sync modifies Agent skill files. Re-run with --yes, or use --dry-run to preview.");
517
+ }
518
+ return {
519
+ schema_version: "aiwiki.agent_sync.v1",
520
+ generated_at: new Date().toISOString(),
521
+ dry_run: options.dryRun,
522
+ results: await Promise.all(syncTargets.map((target) => syncAgentTarget(target, options.dryRun)))
523
+ };
524
+ }
418
525
  async function copyInstallFile(source, target, force) {
526
+ return copyInstallFileSafe(source, target, force);
419
527
  await fs.access(source);
420
528
  if (!force && (await exists(target))) {
421
529
  throw new CliError(`目标文件已存在: ${target}。如需覆盖,请加 --force。`);
@@ -423,6 +531,84 @@ async function copyInstallFile(source, target, force) {
423
531
  await fs.mkdir(path.dirname(target), { recursive: true });
424
532
  await fs.copyFile(source, target);
425
533
  }
534
+ async function copyInstallFileSafe(source, target, force) {
535
+ await fs.access(source);
536
+ const targetExists = await exists(target);
537
+ if (!force && targetExists) {
538
+ throw new CliError(`目标文件已存在: ${target}。如需覆盖,请运行 aiwiki agent sync --agent <id> --yes,或为 install 加 --force。`);
539
+ }
540
+ if (targetExists && await sameFileContent(source, target)) {
541
+ return { action: "current" };
542
+ }
543
+ await fs.mkdir(path.dirname(target), { recursive: true });
544
+ const backupPath = targetExists ? await backupFile(target) : undefined;
545
+ await fs.copyFile(source, target);
546
+ return { action: targetExists ? "updated" : "installed", backupPath };
547
+ }
548
+ async function inspectAgentTarget(target) {
549
+ if (!target.installable || !target.source || !target.target) {
550
+ return "unsupported";
551
+ }
552
+ if (!(await exists(target.target))) {
553
+ return "missing";
554
+ }
555
+ return await sameFileContent(target.source, target.target) ? "current" : "different";
556
+ }
557
+ async function syncAgentTarget(target, dryRun) {
558
+ const state = await inspectAgentTarget(target);
559
+ const base = {
560
+ id: target.id,
561
+ name: target.name,
562
+ detected: target.detected,
563
+ installable: target.installable,
564
+ state,
565
+ target: target.target,
566
+ source: target.source,
567
+ changed: false,
568
+ dryRun
569
+ };
570
+ if (state === "unsupported" || !target.source || !target.target) {
571
+ return { ...base, action: "unsupported", note: target.note };
572
+ }
573
+ if (state === "current") {
574
+ return { ...base, action: "current" };
575
+ }
576
+ if (dryRun) {
577
+ return { ...base, action: state === "missing" ? "would_install" : "would_update", changed: true };
578
+ }
579
+ const result = await copyInstallFileSafe(target.source, target.target, true);
580
+ return { ...base, action: result.action, backupPath: result.backupPath, changed: result.action !== "current" };
581
+ }
582
+ async function sameFileContent(source, target) {
583
+ try {
584
+ const [sourceText, targetText] = await Promise.all([fs.readFile(source, "utf8"), fs.readFile(target, "utf8")]);
585
+ return sourceText === targetText;
586
+ }
587
+ catch {
588
+ return false;
589
+ }
590
+ }
591
+ async function backupFile(target) {
592
+ const parsed = path.parse(target);
593
+ const stamp = new Date().toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
594
+ const backupPath = path.join(parsed.dir, `${parsed.base}.bak-${stamp}`);
595
+ await fs.copyFile(target, backupPath);
596
+ return backupPath;
597
+ }
598
+ function printAgentSyncResult(stream, report) {
599
+ writeLine(stream, "AIWiki Agent sync");
600
+ writeLine(stream, `dry_run: ${report.dry_run ? "yes" : "no"}`);
601
+ for (const item of report.results) {
602
+ writeLine(stream, `${item.id}: ${item.name} | state=${item.state} | action=${item.action} | changed=${item.changed ? "yes" : "no"}`);
603
+ if (item.target) {
604
+ writeLine(stream, ` target: ${item.target}`);
605
+ }
606
+ if (item.backupPath) {
607
+ writeLine(stream, ` backup: ${item.backupPath}`);
608
+ }
609
+ }
610
+ writeLine(stream, "next: restart or reload the target Agent so it reads the synced AIWiki skill.");
611
+ }
426
612
  function printAgentPrompt(stream) {
427
613
  writeLine(stream, "AIWiki Agent 中文提示");
428
614
  writeLine(stream, "");
@@ -439,7 +625,7 @@ function printAgentPrompt(stream) {
439
625
  writeLine(stream, "回复措辞:成功时说“AIWiki 已完成入库,并生成 Wiki 条目。” 如果 wiki_entry_quality=scaffold,说明该条目只是可追溯脚手架,仍需宿主 Agent 后续补全。Dataview 是可选增强,不要替用户安装插件或修改 .obsidian。");
440
626
  writeLine(stream, "");
441
627
  writeLine(stream, "查询:当用户要求从 AIWiki 里了解某个主题时,调用 `aiwiki context <主题>`。");
442
- writeLine(stream, "整理:当用户要求检查或整理知识库时,调用 `aiwiki lint`。");
628
+ writeLine(stream, "整理:当用户要求检查或整理知识库时,先调用 `aiwiki lint --json`;若只有 safe fix 且用户允许整理,再调用 `aiwiki lint --fix-empty-dirs --json`,随后重跑 `aiwiki lint --json`。");
443
629
  writeLine(stream, "");
444
630
  writeLine(stream, "禁止:让用户保存 payload;让用户每次输入 --path;声称 AIWiki CLI 负责网页抓取;声称 AIWiki CLI 会在没有 Agent 分析字段时自动高质量总结。");
445
631
  }
@@ -508,7 +694,7 @@ async function printNext(stream, root, runCount, checks, targets, report) {
508
694
  if (runCount === 0) {
509
695
  writeLine(stream, "");
510
696
  writeLine(stream, "No ingest records yet.");
511
- writeLine(stream, "- aiwiki agent install");
697
+ writeLine(stream, "- aiwiki agent sync --yes");
512
698
  writeLine(stream, "- Then ask the host Agent to ingest a URL.");
513
699
  writeLine(stream, "- AIWiki CLI does not fetch webpages; the host Agent supplies content.");
514
700
  writeLine(stream, "- repair_order: empty_workspace");
@@ -535,12 +721,25 @@ function recommendedNextAction(runCount, lintStatus, hasMissingSystemFiles) {
535
721
  return "next_action: aiwiki lint";
536
722
  }
537
723
  if (runCount === 0) {
538
- return "next_action: aiwiki agent install";
724
+ return "next_action: aiwiki agent sync --yes";
539
725
  }
540
726
  return "next_action: aiwiki query <topic>";
541
727
  }
728
+ function contextOptions(args) {
729
+ const limit = flagString(args, "limit");
730
+ return {
731
+ filters: {
732
+ type: flagString(args, "type"),
733
+ source_role: flagString(args, "source-role"),
734
+ wiki_type: flagString(args, "wiki-type"),
735
+ status: flagString(args, "status")
736
+ },
737
+ limit: limit === undefined ? undefined : Number(limit)
738
+ };
739
+ }
542
740
  function renderQuery(context) {
543
741
  const lines = [`AIWiki 查询: ${context.query}`, ""];
742
+ lines.push(`结果质量: matches=${context.result_quality.total_matches}, best_score=${context.result_quality.best_score}, has_wiki_entry=${context.result_quality.has_wiki_entry ? "yes" : "no"}`, `下一步建议: ${context.recommended_next_action}`, `查询范围: groups=${context.query_scope.searched_groups.join(",") || "none"}, limit=${context.query_scope.limit}, filters=${JSON.stringify(context.query_scope.filters)}`, "");
544
743
  appendQueryGroup(lines, "Wiki 条目", context.matches.wiki_entries);
545
744
  appendQueryGroup(lines, "资料卡", context.matches.source_cards);
546
745
  appendQueryGroup(lines, "选题", context.matches.topics);
@@ -561,9 +760,13 @@ function appendQueryGroup(lines, label, items) {
561
760
  }
562
761
  for (const item of items.slice(0, 5)) {
563
762
  lines.push(`- ${item.title} (${item.path})`);
763
+ lines.push(` score=${item.score}; reasons=${item.match_reasons.join(",") || "unknown"}; quality=${item.quality_signals.join(",") || "none"}`);
564
764
  if (item.summary) {
565
765
  lines.push(` ${item.summary}`);
566
766
  }
767
+ if (item.related_refs.length) {
768
+ lines.push(` related: ${item.related_refs.slice(0, 5).join("; ")}`);
769
+ }
567
770
  if (item.warnings.length) {
568
771
  lines.push(` 提示: ${item.warnings.join(";")}`);
569
772
  }