@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
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { copyFile } from "node:fs/promises";
|
|
2
3
|
import { extname, join } from "node:path";
|
|
3
4
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
5
|
+
import { createKnowledgeDocument, serializeKnowledgeDocument } from "./knowledge-document.js";
|
|
4
6
|
import { appendEvent } from "./metadata.js";
|
|
5
7
|
import {
|
|
6
8
|
type ExtractedContent,
|
|
@@ -9,7 +11,16 @@ import {
|
|
|
9
11
|
extractUrlContent,
|
|
10
12
|
fileExtractorFor,
|
|
11
13
|
} from "./source-extractors.js";
|
|
12
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
type ExecApi,
|
|
16
|
+
type VaultPaths,
|
|
17
|
+
exec,
|
|
18
|
+
fmtDate,
|
|
19
|
+
nextSourceId,
|
|
20
|
+
readText,
|
|
21
|
+
writeJson,
|
|
22
|
+
} from "./utils.js";
|
|
23
|
+
import { assertWritableVault } from "./vault-format.js";
|
|
13
24
|
|
|
14
25
|
/**
|
|
15
26
|
* Source packet capture and management.
|
|
@@ -47,7 +58,7 @@ const URL_ORIGINAL_EXTENSIONS = new Set([".html", ".htm", ".md", ".pdf", ".txt",
|
|
|
47
58
|
|
|
48
59
|
/** Capture a URL into a source packet. */
|
|
49
60
|
export async function captureUrl(
|
|
50
|
-
pi:
|
|
61
|
+
pi: ExecApi,
|
|
51
62
|
paths: VaultPaths,
|
|
52
63
|
url: string,
|
|
53
64
|
signal?: AbortSignal,
|
|
@@ -57,7 +68,7 @@ export async function captureUrl(
|
|
|
57
68
|
|
|
58
69
|
/** Capture a local file into a source packet. */
|
|
59
70
|
export async function captureFile(
|
|
60
|
-
pi:
|
|
71
|
+
pi: ExecApi,
|
|
61
72
|
paths: VaultPaths,
|
|
62
73
|
filePath: string,
|
|
63
74
|
signal?: AbortSignal,
|
|
@@ -71,6 +82,7 @@ export function captureText(paths: VaultPaths, text: string, title?: string): Ca
|
|
|
71
82
|
}
|
|
72
83
|
|
|
73
84
|
async function captureSource(paths: VaultPaths, source: CaptureSource): Promise<CaptureResult> {
|
|
85
|
+
assertWritableVault(paths);
|
|
74
86
|
const packet = createSourcePacket(paths, source.needsOriginalDir);
|
|
75
87
|
await source.preserveOriginal?.(packet.packetPath);
|
|
76
88
|
const content = await source.extract();
|
|
@@ -78,12 +90,13 @@ async function captureSource(paths: VaultPaths, source: CaptureSource): Promise<
|
|
|
78
90
|
}
|
|
79
91
|
|
|
80
92
|
function captureSourceSync(paths: VaultPaths, source: CaptureSource): CaptureResult {
|
|
93
|
+
assertWritableVault(paths);
|
|
81
94
|
const packet = createSourcePacket(paths, source.needsOriginalDir);
|
|
82
95
|
const content = source.extract() as ExtractedContent;
|
|
83
96
|
return finalizeCapture(paths, packet, source, content);
|
|
84
97
|
}
|
|
85
98
|
|
|
86
|
-
function urlCaptureSource(pi:
|
|
99
|
+
function urlCaptureSource(pi: ExecApi, url: string, signal?: AbortSignal): CaptureSource {
|
|
87
100
|
return {
|
|
88
101
|
needsOriginalDir: true,
|
|
89
102
|
fallbackText: contentExtractionFailureMessage(url),
|
|
@@ -98,11 +111,7 @@ function urlCaptureSource(pi: ExtensionAPI, url: string, signal?: AbortSignal):
|
|
|
98
111
|
};
|
|
99
112
|
}
|
|
100
113
|
|
|
101
|
-
function fileCaptureSource(
|
|
102
|
-
pi: ExtensionAPI,
|
|
103
|
-
filePath: string,
|
|
104
|
-
signal?: AbortSignal,
|
|
105
|
-
): CaptureSource {
|
|
114
|
+
function fileCaptureSource(pi: ExecApi, filePath: string, signal?: AbortSignal): CaptureSource {
|
|
106
115
|
const fileName = filePath.split("/").pop() || "unknown";
|
|
107
116
|
const extractor = fileExtractorFor(filePath);
|
|
108
117
|
const content = extractor.shouldReadText ? readText(filePath) : "";
|
|
@@ -110,8 +119,7 @@ function fileCaptureSource(
|
|
|
110
119
|
return {
|
|
111
120
|
needsOriginalDir: true,
|
|
112
121
|
fallbackText: "",
|
|
113
|
-
preserveOriginal: (packetPath) =>
|
|
114
|
-
preserveFileOriginal(pi, packetPath, filePath, fileName, content, signal),
|
|
122
|
+
preserveOriginal: (packetPath) => preserveFileOriginal(packetPath, filePath, fileName, content),
|
|
115
123
|
extract: async () => {
|
|
116
124
|
// Guard: if we hit the generic catch-all extractor, check for binary magic bytes first
|
|
117
125
|
if (extractor.format === "file") {
|
|
@@ -139,7 +147,7 @@ function fileCaptureSource(
|
|
|
139
147
|
file_path: filePath,
|
|
140
148
|
format: extractor.format,
|
|
141
149
|
}),
|
|
142
|
-
event: () => ({
|
|
150
|
+
event: () => ({ format: extractor.format }),
|
|
143
151
|
};
|
|
144
152
|
}
|
|
145
153
|
|
|
@@ -171,6 +179,7 @@ function finalizeCapture(
|
|
|
171
179
|
source: CaptureSource,
|
|
172
180
|
content: ExtractedContent,
|
|
173
181
|
): CaptureResult {
|
|
182
|
+
assertWritableVault(paths);
|
|
174
183
|
const extracted = content.extracted || source.fallbackText;
|
|
175
184
|
const manifest = {
|
|
176
185
|
id: packet.sourceId,
|
|
@@ -203,23 +212,21 @@ function finalizeCapture(
|
|
|
203
212
|
}
|
|
204
213
|
|
|
205
214
|
async function preserveFileOriginal(
|
|
206
|
-
pi: ExtensionAPI,
|
|
207
215
|
packetPath: string,
|
|
208
216
|
filePath: string,
|
|
209
217
|
fileName: string,
|
|
210
218
|
fallbackContent: string,
|
|
211
|
-
signal?: AbortSignal,
|
|
212
219
|
): Promise<void> {
|
|
213
220
|
try {
|
|
214
|
-
await
|
|
221
|
+
await copyFile(filePath, join(packetPath, "original", fileName));
|
|
215
222
|
} catch {
|
|
216
|
-
// If
|
|
223
|
+
// If copying fails, preserve whatever text content was available.
|
|
217
224
|
writeFileSync(join(packetPath, "original", fileName), fallbackContent, "utf-8");
|
|
218
225
|
}
|
|
219
226
|
}
|
|
220
227
|
|
|
221
228
|
async function preserveUrlOriginal(
|
|
222
|
-
pi:
|
|
229
|
+
pi: ExecApi,
|
|
223
230
|
packetPath: string,
|
|
224
231
|
url: string,
|
|
225
232
|
signal?: AbortSignal,
|
|
@@ -268,16 +275,7 @@ function buildSourcePageSkeleton(manifest: Record<string, unknown>, extracted: s
|
|
|
268
275
|
.trim()
|
|
269
276
|
.slice(0, 500);
|
|
270
277
|
|
|
271
|
-
|
|
272
|
-
type: source
|
|
273
|
-
format: ${format}
|
|
274
|
-
source_id: ${id}
|
|
275
|
-
raw_path: raw/sources/${id}/extracted.md
|
|
276
|
-
captured: ${captured}
|
|
277
|
-
status: skeleton
|
|
278
|
-
---
|
|
279
|
-
|
|
280
|
-
# ${title}${url}
|
|
278
|
+
const body = `# ${title}${url}
|
|
281
279
|
|
|
282
280
|
## Summary
|
|
283
281
|
|
|
@@ -293,11 +291,11 @@ status: skeleton
|
|
|
293
291
|
|
|
294
292
|
## Entities Mentioned
|
|
295
293
|
|
|
296
|
-
- [
|
|
294
|
+
- [LLM: Add linked entities after review]
|
|
297
295
|
|
|
298
296
|
## Concepts Mentioned
|
|
299
297
|
|
|
300
|
-
- [
|
|
298
|
+
- [LLM: Add linked concepts after review]
|
|
301
299
|
|
|
302
300
|
## Notable Quotes
|
|
303
301
|
|
|
@@ -305,8 +303,23 @@ status: skeleton
|
|
|
305
303
|
|
|
306
304
|
## Source Packet
|
|
307
305
|
|
|
308
|
-
- **ID:** \`
|
|
309
|
-
- **Extracted:**
|
|
310
|
-
- **Manifest:**
|
|
306
|
+
- **ID:** \`sources/${id}\`
|
|
307
|
+
- **Extracted:** \`raw/sources/${id}/extracted.md\`
|
|
308
|
+
- **Manifest:** \`raw/sources/${id}/manifest.json\`
|
|
311
309
|
`;
|
|
310
|
+
|
|
311
|
+
const doc = createKnowledgeDocument(
|
|
312
|
+
`sources/${id}.md`,
|
|
313
|
+
{
|
|
314
|
+
type: "source",
|
|
315
|
+
title,
|
|
316
|
+
format,
|
|
317
|
+
source_id: id,
|
|
318
|
+
raw_path: `raw/sources/${id}/extracted.md`,
|
|
319
|
+
captured,
|
|
320
|
+
status: "skeleton",
|
|
321
|
+
},
|
|
322
|
+
body,
|
|
323
|
+
);
|
|
324
|
+
return serializeKnowledgeDocument(doc);
|
|
312
325
|
}
|
|
@@ -100,6 +100,14 @@ export interface TaskConfig {
|
|
|
100
100
|
* they cost nothing in the system prompt for the ~95% who don't use them.
|
|
101
101
|
*/
|
|
102
102
|
trajectories?: boolean;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Language for background ingest synthesis narrative content (issue #124).
|
|
106
|
+
* BCP 47 language tag (e.g. "ru", "fr"). When unset, synthesis defaults to
|
|
107
|
+
* English. Applies to titles, summaries, takeaways, descriptions, etc.; raw
|
|
108
|
+
* source content and technical identifiers remain unchanged.
|
|
109
|
+
*/
|
|
110
|
+
synthesisLanguage?: string;
|
|
103
111
|
}
|
|
104
112
|
|
|
105
113
|
export const TASK_DEFAULTS: TaskConfig = {};
|
|
@@ -175,6 +183,13 @@ function readNamespacedConfig(path: string): Partial<TaskConfig> {
|
|
|
175
183
|
if (typeof section.trajectories === "boolean") {
|
|
176
184
|
out.trajectories = section.trajectories;
|
|
177
185
|
}
|
|
186
|
+
|
|
187
|
+
const lang = section.synthesisLanguage;
|
|
188
|
+
if (typeof lang === "string" && lang.trim()) {
|
|
189
|
+
const canonical = validateSynthesisLanguage(lang.trim());
|
|
190
|
+
if (canonical) out.synthesisLanguage = canonical;
|
|
191
|
+
}
|
|
192
|
+
|
|
178
193
|
return out;
|
|
179
194
|
} catch {
|
|
180
195
|
return {};
|
|
@@ -198,6 +213,27 @@ export function parseModelRef(ref: string): { provider: string; id: string } | u
|
|
|
198
213
|
return { provider, id };
|
|
199
214
|
}
|
|
200
215
|
|
|
216
|
+
/**
|
|
217
|
+
* Validate and canonicalize a BCP 47 language tag for synthesisLanguage (issue #124).
|
|
218
|
+
* Returns the canonical tag, or undefined if invalid or suspicious.
|
|
219
|
+
* Uses Intl.getCanonicalLocales for validation; rejects tags containing
|
|
220
|
+
* newlines, quotes, or other prompt-injection candidates.
|
|
221
|
+
*/
|
|
222
|
+
export function validateSynthesisLanguage(tag: string): string | undefined {
|
|
223
|
+
// Reject obvious injection attempts: newlines, quotes, braces, angle brackets
|
|
224
|
+
if (/\n|\r|"|'|<|>|{|}/.test(tag)) return undefined;
|
|
225
|
+
// Reject if it looks like an instruction (contains "write", "ignore", "translate", etc.)
|
|
226
|
+
const lower = tag.toLowerCase();
|
|
227
|
+
if (/\b(write|ignore|translate|system|prompt|instruction)\b/.test(lower)) return undefined;
|
|
228
|
+
// Basic BCP 47 pattern: language[-script][-region][-variant]*
|
|
229
|
+
// Must start with 2-3 letter language code
|
|
230
|
+
if (!/^[a-z]{2,3}(?:-[A-Za-z]{1,8})*$/.test(tag)) return undefined;
|
|
231
|
+
|
|
232
|
+
const canonical = Intl.getCanonicalLocales(tag);
|
|
233
|
+
if (canonical.length === 0) return undefined;
|
|
234
|
+
return canonical[0];
|
|
235
|
+
}
|
|
236
|
+
|
|
201
237
|
/**
|
|
202
238
|
* Read a settings JSON file as a plain object, or `{}` when it is absent or
|
|
203
239
|
* corrupt. Reads directly (no `existsSync` pre-check) so there is no
|