@itradingai/aiwiki 0.2.14 → 0.2.15

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/dist/src/app.js CHANGED
@@ -430,18 +430,29 @@ function printAgentPrompt(stream) {
430
430
  }
431
431
  async function printStatusDetails(stream, root, runCount) {
432
432
  const counts = await contentCounts(root);
433
+ const summary = await statusSummary(root);
433
434
  const lintPath = path.join(root, "dashboards", "Lint Report.md");
434
435
  writeLine(stream, "");
435
- writeLine(stream, "内容统计:");
436
- writeLine(stream, `Wiki 条目: ${counts.wikiEntries}`);
437
- writeLine(stream, `资料卡: ${counts.sourceCards}`);
438
- writeLine(stream, `原文: ${counts.rawFiles}`);
439
- writeLine(stream, `选题: ${counts.topics}`);
440
- writeLine(stream, `大纲: ${counts.outlines}`);
441
- writeLine(stream, `最近 lint: ${await exists(lintPath) ? await relativeMtime(root, lintPath) : "无"}`);
436
+ writeLine(stream, "Content stats:");
437
+ writeLine(stream, `Wiki entries: ${counts.wikiEntries}`);
438
+ writeLine(stream, `Source cards: ${counts.sourceCards}`);
439
+ writeLine(stream, `Raw files: ${counts.rawFiles}`);
440
+ writeLine(stream, `Topics: ${counts.topics}`);
441
+ writeLine(stream, `Outlines: ${counts.outlines}`);
442
+ writeLine(stream, `fallback_entries: ${summary.fallbackCount}`);
443
+ writeLine(stream, `grounding_review_entries: ${summary.groundingReviewCount}`);
444
+ writeLine(stream, `recent_lint: ${await exists(lintPath) ? await relativeMtime(root, lintPath) : "none"}`);
445
+ writeLine(stream, `lint_status: ${summary.lintStatus}`);
446
+ if (summary.lastSuccessRunId) {
447
+ writeLine(stream, `last_success: ${summary.lastSuccessRunId}`);
448
+ }
449
+ if (summary.lastFailureRunId) {
450
+ writeLine(stream, `last_failure: ${summary.lastFailureRunId}`);
451
+ }
452
+ writeLine(stream, `system_files: ${summary.systemFiles.map((item) => `${item.path}=${item.status}`).join(", ")}`);
442
453
  writeLine(stream, "");
443
- writeLine(stream, "下一步建议:");
444
- writeLine(stream, runCount === 0 ? "运行 `aiwiki agent install` 接入宿主 Agent,然后发送 `入库 <url>`。" : "运行 `aiwiki query <主题>` 查询知识,或运行 `aiwiki lint` 检查结构。");
454
+ writeLine(stream, "Next action:");
455
+ writeLine(stream, recommendedNextAction(runCount, summary.lintStatus, summary.systemFiles.some((item) => item.status !== "ok")));
445
456
  }
446
457
  async function printNext(stream, root, runCount, checks, targets, report) {
447
458
  const missing = checks.filter((check) => check.status !== "ok");
@@ -452,42 +463,67 @@ async function printNext(stream, root, runCount, checks, targets, report) {
452
463
  }
453
464
  }
454
465
  writeLine(stream, "AIWiki 下一步建议");
455
- writeLine(stream, `知识库路径: ${root}`);
466
+ writeLine(stream, `workspace: ${root}`);
456
467
  if (missing.length) {
457
468
  writeLine(stream, "");
458
- writeLine(stream, "先修复知识库结构:");
469
+ writeLine(stream, "Repair workspace structure first:");
459
470
  writeLine(stream, `- aiwiki setup --path "${root}" --yes`);
471
+ writeLine(stream, "- repair_order: structure");
460
472
  return;
461
473
  }
462
- if (runCount === 0) {
474
+ const actionableIssues = report?.issues.filter((issue) => issue.severity !== "info") ?? [];
475
+ const errorCount = actionableIssues.filter((issue) => issue.severity === "error").length;
476
+ const warningCount = actionableIssues.filter((issue) => issue.severity === "warning").length;
477
+ if (errorCount > 0) {
463
478
  writeLine(stream, "");
464
- writeLine(stream, "还没有入库记录。");
465
- writeLine(stream, "- aiwiki agent install");
466
- writeLine(stream, "- 然后向宿主 Agent 发送 `入库 <url>`");
467
- writeLine(stream, "- CLI 不抓网页;网页正文由宿主 Agent 提供。");
479
+ writeLine(stream, `结构检查发现 ${errorCount} 个 error 问题。`);
480
+ writeLine(stream, "- aiwiki lint");
481
+ writeLine(stream, "- report: dashboards/Lint Report.md");
482
+ writeLine(stream, "- repair_order: lint_errors");
468
483
  return;
469
484
  }
470
- const actionableIssues = report?.issues.filter((issue) => issue.severity !== "info") ?? [];
471
- if (actionableIssues.length) {
485
+ if (warningCount > 0) {
472
486
  writeLine(stream, "");
473
- writeLine(stream, `结构检查发现 ${actionableIssues.length} 个需要处理的问题。`);
487
+ writeLine(stream, `结构检查发现 ${warningCount} 个 warning 问题。`);
474
488
  writeLine(stream, "- aiwiki lint");
475
- writeLine(stream, `- 查看报告: dashboards/Lint Report.md`);
476
- writeLine(stream, "- 先处理 error / warning,再继续扩展查询或入库。");
489
+ writeLine(stream, "- report: dashboards/Lint Report.md");
490
+ writeLine(stream, "- repair_order: lint_warnings");
491
+ return;
492
+ }
493
+ if (runCount === 0) {
494
+ writeLine(stream, "");
495
+ writeLine(stream, "No ingest records yet.");
496
+ writeLine(stream, "- aiwiki agent install");
497
+ writeLine(stream, "- Then ask the host Agent to ingest a URL.");
498
+ writeLine(stream, "- AIWiki CLI does not fetch webpages; the host Agent supplies content.");
499
+ writeLine(stream, "- repair_order: empty_workspace");
477
500
  return;
478
501
  }
479
502
  writeLine(stream, "");
480
- writeLine(stream, "已有入库记录,可以继续:");
481
- writeLine(stream, "- aiwiki query <主题>");
503
+ writeLine(stream, "Workspace is healthy enough for retrieval:");
504
+ writeLine(stream, "- aiwiki query <topic>");
482
505
  writeLine(stream, "- aiwiki lint");
506
+ writeLine(stream, "- repair_order: healthy_query");
483
507
  if (installableMissing.length) {
484
508
  writeLine(stream, "");
485
- writeLine(stream, "可补充宿主 Agent 接入:");
509
+ writeLine(stream, "Optional host Agent setup:");
486
510
  for (const target of installableMissing) {
487
511
  writeLine(stream, `- aiwiki agent install --agent ${target.id} --yes`);
488
512
  }
489
513
  }
490
514
  }
515
+ function recommendedNextAction(runCount, lintStatus, hasMissingSystemFiles) {
516
+ if (hasMissingSystemFiles) {
517
+ return "next_action: aiwiki setup --path <workspace> --yes";
518
+ }
519
+ if (lintStatus === "needs_attention") {
520
+ return "next_action: aiwiki lint";
521
+ }
522
+ if (runCount === 0) {
523
+ return "next_action: aiwiki agent install";
524
+ }
525
+ return "next_action: aiwiki query <topic>";
526
+ }
491
527
  function renderQuery(context) {
492
528
  const lines = [`AIWiki 查询: ${context.query}`, ""];
493
529
  appendQueryGroup(lines, "Wiki 条目", context.matches.wiki_entries);
@@ -3,7 +3,9 @@ import os from "node:os";
3
3
  import path from "node:path";
4
4
  import { createInterface } from "node:readline/promises";
5
5
  import { stdin as input, stdout as output } from "node:process";
6
+ import { frontmatterBoolean, frontmatterString, parseMarkdown } from "./frontmatter.js";
6
7
  import { CliError } from "./output.js";
8
+ import { relativePath } from "./paths.js";
7
9
  export const CONFIG_FILE = "aiwiki.yaml";
8
10
  export const REQUIRED_DIRS = [
9
11
  "02-raw/articles",
@@ -551,6 +553,14 @@ export async function doctor(rootPath) {
551
553
  detail: absolute
552
554
  });
553
555
  }
556
+ for (const file of REQUIRED_FILES) {
557
+ const absolute = path.join(root, file);
558
+ checks.push({
559
+ name: file,
560
+ status: (await exists(absolute)) ? "ok" : "missing",
561
+ detail: absolute
562
+ });
563
+ }
554
564
  const writeTarget = path.join(root, "_system", "logs", ".doctor-write-test");
555
565
  try {
556
566
  await fs.writeFile(writeTarget, "ok", "utf8");
@@ -566,7 +576,16 @@ export async function statusSummary(rootPath) {
566
576
  const root = resolveRoot(rootPath);
567
577
  const runsRoot = path.join(root, "09-runs");
568
578
  if (!(await exists(runsRoot))) {
569
- return { root, runCount: 0, failedCount: 0 };
579
+ return {
580
+ root,
581
+ runCount: 0,
582
+ failedCount: 0,
583
+ fallbackCount: await countWikiEntries(root, "deterministic_fallback"),
584
+ groundingReviewCount: await countGroundingReviewEntries(root),
585
+ lintStatus: await readLintStatus(root),
586
+ lintReportPath: await lintReportPath(root),
587
+ systemFiles: await systemFileSummary(root)
588
+ };
570
589
  }
571
590
  const entries = await fs.readdir(runsRoot, { withFileTypes: true });
572
591
  const dirs = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
@@ -590,11 +609,20 @@ export async function statusSummary(rootPath) {
590
609
  stats.push({ dir, mtimeMs: (await fs.stat(path.join(runsRoot, dir))).mtimeMs });
591
610
  }
592
611
  stats.sort((a, b) => b.mtimeMs - a.mtimeMs);
612
+ const successDirs = dirs.filter((dir) => !dir.endsWith("-fetch-failed"));
613
+ const failureDirs = dirs.filter((dir) => dir.endsWith("-fetch-failed"));
593
614
  return {
594
615
  root,
595
616
  runCount: dirs.length,
596
617
  failedCount,
597
- lastRunId: stats[0]?.dir
618
+ lastRunId: stats[0]?.dir,
619
+ lastSuccessRunId: await newestDir(root, successDirs),
620
+ lastFailureRunId: await newestDir(root, failureDirs),
621
+ fallbackCount: await countWikiEntries(root, "deterministic_fallback"),
622
+ groundingReviewCount: await countGroundingReviewEntries(root),
623
+ lintStatus: await readLintStatus(root),
624
+ lintReportPath: await lintReportPath(root),
625
+ systemFiles: await systemFileSummary(root)
598
626
  };
599
627
  }
600
628
  export async function exists(target) {
@@ -613,3 +641,73 @@ function readScalar(text, key) {
613
641
  function unquote(value) {
614
642
  return value.replace(/^["']|["']$/g, "");
615
643
  }
644
+ const REQUIRED_FILES = ["_system/purpose.md", "_system/index.md", "_system/log.md"];
645
+ async function systemFileSummary(root) {
646
+ const files = [];
647
+ for (const file of REQUIRED_FILES) {
648
+ files.push({ path: file, status: await exists(path.join(root, file)) ? "ok" : "missing" });
649
+ }
650
+ return files;
651
+ }
652
+ async function newestDir(root, dirs) {
653
+ const stats = [];
654
+ for (const dir of dirs) {
655
+ stats.push({ dir, mtimeMs: (await fs.stat(path.join(root, "09-runs", dir))).mtimeMs });
656
+ }
657
+ stats.sort((a, b) => b.mtimeMs - a.mtimeMs);
658
+ return stats[0]?.dir;
659
+ }
660
+ async function countWikiEntries(root, generationMode) {
661
+ const files = await markdownFiles(path.join(root, "05-wiki", "source-knowledge"));
662
+ let count = 0;
663
+ for (const file of files) {
664
+ const parsed = parseMarkdown(await fs.readFile(file, "utf8"));
665
+ if (frontmatterString(parsed.frontmatter, "generation_mode") === generationMode) {
666
+ count += 1;
667
+ }
668
+ }
669
+ return count;
670
+ }
671
+ async function countGroundingReviewEntries(root) {
672
+ const files = await markdownFiles(path.join(root, "05-wiki", "source-knowledge"));
673
+ let count = 0;
674
+ for (const file of files) {
675
+ const parsed = parseMarkdown(await fs.readFile(file, "utf8"));
676
+ if (frontmatterBoolean(parsed.frontmatter, "grounding_needs_review") === true) {
677
+ count += 1;
678
+ }
679
+ }
680
+ return count;
681
+ }
682
+ async function readLintStatus(root) {
683
+ const reportPath = await lintReportPath(root);
684
+ if (!reportPath) {
685
+ return "missing";
686
+ }
687
+ const text = await fs.readFile(path.join(root, reportPath), "utf8");
688
+ if (/\[(error|warning)\]/.test(text)) {
689
+ return "needs_attention";
690
+ }
691
+ return "ok";
692
+ }
693
+ async function lintReportPath(root) {
694
+ const absolute = path.join(root, "dashboards", "Lint Report.md");
695
+ return await exists(absolute) ? relativePath(root, absolute) : undefined;
696
+ }
697
+ async function markdownFiles(dir) {
698
+ if (!(await exists(dir))) {
699
+ return [];
700
+ }
701
+ const entries = await fs.readdir(dir, { withFileTypes: true });
702
+ const files = [];
703
+ for (const entry of entries) {
704
+ const target = path.join(dir, entry.name);
705
+ if (entry.isDirectory()) {
706
+ files.push(...await markdownFiles(target));
707
+ }
708
+ else if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
709
+ files.push(target);
710
+ }
711
+ }
712
+ return files;
713
+ }
package/docs/USAGE.md CHANGED
@@ -410,3 +410,17 @@ aiwiki status
410
410
  # System Purpose Files
411
411
 
412
412
  `aiwiki setup` now also seeds `_system/purpose.md`, `_system/index.md`, and `_system/log.md` when they are missing. These files give humans and host Agents a stable entry point for the knowledge-base goal, scope, common folders, common commands, and lightweight event notes. Re-running setup preserves user edits.
413
+
414
+ ## Diagnostic Commands
415
+
416
+ `aiwiki doctor` checks the workspace directories, write permission, and required system files: `_system/purpose.md`, `_system/index.md`, and `_system/log.md`.
417
+
418
+ `aiwiki status` keeps the existing run-count summary and also reports:
419
+
420
+ - `fallback_entries`: Wiki entries generated as deterministic fallback/scaffold.
421
+ - `grounding_review_entries`: Wiki entries marked for grounding review.
422
+ - `lint_status`: whether a lint report is missing, clean, or needs attention.
423
+ - `system_files`: readiness of purpose, index, and log files.
424
+ - `next_action`: the recommended next command.
425
+
426
+ `aiwiki next` uses the same repair order: fix workspace structure first, then lint errors, lint warnings, empty-workspace onboarding, and finally healthy-state query guidance.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itradingai/aiwiki",
3
- "version": "0.2.14",
3
+ "version": "0.2.15",
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",