@zosmaai/pi-llm-wiki 0.2.0 → 0.2.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/extensions/llm-wiki/lib/source-packet.ts +67 -20
- package/package.json +1 -1
- package/prompts/wiki-digest.md +5 -1
- package/prompts/wiki-discover.md +5 -1
- package/prompts/wiki-ingest.md +5 -1
- package/prompts/wiki-init.md +5 -1
- package/prompts/wiki-lint.md +5 -1
- package/prompts/wiki-query.md +5 -1
- package/prompts/wiki-run.md +5 -1
- package/prompts/wiki-status.md +1 -1
- package/test/llm-wiki.test.ts +33 -0
|
@@ -22,6 +22,36 @@ export interface CaptureResult {
|
|
|
22
22
|
extracted: string;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
const DEFAULT_MARKITDOWN_TIMEOUT_MS = 180_000;
|
|
26
|
+
const DEFAULT_CURL_TIMEOUT_SECONDS = 30;
|
|
27
|
+
|
|
28
|
+
function markitdownTimeoutMs(): number {
|
|
29
|
+
return positiveIntegerFromEnv("WIKI_MARKITDOWN_TIMEOUT_MS", DEFAULT_MARKITDOWN_TIMEOUT_MS);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function positiveIntegerFromEnv(name: string, fallback: number): number {
|
|
33
|
+
const raw = process.env[name];
|
|
34
|
+
if (!raw) return fallback;
|
|
35
|
+
const parsed = Number.parseInt(raw, 10);
|
|
36
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function isPdfUrl(url: string): boolean {
|
|
40
|
+
try {
|
|
41
|
+
return new URL(url).pathname.toLowerCase().endsWith(".pdf");
|
|
42
|
+
} catch {
|
|
43
|
+
return url.toLowerCase().split(/[?#]/, 1)[0].endsWith(".pdf");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function looksLikePdf(content: string): boolean {
|
|
48
|
+
return content.trimStart().startsWith("%PDF-");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function pdfExtractionFailureMessage(source: string): string {
|
|
52
|
+
return `_PDF content could not be converted to markdown from ${source}. Try increasing WIKI_MARKITDOWN_TIMEOUT_MS._\n`;
|
|
53
|
+
}
|
|
54
|
+
|
|
25
55
|
/** Capture a URL into a source packet. */
|
|
26
56
|
export async function captureUrl(
|
|
27
57
|
pi: ExtensionAPI,
|
|
@@ -38,8 +68,9 @@ export async function captureUrl(
|
|
|
38
68
|
// Try to fetch and extract content
|
|
39
69
|
let extracted = "";
|
|
40
70
|
let title = url;
|
|
71
|
+
const isPdf = isPdfUrl(url);
|
|
41
72
|
|
|
42
|
-
// Try
|
|
73
|
+
// Try MarkItDown first.
|
|
43
74
|
const markitdown = await exec(
|
|
44
75
|
pi,
|
|
45
76
|
"sh",
|
|
@@ -53,7 +84,7 @@ export async function captureUrl(
|
|
|
53
84
|
pi,
|
|
54
85
|
"sh",
|
|
55
86
|
["-c", `uvx --from 'markitdown[pdf]' markitdown "${url}" 2>/dev/null || echo ""`],
|
|
56
|
-
{ signal, timeout:
|
|
87
|
+
{ signal, timeout: markitdownTimeoutMs() },
|
|
57
88
|
);
|
|
58
89
|
if (mdResult.stdout.trim()) {
|
|
59
90
|
extracted = mdResult.stdout;
|
|
@@ -66,21 +97,35 @@ export async function captureUrl(
|
|
|
66
97
|
}
|
|
67
98
|
}
|
|
68
99
|
|
|
69
|
-
// Fallback: try fetch_content equivalent via curl
|
|
100
|
+
// Fallback: try fetch_content equivalent via curl for text/html sources.
|
|
101
|
+
// Do not write binary PDF bytes into extracted.md when PDF conversion fails.
|
|
70
102
|
if (!extracted) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
103
|
+
if (isPdf) {
|
|
104
|
+
extracted = pdfExtractionFailureMessage(url);
|
|
105
|
+
} else {
|
|
106
|
+
try {
|
|
107
|
+
const curlResult = await exec(
|
|
108
|
+
pi,
|
|
109
|
+
"curl",
|
|
110
|
+
["-sL", "--max-time", String(DEFAULT_CURL_TIMEOUT_SECONDS), url],
|
|
111
|
+
{
|
|
112
|
+
signal,
|
|
113
|
+
timeout: (DEFAULT_CURL_TIMEOUT_SECONDS + 5) * 1_000,
|
|
114
|
+
},
|
|
115
|
+
);
|
|
116
|
+
if (curlResult.stdout) {
|
|
117
|
+
if (looksLikePdf(curlResult.stdout)) {
|
|
118
|
+
extracted = pdfExtractionFailureMessage(url);
|
|
119
|
+
} else {
|
|
120
|
+
extracted = curlResult.stdout;
|
|
121
|
+
// Try to extract title from HTML
|
|
122
|
+
const titleMatch = extracted.match(/<title>([^<]*)<\/title>/i);
|
|
123
|
+
if (titleMatch) title = titleMatch[1].trim();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
} catch {
|
|
127
|
+
// curl failed too
|
|
81
128
|
}
|
|
82
|
-
} catch {
|
|
83
|
-
// curl failed too
|
|
84
129
|
}
|
|
85
130
|
}
|
|
86
131
|
|
|
@@ -126,12 +171,13 @@ export async function captureFile(
|
|
|
126
171
|
mkdirSync(join(packetPath, "original"), { recursive: true });
|
|
127
172
|
mkdirSync(join(packetPath, "attachments"), { recursive: true });
|
|
128
173
|
|
|
129
|
-
const
|
|
174
|
+
const isPdf = filePath.toLowerCase().endsWith(".pdf");
|
|
175
|
+
const content = isPdf ? "" : readText(filePath);
|
|
130
176
|
const fileName = filePath.split("/").pop() || "unknown";
|
|
131
177
|
|
|
132
|
-
// Try
|
|
178
|
+
// Try MarkItDown for PDFs.
|
|
133
179
|
let extracted = content;
|
|
134
|
-
if (
|
|
180
|
+
if (isPdf) {
|
|
135
181
|
const markitdown = await exec(
|
|
136
182
|
pi,
|
|
137
183
|
"sh",
|
|
@@ -145,13 +191,14 @@ export async function captureFile(
|
|
|
145
191
|
pi,
|
|
146
192
|
"sh",
|
|
147
193
|
["-c", `uvx --from 'markitdown[pdf]' markitdown "${filePath}" 2>/dev/null || echo ""`],
|
|
148
|
-
{ signal, timeout:
|
|
194
|
+
{ signal, timeout: markitdownTimeoutMs() },
|
|
149
195
|
);
|
|
150
196
|
if (mdResult.stdout.trim()) extracted = mdResult.stdout;
|
|
151
197
|
} catch {
|
|
152
|
-
|
|
198
|
+
extracted = pdfExtractionFailureMessage(filePath);
|
|
153
199
|
}
|
|
154
200
|
}
|
|
201
|
+
if (!extracted) extracted = pdfExtractionFailureMessage(filePath);
|
|
155
202
|
}
|
|
156
203
|
|
|
157
204
|
// Copy original to packet
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zosmaai/pi-llm-wiki",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "LLM Wiki for Pi — self-maintaining knowledge base following Karpathy's pattern. Obsidian-friendly, auto-updating, personal & company wiki.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
package/prompts/wiki-digest.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Generate a daily or weekly digest of wiki changes — new sources, pages, insights, and gaps.
|
|
3
|
-
|
|
3
|
+
argument-hint: "[--period daily|weekly]"
|
|
4
4
|
section: LLM Wiki
|
|
5
5
|
topLevelCli: true
|
|
6
6
|
---
|
|
@@ -9,6 +9,10 @@ topLevelCli: true
|
|
|
9
9
|
|
|
10
10
|
Generate a digest of recent wiki activity.
|
|
11
11
|
|
|
12
|
+
## User Arguments
|
|
13
|
+
|
|
14
|
+
$ARGUMENTS
|
|
15
|
+
|
|
12
16
|
## Steps
|
|
13
17
|
|
|
14
18
|
1. Read `wiki/LOG.md` — filter entries since last digest
|
package/prompts/wiki-discover.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Auto-discover new sources from the web. Searches based on config topics and known knowledge gaps.
|
|
3
|
-
|
|
3
|
+
argument-hint: "[--topic <topic>]"
|
|
4
4
|
section: LLM Wiki
|
|
5
5
|
topLevelCli: true
|
|
6
6
|
---
|
|
@@ -9,6 +9,10 @@ topLevelCli: true
|
|
|
9
9
|
|
|
10
10
|
Find new source material for the wiki by searching the web.
|
|
11
11
|
|
|
12
|
+
## User Arguments
|
|
13
|
+
|
|
14
|
+
$ARGUMENTS
|
|
15
|
+
|
|
12
16
|
Read the LLM Wiki skill at `.pi/skills/llm-wiki/SKILL.md` first. Also read `config.yaml` for topics and feeds.
|
|
13
17
|
|
|
14
18
|
## Steps
|
package/prompts/wiki-ingest.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Process new source files in raw/ and update the wiki. Creates summaries, entities, concepts, and cross-references.
|
|
3
|
-
|
|
3
|
+
argument-hint: "[path]"
|
|
4
4
|
section: LLM Wiki
|
|
5
5
|
topLevelCli: true
|
|
6
6
|
---
|
|
@@ -9,6 +9,10 @@ topLevelCli: true
|
|
|
9
9
|
|
|
10
10
|
Process new files in `raw/` and integrate them into the wiki.
|
|
11
11
|
|
|
12
|
+
## User Arguments
|
|
13
|
+
|
|
14
|
+
$ARGUMENTS
|
|
15
|
+
|
|
12
16
|
Read the LLM Wiki skill at `.pi/skills/llm-wiki/SKILL.md` first to understand the full schema, page formats, and conventions.
|
|
13
17
|
|
|
14
18
|
## Steps
|
package/prompts/wiki-init.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Initialize a new LLM Wiki in the current directory. Creates the full directory structure, config, and template files.
|
|
3
|
-
|
|
3
|
+
argument-hint: "<topic> [--mode personal|company]"
|
|
4
4
|
section: LLM Wiki
|
|
5
5
|
topLevelCli: true
|
|
6
6
|
---
|
|
@@ -9,6 +9,10 @@ topLevelCli: true
|
|
|
9
9
|
|
|
10
10
|
Initialize a new LLM Wiki in the current directory.
|
|
11
11
|
|
|
12
|
+
## User Arguments
|
|
13
|
+
|
|
14
|
+
$ARGUMENTS
|
|
15
|
+
|
|
12
16
|
Read the LLM Wiki skill at `.pi/skills/llm-wiki/SKILL.md` (or wherever the skill is installed) first to understand the full schema and conventions.
|
|
13
17
|
|
|
14
18
|
## Steps
|
package/prompts/wiki-lint.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Health check the wiki. Detects contradictions, orphans, missing pages, stale claims, and knowledge gaps.
|
|
3
|
-
|
|
3
|
+
argument-hint: "[--fix]"
|
|
4
4
|
section: LLM Wiki
|
|
5
5
|
topLevelCli: true
|
|
6
6
|
---
|
|
@@ -9,6 +9,10 @@ topLevelCli: true
|
|
|
9
9
|
|
|
10
10
|
Run a comprehensive health check on the wiki.
|
|
11
11
|
|
|
12
|
+
## User Arguments
|
|
13
|
+
|
|
14
|
+
$ARGUMENTS
|
|
15
|
+
|
|
12
16
|
Read the LLM Wiki skill at `.pi/skills/llm-wiki/SKILL.md` first to understand the full schema and conventions.
|
|
13
17
|
|
|
14
18
|
## Steps
|
package/prompts/wiki-query.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Ask questions against the wiki. Synthesizes answers from wiki pages with cross-reference citations.
|
|
3
|
-
|
|
3
|
+
argument-hint: "<question>"
|
|
4
4
|
section: LLM Wiki
|
|
5
5
|
topLevelCli: true
|
|
6
6
|
---
|
|
@@ -9,6 +9,10 @@ topLevelCli: true
|
|
|
9
9
|
|
|
10
10
|
Ask a question and get an answer synthesized from wiki content.
|
|
11
11
|
|
|
12
|
+
## User Question
|
|
13
|
+
|
|
14
|
+
$ARGUMENTS
|
|
15
|
+
|
|
12
16
|
Read the LLM Wiki skill at `.pi/skills/llm-wiki/SKILL.md` first to understand the full schema and conventions.
|
|
13
17
|
|
|
14
18
|
## Steps
|
package/prompts/wiki-run.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Run the full wiki cycle: discover → ingest → lint. Optionally schedule for auto-updates.
|
|
3
|
-
|
|
3
|
+
argument-hint: "[--schedule daily|weekly]"
|
|
4
4
|
section: LLM Wiki
|
|
5
5
|
topLevelCli: true
|
|
6
6
|
---
|
|
@@ -9,6 +9,10 @@ topLevelCli: true
|
|
|
9
9
|
|
|
10
10
|
Run the complete wiki maintenance cycle: discover new sources, ingest them, and lint for health.
|
|
11
11
|
|
|
12
|
+
## User Arguments
|
|
13
|
+
|
|
14
|
+
$ARGUMENTS
|
|
15
|
+
|
|
12
16
|
Read the LLM Wiki skill at `.pi/skills/llm-wiki/SKILL.md` first.
|
|
13
17
|
|
|
14
18
|
## Steps
|
package/prompts/wiki-status.md
CHANGED
package/test/llm-wiki.test.ts
CHANGED
|
@@ -111,11 +111,33 @@ describe("package structure", () => {
|
|
|
111
111
|
expect(existsSync(path)).toBe(true);
|
|
112
112
|
const content = readFile(path);
|
|
113
113
|
expect(content).toContain("description:");
|
|
114
|
+
expect(content).toContain("argument-hint:");
|
|
114
115
|
expect(content).toContain("section: LLM Wiki");
|
|
115
116
|
expect(content).toContain("topLevelCli: true");
|
|
117
|
+
expect(content).not.toContain("\nargs:");
|
|
116
118
|
}
|
|
117
119
|
});
|
|
118
120
|
|
|
121
|
+
it("should include prompt arguments in templates that accept them", () => {
|
|
122
|
+
const promptsWithArgs = [
|
|
123
|
+
"wiki-init.md",
|
|
124
|
+
"wiki-ingest.md",
|
|
125
|
+
"wiki-query.md",
|
|
126
|
+
"wiki-lint.md",
|
|
127
|
+
"wiki-discover.md",
|
|
128
|
+
"wiki-run.md",
|
|
129
|
+
"wiki-digest.md",
|
|
130
|
+
];
|
|
131
|
+
for (const prompt of promptsWithArgs) {
|
|
132
|
+
const content = readFile(join(rootDir, "prompts", prompt));
|
|
133
|
+
expect(content).toContain("$ARGUMENTS");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const query = readFile(join(rootDir, "prompts", "wiki-query.md"));
|
|
137
|
+
expect(query).toContain("## User Question");
|
|
138
|
+
expect(query).toContain("$ARGUMENTS");
|
|
139
|
+
});
|
|
140
|
+
|
|
119
141
|
it("should have all wiki template files", () => {
|
|
120
142
|
const t = join(rootDir, "skills", "llm-wiki", "templates");
|
|
121
143
|
expect(existsSync(join(t, "INDEX.md"))).toBe(true);
|
|
@@ -157,6 +179,17 @@ describe("package structure", () => {
|
|
|
157
179
|
}
|
|
158
180
|
});
|
|
159
181
|
|
|
182
|
+
it("should keep MarkItDown timeout configurable and avoid PDF byte fallbacks", () => {
|
|
183
|
+
const sourcePacketPath = join(rootDir, "extensions", "llm-wiki", "lib", "source-packet.ts");
|
|
184
|
+
expect(existsSync(sourcePacketPath)).toBe(true);
|
|
185
|
+
const content = readFile(sourcePacketPath);
|
|
186
|
+
expect(content).toContain("WIKI_MARKITDOWN_TIMEOUT_MS");
|
|
187
|
+
expect(content).toContain("DEFAULT_MARKITDOWN_TIMEOUT_MS = 180_000");
|
|
188
|
+
expect(content).toContain("isPdfUrl(url)");
|
|
189
|
+
expect(content).toContain("looksLikePdf(curlResult.stdout)");
|
|
190
|
+
expect(content).toContain("pdfExtractionFailureMessage");
|
|
191
|
+
});
|
|
192
|
+
|
|
160
193
|
it("should have a comprehensive README with install instructions", () => {
|
|
161
194
|
const readme = readFile(join(rootDir, "README.md"));
|
|
162
195
|
expect(readme).toContain("@zosmaai/pi-llm-wiki");
|