@creativeaitools/agent-wiki 2.0.0
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/AGENT-WIKI-SPEC-v2.md +2584 -0
- package/AGENTS.md +314 -0
- package/INBOX.md +19 -0
- package/LICENSE +21 -0
- package/ONBOARD.md +373 -0
- package/README.md +429 -0
- package/WIKI.md +706 -0
- package/_system/config.example.json +105 -0
- package/dist/src/catalog.js +66 -0
- package/dist/src/cli.js +330 -0
- package/dist/src/compile.js +104 -0
- package/dist/src/config.js +84 -0
- package/dist/src/lifecycle.js +171 -0
- package/dist/src/migrate.js +26 -0
- package/dist/src/onboard.js +159 -0
- package/dist/src/page.js +188 -0
- package/dist/src/registry.js +74 -0
- package/dist/src/schedule-prompts.js +74 -0
- package/dist/src/upgrade.js +215 -0
- package/dist/src/wiki-utils.js +112 -0
- package/dist/src/workspace.js +198 -0
- package/package.json +54 -0
- package/skills/compile-wiki/SKILL.md +140 -0
- package/skills/extract-knowledge-primitives/SKILL.md +350 -0
- package/skills/import-link/SKILL.md +101 -0
- package/skills/import-link/config.json +12 -0
- package/skills/process-inbox/SKILL.md +255 -0
- package/skills/process-workspace-sources/SKILL.md +127 -0
- package/skills/update-overview/SKILL.md +140 -0
- package/skills/write-synthesis/SKILL.md +154 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"wikiType": "vault",
|
|
4
|
+
"pythonCommand": null,
|
|
5
|
+
"knownVaults": {},
|
|
6
|
+
"workspace": {
|
|
7
|
+
"root": null,
|
|
8
|
+
"wikiDir": "wiki",
|
|
9
|
+
"scan": {
|
|
10
|
+
"includeExtensions": [
|
|
11
|
+
".md",
|
|
12
|
+
".markdown",
|
|
13
|
+
".txt",
|
|
14
|
+
".pdf",
|
|
15
|
+
".docx",
|
|
16
|
+
".csv",
|
|
17
|
+
".json",
|
|
18
|
+
".yaml",
|
|
19
|
+
".yml"
|
|
20
|
+
],
|
|
21
|
+
"excludeDirs": [
|
|
22
|
+
".git",
|
|
23
|
+
".hg",
|
|
24
|
+
".svn",
|
|
25
|
+
".obsidian",
|
|
26
|
+
".venv",
|
|
27
|
+
"venv",
|
|
28
|
+
"env",
|
|
29
|
+
"__pycache__",
|
|
30
|
+
".pytest_cache",
|
|
31
|
+
".mypy_cache",
|
|
32
|
+
".ruff_cache",
|
|
33
|
+
"node_modules",
|
|
34
|
+
"dist",
|
|
35
|
+
"build",
|
|
36
|
+
".next",
|
|
37
|
+
".turbo",
|
|
38
|
+
".cache",
|
|
39
|
+
"_system",
|
|
40
|
+
"reports",
|
|
41
|
+
"target",
|
|
42
|
+
"vendor"
|
|
43
|
+
],
|
|
44
|
+
"excludeFileGlobs": [
|
|
45
|
+
"*.lock",
|
|
46
|
+
"package-lock.json",
|
|
47
|
+
"pnpm-lock.yaml",
|
|
48
|
+
"yarn.lock",
|
|
49
|
+
"uv.lock",
|
|
50
|
+
"poetry.lock"
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"conversion": {
|
|
55
|
+
"enabled": false,
|
|
56
|
+
"defaultBackend": "auto",
|
|
57
|
+
"backendOrder": [
|
|
58
|
+
"pymupdf4llm",
|
|
59
|
+
"markitdown"
|
|
60
|
+
],
|
|
61
|
+
"allowNetwork": false,
|
|
62
|
+
"allowOcr": false,
|
|
63
|
+
"allowLlm": false,
|
|
64
|
+
"allowTranscription": false,
|
|
65
|
+
"allowHostedDocumentIntelligence": false,
|
|
66
|
+
"backends": {
|
|
67
|
+
"pymupdf4llm": {
|
|
68
|
+
"enabled": true,
|
|
69
|
+
"command": null,
|
|
70
|
+
"formats": [
|
|
71
|
+
"pdf"
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
"markitdown": {
|
|
75
|
+
"enabled": true,
|
|
76
|
+
"command": "markitdown",
|
|
77
|
+
"formats": [
|
|
78
|
+
"pdf",
|
|
79
|
+
"docx",
|
|
80
|
+
"pptx",
|
|
81
|
+
"xlsx",
|
|
82
|
+
"html",
|
|
83
|
+
"csv",
|
|
84
|
+
"json",
|
|
85
|
+
"xml",
|
|
86
|
+
"epub"
|
|
87
|
+
]
|
|
88
|
+
},
|
|
89
|
+
"arxiv2md": {
|
|
90
|
+
"enabled": false,
|
|
91
|
+
"command": null,
|
|
92
|
+
"formats": [
|
|
93
|
+
"pdf"
|
|
94
|
+
]
|
|
95
|
+
},
|
|
96
|
+
"marker": {
|
|
97
|
+
"enabled": false,
|
|
98
|
+
"command": null,
|
|
99
|
+
"formats": [
|
|
100
|
+
"pdf"
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { readJson, writeOperationalLog, writeText } from "./wiki-utils.js";
|
|
4
|
+
export function renderIndexCommand(args) {
|
|
5
|
+
const wikiRoot = process.cwd();
|
|
6
|
+
const data = readJson(join(wikiRoot, "_system/cache/pages.json"));
|
|
7
|
+
if (!data || !Array.isArray(data.pages))
|
|
8
|
+
throw new Error("Missing _system/cache/pages.json; run compile first.");
|
|
9
|
+
const rendered = renderIndex(data.pages);
|
|
10
|
+
const path = join(wikiRoot, "index.md");
|
|
11
|
+
const existing = existsSync(path) ? readFileSync(path, "utf8") : "";
|
|
12
|
+
if (args.check) {
|
|
13
|
+
if (existing !== rendered) {
|
|
14
|
+
console.log("index.md is out of date");
|
|
15
|
+
return 1;
|
|
16
|
+
}
|
|
17
|
+
console.log("index.md is current");
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
20
|
+
if (existing === rendered) {
|
|
21
|
+
console.log("index.md is current");
|
|
22
|
+
return 0;
|
|
23
|
+
}
|
|
24
|
+
writeText(path, rendered);
|
|
25
|
+
console.log("Wrote index.md");
|
|
26
|
+
if (!args["no-log"])
|
|
27
|
+
writeOperationalLog(wikiRoot, `index: regenerated root page catalog; pages=${data.pages.length}`);
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
export function renderIndex(pages) {
|
|
31
|
+
const byType = new Map();
|
|
32
|
+
for (const page of pages) {
|
|
33
|
+
const type = String(page.pageType || "other");
|
|
34
|
+
byType.set(type, [...(byType.get(type) ?? []), page]);
|
|
35
|
+
}
|
|
36
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
37
|
+
const lines = ["---", "id: index.root", "pageType: index", "title: Page Index", "status: active", `createdAt: ${today}`, `updatedAt: ${today}`, "---", "", "# Page Index", ""];
|
|
38
|
+
for (const type of ["overview", "source", "entity", "concept", "claim", "synthesis", "question", "report", "index"]) {
|
|
39
|
+
const rows = (byType.get(type) ?? []).sort((a, b) => String(a.title).localeCompare(String(b.title)));
|
|
40
|
+
if (!rows.length)
|
|
41
|
+
continue;
|
|
42
|
+
lines.push(`## ${label(type)}`, "", "| Page | Path | Status | Updated |", "|---|---|---|---|");
|
|
43
|
+
for (const page of rows) {
|
|
44
|
+
lines.push(`| ${esc(page.title || page.id)} | ${esc(page.path)} | ${esc(page.status)} | ${esc(page.updatedAt)} |`);
|
|
45
|
+
}
|
|
46
|
+
lines.push("");
|
|
47
|
+
}
|
|
48
|
+
return `${lines.join("\n").trimEnd()}\n`;
|
|
49
|
+
}
|
|
50
|
+
function label(type) {
|
|
51
|
+
const labels = {
|
|
52
|
+
overview: "Overviews",
|
|
53
|
+
source: "Sources",
|
|
54
|
+
entity: "Entities",
|
|
55
|
+
concept: "Concepts",
|
|
56
|
+
claim: "Claims",
|
|
57
|
+
synthesis: "Syntheses",
|
|
58
|
+
question: "Questions",
|
|
59
|
+
report: "Reports",
|
|
60
|
+
index: "Indexes"
|
|
61
|
+
};
|
|
62
|
+
return labels[type] ?? `${type.charAt(0).toUpperCase()}${type.slice(1).replaceAll("-", " ")}s`;
|
|
63
|
+
}
|
|
64
|
+
function esc(value) {
|
|
65
|
+
return String(value ?? "").replaceAll("|", "\\|").replaceAll("\n", " ");
|
|
66
|
+
}
|
package/dist/src/cli.js
ADDED
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { realpathSync } from "node:fs";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { loadConfig } from "./config.js";
|
|
5
|
+
import { renderIndexCommand } from "./catalog.js";
|
|
6
|
+
import { compileWiki } from "./compile.js";
|
|
7
|
+
import { doctorWiki, initWiki, issuesToJson, issuesToText } from "./lifecycle.js";
|
|
8
|
+
import { migrateRefs } from "./migrate.js";
|
|
9
|
+
import { buildOnboardingReport, onboard } from "./onboard.js";
|
|
10
|
+
import { createPage } from "./page.js";
|
|
11
|
+
import { migrateWiki } from "./upgrade.js";
|
|
12
|
+
import { writeOperationalLog } from "./wiki-utils.js";
|
|
13
|
+
import { randomBytes } from "node:crypto";
|
|
14
|
+
import { addRegistryEntry, getRegistryEntry, listRegistryEntries, registryPath, removeRegistryEntry } from "./registry.js";
|
|
15
|
+
import { schedulePrompt } from "./schedule-prompts.js";
|
|
16
|
+
import { defaultWorkspaceRoot, filesToJson, filesToText, loadState, markSourced, scanWorkspace, updateStateFromScan, wikiRootForWorkspace } from "./workspace.js";
|
|
17
|
+
export function main(argv = process.argv.slice(2)) {
|
|
18
|
+
try {
|
|
19
|
+
const global = parseArgs(argv);
|
|
20
|
+
const command = global._[0];
|
|
21
|
+
if (!command || global.help) {
|
|
22
|
+
printHelp();
|
|
23
|
+
return global.help ? 0 : 2;
|
|
24
|
+
}
|
|
25
|
+
const wikiRoot = resolveWikiRoot(global);
|
|
26
|
+
if (command === "list")
|
|
27
|
+
return cmdList(global);
|
|
28
|
+
if (command === "registry")
|
|
29
|
+
return cmdRegistry(global);
|
|
30
|
+
if (command === "check")
|
|
31
|
+
return cmdCheck(global);
|
|
32
|
+
if (command === "schedule")
|
|
33
|
+
return schedulePrompt(global);
|
|
34
|
+
if (command === "init")
|
|
35
|
+
return cmdInit(global);
|
|
36
|
+
if (command === "doctor")
|
|
37
|
+
return cmdDoctor(global, wikiRoot);
|
|
38
|
+
if (command === "workspace")
|
|
39
|
+
return withWikiRoot(wikiRoot, () => cmdWorkspace(global));
|
|
40
|
+
if (command === "create-page")
|
|
41
|
+
return withWikiRoot(wikiRoot, () => createPage(global));
|
|
42
|
+
if (command === "onboard")
|
|
43
|
+
return onboard({ ...global, "wiki-root": wikiRoot ?? global["wiki-root"] });
|
|
44
|
+
if (command === "index")
|
|
45
|
+
return withWikiRoot(wikiRoot, () => renderIndexCommand(global));
|
|
46
|
+
if (command === "log")
|
|
47
|
+
return withWikiRoot(wikiRoot, () => cmdLog(global));
|
|
48
|
+
if (command === "migrate-refs-to-links")
|
|
49
|
+
return withWikiRoot(wikiRoot, () => migrateRefs(global));
|
|
50
|
+
if (command === "migrate")
|
|
51
|
+
return withWikiRoot(wikiRoot, () => migrateWiki(global));
|
|
52
|
+
if (command === "compile")
|
|
53
|
+
return withWikiRoot(wikiRoot, () => compileWiki(global));
|
|
54
|
+
if (command === "uuid")
|
|
55
|
+
return cmdUuid(global);
|
|
56
|
+
throw new Error(`Unknown command: ${command}`);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
60
|
+
return 2;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function cmdInit(args) {
|
|
64
|
+
const type = String(args.type ?? "");
|
|
65
|
+
if (type !== "vault" && type !== "workspace") {
|
|
66
|
+
throw new Error("init requires --type vault|workspace");
|
|
67
|
+
}
|
|
68
|
+
const result = initWiki({
|
|
69
|
+
wikiType: type,
|
|
70
|
+
root: stringOpt(args["root"], "."),
|
|
71
|
+
workspaceRoot: stringOpt(args["workspace-root"]),
|
|
72
|
+
wikiDir: stringOpt(args["wiki-dir"], "wiki"),
|
|
73
|
+
writeConfig: args["no-config"] ? false : true,
|
|
74
|
+
withTemplate: args["no-template"] ? false : true
|
|
75
|
+
});
|
|
76
|
+
console.log(`Initialized ${result.wikiType} wiki at ${result.wikiRoot}`);
|
|
77
|
+
if (result.workspaceRoot)
|
|
78
|
+
console.log(`Workspace root: ${result.workspaceRoot}`);
|
|
79
|
+
console.log(`Created folders: ${result.created.length}`);
|
|
80
|
+
if (result.configWritten)
|
|
81
|
+
console.log("Wrote _system/config.json");
|
|
82
|
+
if (result.templateCopied.length > 0)
|
|
83
|
+
console.log(`Copied template files: ${result.templateCopied.length}`);
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
function cmdDoctor(args, wikiRoot) {
|
|
87
|
+
const root = wikiRoot ?? stringOpt(args["wiki-root"], stringOpt(args.root, ".")) ?? ".";
|
|
88
|
+
const type = stringOpt(args.type);
|
|
89
|
+
const issues = doctorWiki(root, type);
|
|
90
|
+
console.log(args.json ? issuesToJson(issues) : issuesToText(issues));
|
|
91
|
+
return issues.some((issue) => issue.level === "error") ? 1 : 0;
|
|
92
|
+
}
|
|
93
|
+
function cmdList(args) {
|
|
94
|
+
const entries = listRegistryEntries();
|
|
95
|
+
if (args.json) {
|
|
96
|
+
console.log(JSON.stringify({ schemaVersion: 1, registryPath: registryPath(), wikis: entries }, null, 2));
|
|
97
|
+
return 0;
|
|
98
|
+
}
|
|
99
|
+
if (entries.length === 0) {
|
|
100
|
+
console.log("No Agent Wiki roots registered.");
|
|
101
|
+
return 0;
|
|
102
|
+
}
|
|
103
|
+
const width = Math.max(...entries.map((entry) => entry.name.length), 4);
|
|
104
|
+
for (const entry of entries)
|
|
105
|
+
console.log(`${entry.name.padEnd(width)} ${entry.type.padEnd(9)} ${entry.root}`);
|
|
106
|
+
return 0;
|
|
107
|
+
}
|
|
108
|
+
function cmdRegistry(args) {
|
|
109
|
+
const subcommand = args._[1];
|
|
110
|
+
if (subcommand === "list")
|
|
111
|
+
return cmdList(args);
|
|
112
|
+
if (subcommand === "add") {
|
|
113
|
+
const name = stringOpt(args.name, args._[2]);
|
|
114
|
+
if (!name)
|
|
115
|
+
throw new Error("registry add requires a wiki name");
|
|
116
|
+
const root = required(args, "root");
|
|
117
|
+
const entry = addRegistryEntry(name, root, stringOpt(args.type));
|
|
118
|
+
console.log(args.json ? JSON.stringify(entry, null, 2) : `Registered ${entry.name}: ${entry.root}`);
|
|
119
|
+
return 0;
|
|
120
|
+
}
|
|
121
|
+
if (subcommand === "show") {
|
|
122
|
+
const name = stringOpt(args.name, args._[2]);
|
|
123
|
+
if (!name)
|
|
124
|
+
throw new Error("registry show requires a wiki name");
|
|
125
|
+
const entry = getRegistryEntry(name);
|
|
126
|
+
console.log(args.json ? JSON.stringify(entry, null, 2) : `${entry.name} ${entry.type} ${entry.root}`);
|
|
127
|
+
return 0;
|
|
128
|
+
}
|
|
129
|
+
if (subcommand === "remove") {
|
|
130
|
+
const name = stringOpt(args.name, args._[2]);
|
|
131
|
+
if (!name)
|
|
132
|
+
throw new Error("registry remove requires a wiki name");
|
|
133
|
+
const removed = removeRegistryEntry(name);
|
|
134
|
+
console.log(removed ? `Removed ${name}` : `No registered wiki named ${name}`);
|
|
135
|
+
return removed ? 0 : 1;
|
|
136
|
+
}
|
|
137
|
+
throw new Error("registry requires list, add, show, or remove");
|
|
138
|
+
}
|
|
139
|
+
function cmdCheck(args) {
|
|
140
|
+
const name = args._[1] && !String(args._[1]).startsWith("--") ? String(args._[1]) : undefined;
|
|
141
|
+
const entries = args.all ? listRegistryEntries() : [getRegistryEntry(name ?? required(args, "wiki"))];
|
|
142
|
+
const results = entries.map((entry) => checkEntry(entry, Boolean(args.full)));
|
|
143
|
+
if (args.json) {
|
|
144
|
+
console.log(JSON.stringify({ schemaVersion: 1, full: Boolean(args.full), results }, null, 2));
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
for (const result of results) {
|
|
148
|
+
const status = result.ok ? "ok" : "fail";
|
|
149
|
+
console.log(`${status.padEnd(5)} ${result.name} (${result.root})`);
|
|
150
|
+
if (!result.ok) {
|
|
151
|
+
for (const issue of result.doctorIssues)
|
|
152
|
+
console.log(` ${issue.level}: ${issue.code} ${issue.message}`);
|
|
153
|
+
if (result.indexStatus)
|
|
154
|
+
console.log(` index: ${result.indexStatus}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return results.every((result) => result.ok) ? 0 : 1;
|
|
159
|
+
}
|
|
160
|
+
function checkEntry(entry, full) {
|
|
161
|
+
const doctorIssues = doctorWiki(entry.root, entry.type);
|
|
162
|
+
const onboarding = buildOnboardingReport(entry.root);
|
|
163
|
+
const errors = doctorIssues.filter((issue) => issue.level === "error");
|
|
164
|
+
let compileStatus = "skipped";
|
|
165
|
+
let indexStatus = "skipped";
|
|
166
|
+
if (full && errors.length === 0) {
|
|
167
|
+
const originalLog = console.log;
|
|
168
|
+
try {
|
|
169
|
+
console.log = () => undefined;
|
|
170
|
+
withWikiRoot(entry.root, () => compileWiki({}));
|
|
171
|
+
const indexCode = withWikiRoot(entry.root, () => renderIndexCommand({ check: true }));
|
|
172
|
+
compileStatus = "passed";
|
|
173
|
+
indexStatus = indexCode === 0 ? "current" : "out-of-date";
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
compileStatus = "failed";
|
|
177
|
+
indexStatus = "failed";
|
|
178
|
+
}
|
|
179
|
+
finally {
|
|
180
|
+
console.log = originalLog;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
name: entry.name,
|
|
185
|
+
root: entry.root,
|
|
186
|
+
type: entry.type,
|
|
187
|
+
ok: errors.length === 0 && onboarding.summary.ready && (!full || (compileStatus === "passed" && indexStatus === "current")),
|
|
188
|
+
doctorIssues,
|
|
189
|
+
onboardingSummary: onboarding.summary,
|
|
190
|
+
compileStatus,
|
|
191
|
+
indexStatus
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function cmdWorkspace(args) {
|
|
195
|
+
const subcommand = args._[1];
|
|
196
|
+
if (subcommand === "scan" || subcommand === "pending") {
|
|
197
|
+
const includeUnchanged = subcommand === "scan";
|
|
198
|
+
const { files, wikiRoot } = workspaceFiles(args);
|
|
199
|
+
const filtered = includeUnchanged ? files : files.filter((item) => item.reason !== "unchanged");
|
|
200
|
+
if (subcommand === "scan" && args["write-state"]) {
|
|
201
|
+
updateStateFromScan(wikiRoot, files, loadState(wikiRoot));
|
|
202
|
+
}
|
|
203
|
+
console.log(args.json ? filesToJson(filtered) : filesToText(filtered));
|
|
204
|
+
return 0;
|
|
205
|
+
}
|
|
206
|
+
if (subcommand === "mark-sourced") {
|
|
207
|
+
const path = required(args, "path");
|
|
208
|
+
const sourceId = required(args, "source-id");
|
|
209
|
+
const sourcePath = required(args, "source-path");
|
|
210
|
+
const config = loadConfig(stringOpt(args.root, "."));
|
|
211
|
+
const workspaceRoot = defaultWorkspaceRoot(config, stringOpt(args["workspace-root"]));
|
|
212
|
+
const wikiRoot = wikiRootForWorkspace(workspaceRoot, stringOpt(args["wiki-dir"], config.wikiDir));
|
|
213
|
+
const state = markSourced(wikiRoot, loadState(wikiRoot), { relativePath: path, sourceId, sourcePath });
|
|
214
|
+
updateStateFromScan(wikiRoot, scanWorkspace(workspaceRoot, wikiRoot, config.workspaceScan, { state }), state);
|
|
215
|
+
console.log(`Mapped ${path} -> ${sourceId}`);
|
|
216
|
+
return 0;
|
|
217
|
+
}
|
|
218
|
+
throw new Error("workspace requires scan, pending, or mark-sourced");
|
|
219
|
+
}
|
|
220
|
+
function cmdLog(args) {
|
|
221
|
+
const message = required(args, "message");
|
|
222
|
+
const path = writeOperationalLog(process.cwd(), message);
|
|
223
|
+
console.log(`Wrote operational log: ${path}`);
|
|
224
|
+
return 0;
|
|
225
|
+
}
|
|
226
|
+
function cmdUuid(args) {
|
|
227
|
+
const length = Number(stringOpt(args.length, "10"));
|
|
228
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
229
|
+
const bytes = randomBytes(length);
|
|
230
|
+
let out = "";
|
|
231
|
+
for (const byte of bytes)
|
|
232
|
+
out += alphabet[byte % alphabet.length];
|
|
233
|
+
console.log(out);
|
|
234
|
+
return 0;
|
|
235
|
+
}
|
|
236
|
+
function workspaceFiles(args) {
|
|
237
|
+
const config = loadConfig(stringOpt(args.root, "."));
|
|
238
|
+
const workspaceRoot = defaultWorkspaceRoot(config, stringOpt(args["workspace-root"]));
|
|
239
|
+
const wikiRoot = wikiRootForWorkspace(workspaceRoot, stringOpt(args["wiki-dir"], config.wikiDir));
|
|
240
|
+
const sinceHours = stringOpt(args["since-hours"]);
|
|
241
|
+
const since = sinceHours === undefined ? undefined : new Date(Date.now() - Number(sinceHours) * 60 * 60 * 1000);
|
|
242
|
+
const state = loadState(wikiRoot);
|
|
243
|
+
return { files: scanWorkspace(workspaceRoot, wikiRoot, config.workspaceScan, { since, state }), wikiRoot };
|
|
244
|
+
}
|
|
245
|
+
function parseArgs(argv) {
|
|
246
|
+
const parsed = { _: [] };
|
|
247
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
248
|
+
const token = argv[index];
|
|
249
|
+
if (token.startsWith("--")) {
|
|
250
|
+
const [key, inlineValue] = token.slice(2).split("=", 2);
|
|
251
|
+
if (inlineValue !== undefined) {
|
|
252
|
+
addArg(parsed, key, inlineValue);
|
|
253
|
+
}
|
|
254
|
+
else if (argv[index + 1] && !argv[index + 1].startsWith("--")) {
|
|
255
|
+
addArg(parsed, key, argv[index + 1]);
|
|
256
|
+
index += 1;
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
addArg(parsed, key, true);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
parsed._.push(token);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return parsed;
|
|
267
|
+
}
|
|
268
|
+
function addArg(parsed, key, value) {
|
|
269
|
+
const existing = parsed[key];
|
|
270
|
+
if (typeof existing === "string")
|
|
271
|
+
parsed[key] = [existing, String(value)];
|
|
272
|
+
else if (Array.isArray(existing))
|
|
273
|
+
existing.push(String(value));
|
|
274
|
+
else
|
|
275
|
+
parsed[key] = value;
|
|
276
|
+
}
|
|
277
|
+
function required(args, key) {
|
|
278
|
+
const value = stringOpt(args[key]);
|
|
279
|
+
if (!value) {
|
|
280
|
+
throw new Error(`missing required --${key}`);
|
|
281
|
+
}
|
|
282
|
+
return value;
|
|
283
|
+
}
|
|
284
|
+
function stringOpt(value, fallback) {
|
|
285
|
+
return typeof value === "string" ? value : fallback;
|
|
286
|
+
}
|
|
287
|
+
function resolveWikiRoot(args) {
|
|
288
|
+
const name = stringOpt(args.wiki);
|
|
289
|
+
return name ? getRegistryEntry(name).root : undefined;
|
|
290
|
+
}
|
|
291
|
+
function withWikiRoot(root, fn) {
|
|
292
|
+
if (!root)
|
|
293
|
+
return fn();
|
|
294
|
+
const previous = process.cwd();
|
|
295
|
+
process.chdir(root);
|
|
296
|
+
try {
|
|
297
|
+
return fn();
|
|
298
|
+
}
|
|
299
|
+
finally {
|
|
300
|
+
process.chdir(previous);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
function printHelp() {
|
|
304
|
+
console.log(`agent-wiki
|
|
305
|
+
|
|
306
|
+
Commands:
|
|
307
|
+
list [--json]
|
|
308
|
+
registry list|add|show|remove
|
|
309
|
+
check WIKI|--all [--full] [--json]
|
|
310
|
+
schedule prompt process-inbox|extract-primitives|update-overview [WIKI...] [--wiki NAME]
|
|
311
|
+
--wiki NAME <command>
|
|
312
|
+
init --type vault|workspace [--root PATH] [--workspace-root PATH] [--wiki-dir wiki] [--no-config] [--no-template]
|
|
313
|
+
doctor [--wiki-root PATH] [--type vault|workspace] [--json]
|
|
314
|
+
create-page --type TYPE --subtype SUBTYPE --slug SLUG --title TITLE (--body-file PATH|--body TEXT)
|
|
315
|
+
onboard --check [--wiki-root PATH] [--questions] [--compact]
|
|
316
|
+
onboard --write-config [--type vault|workspace] [--wiki-dir wiki] [--python-command CMD] [--conversion disabled|available-local]
|
|
317
|
+
compile [--verbose]
|
|
318
|
+
index --write|--check
|
|
319
|
+
log --message TEXT
|
|
320
|
+
uuid [--length N]
|
|
321
|
+
migrate-refs-to-links --dry-run|--write
|
|
322
|
+
migrate --from v1 --check|--write
|
|
323
|
+
workspace scan [--workspace-root PATH] [--wiki-dir wiki] [--json] [--since-hours N] [--write-state]
|
|
324
|
+
workspace pending [--workspace-root PATH] [--wiki-dir wiki] [--json] [--since-hours N]
|
|
325
|
+
workspace mark-sourced --path PATH --source-id ID --source-path PATH [--workspace-root PATH] [--wiki-dir wiki]
|
|
326
|
+
`);
|
|
327
|
+
}
|
|
328
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) {
|
|
329
|
+
process.exitCode = main();
|
|
330
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { renderIndex } from "./catalog.js";
|
|
4
|
+
import { walkMarkdownPages, writeJson, writeOperationalLog } from "./wiki-utils.js";
|
|
5
|
+
export function compileWiki(args) {
|
|
6
|
+
const root = process.cwd();
|
|
7
|
+
const compiledAt = new Date().toISOString();
|
|
8
|
+
console.log("Agent Wiki v2 Compile Pipeline");
|
|
9
|
+
console.log(`Wiki root: ${root}`);
|
|
10
|
+
console.log(`Compiled at: ${compiledAt}`);
|
|
11
|
+
console.log();
|
|
12
|
+
for (const dir of ["_system/cache", "_system/indexes", "_system/logs", "reports"])
|
|
13
|
+
mkdirSync(join(root, dir), { recursive: true });
|
|
14
|
+
console.log("Reading vault pages...");
|
|
15
|
+
const pages = walkMarkdownPages(root);
|
|
16
|
+
const issues = validatePages(pages);
|
|
17
|
+
console.log(` Found ${pages.length} pages with frontmatter`);
|
|
18
|
+
if (issues.length)
|
|
19
|
+
console.log(` Validation issues detected: ${issues.length}`);
|
|
20
|
+
const claims = extractClaims(pages);
|
|
21
|
+
const relations = extractRelations(pages);
|
|
22
|
+
const timeline = extractTimeline(pages);
|
|
23
|
+
const contradictions = [];
|
|
24
|
+
const health = analyzeHealth(pages, claims);
|
|
25
|
+
const pageOutput = pages.map(({ meta, body, ...page }) => page);
|
|
26
|
+
console.log("\nWriting cache files...");
|
|
27
|
+
writeJson(join(root, "_system/cache/pages.json"), { compiledAt, pages: pageOutput });
|
|
28
|
+
writeJsonl(join(root, "_system/cache/claims.jsonl"), claims);
|
|
29
|
+
writeJsonl(join(root, "_system/cache/relations.jsonl"), relations);
|
|
30
|
+
writeJson(join(root, "_system/cache/agent-digest.json"), { compiledAt, pages: pageOutput.slice(0, 50), claims: claims.slice(0, 30), validationIssues: issues });
|
|
31
|
+
writeJson(join(root, "_system/cache/contradictions.json"), { compiledAt, contradictions });
|
|
32
|
+
writeJson(join(root, "_system/cache/questions.json"), { compiledAt, questions: pageOutput.filter((p) => p.pageType === "question") });
|
|
33
|
+
writeJson(join(root, "_system/cache/timeline-events.json"), { compiledAt, events: timeline });
|
|
34
|
+
writeJson(join(root, "_system/cache/source-index.json"), { compiledAt, sources: pageOutput.filter((p) => p.pageType === "source") });
|
|
35
|
+
writeJson(join(root, "_system/cache/validation-issues.json"), { compiledAt, issues, summary: summarize(issues) });
|
|
36
|
+
writeJson(join(root, "_system/cache/claims-index.json"), { compiledAt, claims: pageOutput.filter((p) => p.pageType === "claim") });
|
|
37
|
+
const indexes = buildIndexes(pageOutput);
|
|
38
|
+
for (const [name, data] of Object.entries(indexes))
|
|
39
|
+
writeJson(join(root, `_system/indexes/${name}.json`), { compiledAt, data });
|
|
40
|
+
console.log("\nWriting root page catalog...");
|
|
41
|
+
writeFileSync(join(root, "index.md"), renderIndex(pageOutput), "utf8");
|
|
42
|
+
writeReports(root, pageOutput, claims, health, compiledAt.slice(0, 10));
|
|
43
|
+
writeOperationalLog(root, `compile-wiki: regenerated index, cache, indexes, and reports; pages=${pages.length} claims=${claims.length} relations=${relations.length} contradictions=0 openQuestions=${pageOutput.filter((p) => p.pageType === "question" && ["open", "researching", "blocked"].includes(String(p.status))).length} evidenceGaps=${health.evidenceGaps.length} lowConfidence=${health.lowConfidence.length} validationIssues=${issues.length} duplicatePageIds=${issues.filter((i) => i.code === "duplicate_page_id").length}`);
|
|
44
|
+
console.log("\nCompile complete.");
|
|
45
|
+
console.log(` Pages: ${pages.length} | Claims: ${claims.length} | Relations: ${relations.length} | Contradictions: 0`);
|
|
46
|
+
console.log(` Validation issues: ${issues.length}`);
|
|
47
|
+
return 0;
|
|
48
|
+
}
|
|
49
|
+
function validatePages(pages) {
|
|
50
|
+
const issues = [];
|
|
51
|
+
const seen = new Map();
|
|
52
|
+
for (const page of pages) {
|
|
53
|
+
for (const field of ["id", "pageType", "title", "status", "createdAt", "updatedAt"]) {
|
|
54
|
+
if (!page[field])
|
|
55
|
+
issues.push({ code: "missing_required_field", path: page.path, field, message: `Missing required field ${field}` });
|
|
56
|
+
}
|
|
57
|
+
if (page.id)
|
|
58
|
+
seen.set(page.id, [...(seen.get(page.id) ?? []), page.path]);
|
|
59
|
+
}
|
|
60
|
+
for (const [id, paths] of seen)
|
|
61
|
+
if (paths.length > 1)
|
|
62
|
+
issues.push({ code: "duplicate_page_id", pageId: id, paths, path: paths[0], message: `Page id ${id} appears in multiple files.` });
|
|
63
|
+
return issues;
|
|
64
|
+
}
|
|
65
|
+
function extractClaims(pages) {
|
|
66
|
+
const claims = [];
|
|
67
|
+
for (const page of pages) {
|
|
68
|
+
if (page.pageType === "claim")
|
|
69
|
+
claims.push({ id: page.id, text: page.text || page.title, status: page.status, confidence: page.confidence, sourceIds: page.sourceIds || [], evidence: page.evidence || [], _owningPagePath: page.path });
|
|
70
|
+
for (const claim of Array.isArray(page.claims) ? page.claims : [])
|
|
71
|
+
claims.push({ ...claim, _owningPagePath: page.path });
|
|
72
|
+
}
|
|
73
|
+
return claims;
|
|
74
|
+
}
|
|
75
|
+
function extractRelations(pages) { return pages.flatMap((p) => (Array.isArray(p.relations) ? p.relations.map((r) => ({ ...r, _owningPagePath: p.path })) : [])); }
|
|
76
|
+
function extractTimeline(pages) { return pages.flatMap((p) => (Array.isArray(p.timeline) ? p.timeline.map((r) => ({ ...r, _owningPagePath: p.path })) : [])); }
|
|
77
|
+
function analyzeHealth(pages, claims) {
|
|
78
|
+
return { lowConfidence: claims.filter((c) => Number(c.confidence ?? 1) < 0.5), evidenceGaps: claims.filter((c) => !Array.isArray(c.evidence) || c.evidence.length === 0), stalePages: pages.filter((p) => false) };
|
|
79
|
+
}
|
|
80
|
+
function buildIndexes(pages) {
|
|
81
|
+
const idToPath = {}, pathToId = {}, pageType = {}, aliases = {}, tags = {};
|
|
82
|
+
for (const page of pages) {
|
|
83
|
+
idToPath[page.id] = page.path;
|
|
84
|
+
pathToId[page.path] = page.id;
|
|
85
|
+
(pageType[page.pageType] ??= []).push(page.id);
|
|
86
|
+
for (const alias of page.aliases || [])
|
|
87
|
+
aliases[String(alias)] = page.id;
|
|
88
|
+
for (const tag of page.tags || [])
|
|
89
|
+
(tags[String(tag)] ??= []).push(page.id);
|
|
90
|
+
}
|
|
91
|
+
return { "id-to-path": idToPath, "path-to-id": pathToId, "pagetype-index": pageType, "alias-index": aliases, "tag-index": tags };
|
|
92
|
+
}
|
|
93
|
+
function writeReports(root, pages, claims, health, date) {
|
|
94
|
+
const report = (title, rows) => `---\npageType: report\ntitle: ${title}\nstatus: active\nupdatedAt: ${date}\n---\n\n# ${title}\n\n${rows.length ? rows.join("\n") : "No entries."}\n`;
|
|
95
|
+
writeFileSync(join(root, "reports/open-questions.md"), report("Open Questions", pages.filter((p) => p.pageType === "question").map((p) => `- ${p.id}: ${p.title}`)), "utf8");
|
|
96
|
+
writeFileSync(join(root, "reports/contradictions.md"), report("Contradictions", []), "utf8");
|
|
97
|
+
writeFileSync(join(root, "reports/low-confidence.md"), report("Low Confidence", health.lowConfidence.map((c) => `- ${c.id}: ${c.text}`)), "utf8");
|
|
98
|
+
writeFileSync(join(root, "reports/claim-health.md"), report("Claim Health", [`Claims: ${claims.length}`, `Evidence gaps: ${health.evidenceGaps.length}`, `Low confidence: ${health.lowConfidence.length}`]), "utf8");
|
|
99
|
+
writeFileSync(join(root, "reports/stale-pages.md"), report("Stale Pages", []), "utf8");
|
|
100
|
+
writeFileSync(join(root, "reports/orphaned-claims.md"), report("Orphaned Claims", []), "utf8");
|
|
101
|
+
writeFileSync(join(root, "reports/evidence-gaps.md"), report("Evidence Gaps", health.evidenceGaps.map((c) => `- ${c.id}: ${c.text}`)), "utf8");
|
|
102
|
+
}
|
|
103
|
+
function writeJsonl(path, rows) { writeFileSync(path, rows.map((row) => JSON.stringify(row)).join("\n") + (rows.length ? "\n" : ""), "utf8"); }
|
|
104
|
+
function summarize(issues) { return issues.reduce((acc, issue) => ({ ...acc, [issue.code]: (acc[issue.code] ?? 0) + 1 }), {}); }
|