@zosmaai/pi-llm-wiki 0.10.9 → 0.11.1
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/CHANGELOG.md +6 -0
- package/README.de.md +35 -4
- package/README.es.md +260 -170
- package/README.fr.md +35 -4
- package/README.hi.md +35 -4
- package/README.ja.md +35 -4
- package/README.ko.md +35 -4
- package/README.md +41 -12
- package/README.pt.md +35 -4
- package/README.ru.md +35 -4
- package/README.zh.md +260 -170
- package/assets/demo.gif +0 -0
- package/dist/extensions/llm-wiki/lib/bootstrap.js +74 -0
- package/dist/extensions/llm-wiki/lib/embeddings.js +401 -0
- package/dist/extensions/llm-wiki/lib/guardrails.js +232 -0
- package/dist/extensions/llm-wiki/lib/indexing.js +78 -0
- package/dist/extensions/llm-wiki/lib/ingest-worker.js +410 -0
- package/dist/extensions/llm-wiki/lib/inject.js +65 -0
- package/dist/extensions/llm-wiki/lib/knowledge-document.js +442 -0
- package/dist/extensions/llm-wiki/lib/knowledge-links.js +206 -0
- package/dist/extensions/llm-wiki/lib/legacy-repair.js +443 -0
- package/dist/extensions/llm-wiki/lib/metadata.js +505 -0
- package/dist/extensions/llm-wiki/lib/model-command.js +85 -0
- package/dist/extensions/llm-wiki/lib/observation.js +283 -0
- package/dist/extensions/llm-wiki/lib/recall.js +875 -0
- package/dist/extensions/llm-wiki/lib/retro.js +158 -0
- package/dist/extensions/llm-wiki/lib/runtime.js +187 -0
- package/dist/extensions/llm-wiki/lib/source-extractors.js +426 -0
- package/dist/extensions/llm-wiki/lib/source-packet.js +229 -0
- package/dist/extensions/llm-wiki/lib/subagent.js +41 -0
- package/dist/extensions/llm-wiki/lib/task-config.js +201 -0
- package/dist/extensions/llm-wiki/lib/tools.js +1199 -0
- package/dist/extensions/llm-wiki/lib/trajectories-command.js +51 -0
- package/dist/extensions/llm-wiki/lib/trajectory.js +467 -0
- package/dist/extensions/llm-wiki/lib/utils.js +353 -0
- package/dist/extensions/llm-wiki/lib/vault-format.js +247 -0
- package/dist/extensions/llm-wiki/lib/visible-status.js +31 -0
- package/dist/extensions/llm-wiki/lib/wiki-service.js +128 -0
- package/dist/mcp/exec.js +121 -0
- package/dist/mcp/index.js +229 -0
- package/dist/mcp/operations.js +130 -0
- package/dist/package.json +1 -0
- package/docs/api.md +5 -2
- package/docs/architecture.md +5 -2
- package/docs/configuration.md +25 -0
- package/docs/superpowers/plans/2026-08-02-okf-foundation.md +1579 -0
- package/docs/superpowers/plans/2026-08-03-okf-foundation-remediation.md +3005 -0
- package/docs/superpowers/plans/2026-08-06-authoritative-event-history-phase-1-foundation-hardening.md +937 -0
- package/docs/superpowers/plans/2026-08-06-okf-foundation-release-remediation.md +1174 -0
- package/docs/superpowers/plans/2026-08-07-synthesis-language.md +98 -0
- package/docs/superpowers/specs/2026-08-02-okf-foundation-design.md +593 -0
- package/docs/superpowers/specs/2026-08-02-okf-v0.2-interoperability-design.md +542 -0
- package/docs/superpowers/specs/2026-08-07-synthesis-language-design.md +94 -0
- package/extensions/llm-wiki/index.ts +22 -36
- package/extensions/llm-wiki/lib/bootstrap.ts +87 -0
- package/extensions/llm-wiki/lib/embeddings.ts +9 -3
- package/extensions/llm-wiki/lib/guardrails.ts +26 -18
- package/extensions/llm-wiki/lib/indexing.ts +2 -1
- package/extensions/llm-wiki/lib/ingest-worker.ts +304 -28
- package/extensions/llm-wiki/lib/knowledge-document.ts +663 -0
- package/extensions/llm-wiki/lib/knowledge-links.ts +282 -0
- package/extensions/llm-wiki/lib/legacy-repair.ts +572 -0
- package/extensions/llm-wiki/lib/metadata.ts +550 -128
- package/extensions/llm-wiki/lib/model-command.ts +0 -1
- package/extensions/llm-wiki/lib/observation.ts +37 -43
- package/extensions/llm-wiki/lib/recall.ts +61 -33
- package/extensions/llm-wiki/lib/retro.ts +65 -41
- package/extensions/llm-wiki/lib/runtime.ts +0 -3
- package/extensions/llm-wiki/lib/source-extractors.ts +12 -17
- package/extensions/llm-wiki/lib/source-packet.ts +45 -32
- package/extensions/llm-wiki/lib/task-config.ts +36 -0
- package/extensions/llm-wiki/lib/tools.ts +413 -342
- package/extensions/llm-wiki/lib/trajectory.ts +15 -1
- package/extensions/llm-wiki/lib/utils.ts +127 -131
- package/extensions/llm-wiki/lib/vault-format.ts +363 -0
- package/extensions/llm-wiki/lib/wiki-service.ts +183 -0
- package/mcp/exec.ts +122 -0
- package/mcp/index.ts +60 -250
- package/mcp/operations.ts +176 -0
- package/package.json +8 -2
- package/scripts/migrate-llm-wiki.js +801 -0
- package/skills/llm-wiki/SKILL.md +12 -8
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import type { Definition, Link, LinkReference, Nodes, Root } from "mdast";
|
|
2
|
+
import { fromMarkdown } from "mdast-util-from-markdown";
|
|
3
|
+
import type { KnowledgeDiagnostic } from "./knowledge-document.js";
|
|
4
|
+
import { compareCodePoint } from "./vault-format.js";
|
|
5
|
+
|
|
6
|
+
export interface ExtractedLink {
|
|
7
|
+
target: string;
|
|
8
|
+
offset: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface KnowledgeLinks {
|
|
12
|
+
markdown: ExtractedLink[];
|
|
13
|
+
wikilinks: ExtractedLink[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface UnresolvedKnowledgeLink {
|
|
17
|
+
target: string;
|
|
18
|
+
syntax: "markdown" | "wikilink";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ResolvedBacklinks {
|
|
22
|
+
targets: string[];
|
|
23
|
+
unresolved: UnresolvedKnowledgeLink[];
|
|
24
|
+
diagnostics: KnowledgeDiagnostic[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function diag(
|
|
28
|
+
severity: "warning" | "error",
|
|
29
|
+
code: KnowledgeDiagnostic["code"],
|
|
30
|
+
path: string,
|
|
31
|
+
message: string,
|
|
32
|
+
): KnowledgeDiagnostic {
|
|
33
|
+
return { severity, code, path, message };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function extractKnowledgeLinks(body: string): KnowledgeLinks {
|
|
37
|
+
const markdown: ExtractedLink[] = [];
|
|
38
|
+
const wikilinks: ExtractedLink[] = [];
|
|
39
|
+
|
|
40
|
+
// Extract legacy wikilinks
|
|
41
|
+
for (const match of body.matchAll(/\[\[([^\]|]+)(?:\|[^\]]*)?\]\]/g)) {
|
|
42
|
+
wikilinks.push({ target: match[1].trim(), offset: match.index ?? 0 });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Parse with CommonMark AST
|
|
46
|
+
let tree: Root;
|
|
47
|
+
try {
|
|
48
|
+
tree = fromMarkdown(body);
|
|
49
|
+
} catch {
|
|
50
|
+
return { markdown, wikilinks };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Build definition map (case-insensitive)
|
|
54
|
+
const defs = new Map<string, Definition>();
|
|
55
|
+
function collectDefs(node: Nodes) {
|
|
56
|
+
if (node.type === "definition") {
|
|
57
|
+
defs.set(node.identifier.toLowerCase(), node);
|
|
58
|
+
}
|
|
59
|
+
if ("children" in node && Array.isArray((node as { children?: Nodes[] }).children)) {
|
|
60
|
+
for (const child of (node as { children: Nodes[] }).children) {
|
|
61
|
+
collectDefs(child);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
collectDefs(tree);
|
|
66
|
+
|
|
67
|
+
// Track used definitions
|
|
68
|
+
const usedDefs = new Set<string>();
|
|
69
|
+
|
|
70
|
+
// Visit nodes for links
|
|
71
|
+
function visit(node: Nodes) {
|
|
72
|
+
// Skip code spans, fenced/indented code blocks, and raw HTML
|
|
73
|
+
if (node.type === "inlineCode" || node.type === "code" || node.type === "html") return;
|
|
74
|
+
|
|
75
|
+
if (node.type === "link") {
|
|
76
|
+
const link = node as Link;
|
|
77
|
+
// Skip autolinks (source starts with <)
|
|
78
|
+
if (link.url && link.position) {
|
|
79
|
+
const source = body.slice(link.position.start.offset, link.position.end.offset);
|
|
80
|
+
if (!source.startsWith("<")) {
|
|
81
|
+
markdown.push({ target: link.url, offset: link.position.start.offset ?? 0 });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (node.type === "linkReference") {
|
|
87
|
+
const ref = node as LinkReference;
|
|
88
|
+
const ident = (ref.identifier ?? "").toLowerCase();
|
|
89
|
+
const def = defs.get(ident);
|
|
90
|
+
if (def && !usedDefs.has(ident)) {
|
|
91
|
+
usedDefs.add(ident);
|
|
92
|
+
if (ref.position) {
|
|
93
|
+
markdown.push({ target: def.url, offset: ref.position.start.offset ?? 0 });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Recurse into children
|
|
99
|
+
if ("children" in node && Array.isArray((node as { children?: Nodes[] }).children)) {
|
|
100
|
+
for (const child of (node as { children: Nodes[] }).children) {
|
|
101
|
+
visit(child);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
visit(tree);
|
|
107
|
+
|
|
108
|
+
return { markdown, wikilinks };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function extractLegacyWikilinks(body: string): ExtractedLink[] {
|
|
112
|
+
const links: ExtractedLink[] = [];
|
|
113
|
+
for (const match of body.matchAll(/\[\[([^\]|]+)(?:\|[^\]]*)?\]\]/g)) {
|
|
114
|
+
links.push({ target: match[1].trim(), offset: match.index ?? 0 });
|
|
115
|
+
}
|
|
116
|
+
return links;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function resolveMarkdownTarget(
|
|
120
|
+
target: string,
|
|
121
|
+
sourceId: string,
|
|
122
|
+
):
|
|
123
|
+
| { kind: "concept"; id: string }
|
|
124
|
+
| { kind: "escape" }
|
|
125
|
+
| { kind: "external" }
|
|
126
|
+
| { kind: "empty" }
|
|
127
|
+
| { kind: "invalid" } {
|
|
128
|
+
// Strip query and fragment (earliest delimiter)
|
|
129
|
+
const qIndex = target.indexOf("?");
|
|
130
|
+
const fIndex = target.indexOf("#");
|
|
131
|
+
let clean = target;
|
|
132
|
+
let cut = -1;
|
|
133
|
+
if (qIndex !== -1 && (fIndex === -1 || qIndex < fIndex)) cut = qIndex;
|
|
134
|
+
else if (fIndex !== -1) cut = fIndex;
|
|
135
|
+
if (cut !== -1) clean = target.slice(0, cut);
|
|
136
|
+
|
|
137
|
+
// Ignore empty fragment-only targets
|
|
138
|
+
if (!clean || clean === "#") return { kind: "empty" };
|
|
139
|
+
|
|
140
|
+
// Ignore external URI schemes
|
|
141
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(clean)) return { kind: "external" };
|
|
142
|
+
|
|
143
|
+
// Percent-decode each segment once, but never let malformed user input escape
|
|
144
|
+
// the link diagnostics boundary.
|
|
145
|
+
let decoded: string[];
|
|
146
|
+
try {
|
|
147
|
+
decoded = clean.split("/").map((segment) => decodeURIComponent(segment));
|
|
148
|
+
} catch {
|
|
149
|
+
return { kind: "invalid" };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Convert decoded backslashes to /
|
|
153
|
+
const normalized = decoded.map((s) => s.replace(/\\/g, "/")).join("/");
|
|
154
|
+
|
|
155
|
+
const sourceDir = sourceId.replace(/[^\/]*$/, ""); // directory of source
|
|
156
|
+
const parts = normalized.split("/");
|
|
157
|
+
const isRootRelative = normalized.startsWith("/");
|
|
158
|
+
|
|
159
|
+
if (isRootRelative) {
|
|
160
|
+
// Root-relative: resolve with explicit stack
|
|
161
|
+
const stack: string[] = [];
|
|
162
|
+
for (const part of parts) {
|
|
163
|
+
if (part === "") continue;
|
|
164
|
+
if (part === ".") continue;
|
|
165
|
+
if (part === "..") {
|
|
166
|
+
if (stack.length === 0) return { kind: "escape" };
|
|
167
|
+
stack.pop();
|
|
168
|
+
} else {
|
|
169
|
+
stack.push(part);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
const resolved = stack.join("/");
|
|
173
|
+
if (!resolved.endsWith(".md")) return { kind: "empty" };
|
|
174
|
+
return { kind: "concept", id: resolved.slice(0, -3) };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// For file-relative paths, resolve against source directory
|
|
178
|
+
const baseParts = sourceDir.split("/").filter(Boolean);
|
|
179
|
+
const allParts = [...baseParts];
|
|
180
|
+
|
|
181
|
+
for (const part of parts) {
|
|
182
|
+
if (part === "" || part === ".") {
|
|
183
|
+
} else if (part === "..") {
|
|
184
|
+
if (allParts.length === 0) {
|
|
185
|
+
return { kind: "escape" };
|
|
186
|
+
}
|
|
187
|
+
allParts.pop();
|
|
188
|
+
} else {
|
|
189
|
+
allParts.push(part);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const resolved = allParts.join("/");
|
|
194
|
+
|
|
195
|
+
// Require .md suffix for Markdown links
|
|
196
|
+
if (resolved.endsWith(".md")) {
|
|
197
|
+
return { kind: "concept", id: resolved.slice(0, -3) };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return { kind: "empty" };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function resolveWikilinkTarget(
|
|
204
|
+
target: string,
|
|
205
|
+
): { kind: "concept"; id: string } | { kind: "empty" } {
|
|
206
|
+
// Wikilinks are already bundle-relative concept IDs
|
|
207
|
+
const cleaned = target.trim();
|
|
208
|
+
if (!cleaned) return { kind: "empty" };
|
|
209
|
+
return { kind: "concept", id: cleaned };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function buildResolvedBacklinks(
|
|
213
|
+
sourceId: string,
|
|
214
|
+
body: string,
|
|
215
|
+
knownIds: Set<string>,
|
|
216
|
+
): ResolvedBacklinks {
|
|
217
|
+
const diagnostics: KnowledgeDiagnostic[] = [];
|
|
218
|
+
const unresolved: UnresolvedKnowledgeLink[] = [];
|
|
219
|
+
const targets = new Set<string>();
|
|
220
|
+
const allLinks = extractKnowledgeLinks(body);
|
|
221
|
+
|
|
222
|
+
// Process Markdown links
|
|
223
|
+
for (const link of allLinks.markdown) {
|
|
224
|
+
const resolved = resolveMarkdownTarget(link.target, sourceId);
|
|
225
|
+
if (resolved.kind === "escape") {
|
|
226
|
+
diagnostics.push(
|
|
227
|
+
diag(
|
|
228
|
+
"warning",
|
|
229
|
+
"link_path_escape",
|
|
230
|
+
`${sourceId}.md`,
|
|
231
|
+
`Link escapes bundle root: ${link.target}`,
|
|
232
|
+
),
|
|
233
|
+
);
|
|
234
|
+
} else if (resolved.kind === "invalid") {
|
|
235
|
+
diagnostics.push(
|
|
236
|
+
diag(
|
|
237
|
+
"warning",
|
|
238
|
+
"link_unresolved",
|
|
239
|
+
`${sourceId}.md`,
|
|
240
|
+
`Malformed percent-encoded link: ${link.target}`,
|
|
241
|
+
),
|
|
242
|
+
);
|
|
243
|
+
} else if (resolved.kind === "concept") {
|
|
244
|
+
const normalizedId = resolved.id.normalize("NFC");
|
|
245
|
+
if (knownIds.has(normalizedId)) {
|
|
246
|
+
targets.add(normalizedId);
|
|
247
|
+
} else {
|
|
248
|
+
unresolved.push({ target: normalizedId, syntax: "markdown" });
|
|
249
|
+
diagnostics.push(
|
|
250
|
+
diag("warning", "link_unresolved", `${sourceId}.md`, `Unresolved link: ${normalizedId}`),
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// external and empty are silently ignored
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Process wikilinks
|
|
258
|
+
for (const link of allLinks.wikilinks) {
|
|
259
|
+
const resolved = resolveWikilinkTarget(link.target);
|
|
260
|
+
if (resolved.kind === "concept") {
|
|
261
|
+
const normalizedId = resolved.id.normalize("NFC");
|
|
262
|
+
if (knownIds.has(normalizedId)) {
|
|
263
|
+
targets.add(normalizedId);
|
|
264
|
+
} else {
|
|
265
|
+
unresolved.push({ target: normalizedId, syntax: "wikilink" });
|
|
266
|
+
diagnostics.push(
|
|
267
|
+
diag(
|
|
268
|
+
"warning",
|
|
269
|
+
"link_unresolved",
|
|
270
|
+
`${sourceId}.md`,
|
|
271
|
+
`Unresolved wikilink: ${normalizedId}`,
|
|
272
|
+
),
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Sort and deduplicate
|
|
279
|
+
const sorted = [...targets].sort(compareCodePoint);
|
|
280
|
+
|
|
281
|
+
return { targets: sorted, unresolved, diagnostics };
|
|
282
|
+
}
|