@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 +60 -24
- package/dist/src/workspace.js +100 -2
- package/docs/USAGE.md +14 -0
- package/package.json +1 -1
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
|
|
437
|
-
writeLine(stream,
|
|
438
|
-
writeLine(stream,
|
|
439
|
-
writeLine(stream,
|
|
440
|
-
writeLine(stream,
|
|
441
|
-
writeLine(stream,
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
|
466
|
-
writeLine(stream, "-
|
|
467
|
-
writeLine(stream, "-
|
|
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
|
-
|
|
471
|
-
if (actionableIssues.length) {
|
|
485
|
+
if (warningCount > 0) {
|
|
472
486
|
writeLine(stream, "");
|
|
473
|
-
writeLine(stream, `结构检查发现 ${
|
|
487
|
+
writeLine(stream, `结构检查发现 ${warningCount} 个 warning 问题。`);
|
|
474
488
|
writeLine(stream, "- aiwiki lint");
|
|
475
|
-
writeLine(stream,
|
|
476
|
-
writeLine(stream, "-
|
|
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, "
|
|
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);
|
package/dist/src/workspace.js
CHANGED
|
@@ -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 {
|
|
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.
|
|
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",
|