@zosmaai/pi-llm-wiki 0.11.6 → 0.11.8
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/README.de.md +1 -0
- package/README.es.md +1 -0
- package/README.fr.md +1 -0
- package/README.hi.md +1 -0
- package/README.ja.md +1 -0
- package/README.ko.md +1 -0
- package/README.md +8 -0
- package/README.pt.md +1 -0
- package/README.ru.md +1 -0
- package/README.zh.md +1 -0
- package/dist/extensions/llm-wiki/lib/dashboard-command.js +7 -2
- package/dist/extensions/llm-wiki/lib/dashboard.js +1 -1
- package/dist/extensions/llm-wiki/lib/host.js +1 -1
- package/dist/extensions/llm-wiki/lib/ingest-worker.js +1 -1
- package/dist/extensions/llm-wiki/lib/knowledge-links.js +1 -1
- package/dist/extensions/llm-wiki/lib/legacy-repair.js +1 -1
- package/dist/extensions/llm-wiki/lib/metadata.js +1 -1
- package/dist/extensions/llm-wiki/lib/model-command.js +77 -4
- package/dist/extensions/llm-wiki/lib/recall.js +2 -2
- package/dist/extensions/llm-wiki/lib/runtime.js +1 -1
- package/dist/extensions/llm-wiki/lib/settings-command.js +6 -1
- package/dist/extensions/llm-wiki/lib/source-extractors.js +21 -5
- package/dist/extensions/llm-wiki/lib/task-config.js +13 -0
- package/dist/extensions/llm-wiki/lib/tools.js +7 -5
- package/dist/extensions/llm-wiki/lib/trajectory.js +1 -1
- package/dist/extensions/llm-wiki/lib/utils.js +1 -1
- package/dist/extensions/llm-wiki/lib/vault-format.js +1 -1
- package/dist/extensions/llm-wiki/lib/visible-status.js +1 -1
- package/dist/mcp/operations.js +1 -1
- package/docs/superpowers/plans/2026-08-29-custom-wiki-page-types.md +378 -0
- package/docs/superpowers/plans/2026-08-29-keyless-provider-ingest-fix.md +256 -0
- package/extensions/llm-wiki/lib/bootstrap.ts +2 -2
- package/extensions/llm-wiki/lib/dashboard-command.ts +9 -3
- package/extensions/llm-wiki/lib/dashboard.ts +1 -1
- package/extensions/llm-wiki/lib/embeddings.ts +1 -1
- package/extensions/llm-wiki/lib/guardrails.ts +2 -2
- package/extensions/llm-wiki/lib/host.ts +1 -1
- package/extensions/llm-wiki/lib/ingest-worker.ts +6 -6
- package/extensions/llm-wiki/lib/knowledge-links.ts +1 -1
- package/extensions/llm-wiki/lib/legacy-repair.ts +4 -4
- package/extensions/llm-wiki/lib/metadata.ts +3 -3
- package/extensions/llm-wiki/lib/model-command.ts +116 -6
- package/extensions/llm-wiki/lib/observation.ts +2 -2
- package/extensions/llm-wiki/lib/recall.ts +4 -4
- package/extensions/llm-wiki/lib/retro.ts +1 -1
- package/extensions/llm-wiki/lib/runtime.ts +1 -1
- package/extensions/llm-wiki/lib/settings-command.ts +9 -2
- package/extensions/llm-wiki/lib/source-extractors.ts +20 -12
- package/extensions/llm-wiki/lib/source-packet.ts +2 -3
- package/extensions/llm-wiki/lib/task-config.ts +24 -1
- package/extensions/llm-wiki/lib/tools.ts +9 -15
- package/extensions/llm-wiki/lib/trajectory.ts +2 -2
- package/extensions/llm-wiki/lib/utils.ts +1 -1
- package/extensions/llm-wiki/lib/vault-format.ts +2 -2
- package/extensions/llm-wiki/lib/visible-status.ts +1 -1
- package/extensions/llm-wiki/lib/wiki-service.ts +1 -1
- package/mcp/operations.ts +3 -3
- package/package.json +14 -9
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { isToolCallEventType } from "@mariozechner/pi-coding-agent";
|
|
2
1
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import { isToolCallEventType } from "@mariozechner/pi-coding-agent";
|
|
3
3
|
import { scheduleReindex } from "./indexing.js";
|
|
4
4
|
import { rebuildMetadataLight } from "./metadata.js";
|
|
5
5
|
import type { Runtime } from "./runtime.js";
|
|
6
|
-
import { isPathWithin, isProtectedPath, resolveVaultPaths } from "./utils.js";
|
|
7
6
|
import type { VaultPaths } from "./utils.js";
|
|
7
|
+
import { isPathWithin, isProtectedPath, resolveVaultPaths } from "./utils.js";
|
|
8
8
|
import { inspectVaultFormat, isGeneratedOkfPath } from "./vault-format.js";
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -129,7 +129,7 @@ export function resolveProjectSettingsPath(cwd: string, host: HostKind = detectH
|
|
|
129
129
|
*
|
|
130
130
|
* Always writes to `settings.json` inside the agent dir — both hosts read it.
|
|
131
131
|
*/
|
|
132
|
-
export function resolveGlobalSettingsPath(
|
|
132
|
+
export function resolveGlobalSettingsPath(_host: HostKind = detectHost()): string {
|
|
133
133
|
let agentDir = "";
|
|
134
134
|
try {
|
|
135
135
|
agentDir = getAgentDir();
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import { existsSync, mkdirSync
|
|
1
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
|
4
4
|
import type { Api, Model } from "@mariozechner/pi-ai";
|
|
5
|
-
import { Type } from "typebox";
|
|
6
5
|
import type { Static } from "typebox";
|
|
6
|
+
import { Type } from "typebox";
|
|
7
7
|
import {
|
|
8
|
+
createKnowledgeDocument,
|
|
8
9
|
type KnowledgeDiagnostic,
|
|
9
10
|
type KnowledgeDocument,
|
|
10
|
-
createKnowledgeDocument,
|
|
11
11
|
patchKnowledgeDocument,
|
|
12
12
|
readKnowledgeDocumentFile,
|
|
13
13
|
serializeKnowledgeDocument,
|
|
14
14
|
writeKnowledgeDocumentFile,
|
|
15
15
|
} from "./knowledge-document.js";
|
|
16
16
|
import {
|
|
17
|
-
type WikilinkValidationMode,
|
|
18
17
|
auditWikilinks,
|
|
19
18
|
buildWikilinkIndex,
|
|
19
|
+
type WikilinkValidationMode,
|
|
20
20
|
} from "./knowledge-links.js";
|
|
21
21
|
import { appendEvent, rebuildMetadataLight } from "./metadata.js";
|
|
22
22
|
import { runSubAgent } from "./subagent.js";
|
|
23
23
|
import { resolveWikilinkValidation } from "./task-config.js";
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
24
|
+
import { fmtDate, readJson, slugify, type VaultPaths } from "./utils.js";
|
|
25
|
+
import { assertWritableVault, VaultWriteError } from "./vault-format.js";
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* Background ingest synthesis (issue #65, part of epic #63).
|
|
@@ -225,7 +225,7 @@ function resolveMarkdownTarget(
|
|
|
225
225
|
// Convert decoded backslashes to /
|
|
226
226
|
const normalized = decoded.map((s) => s.replace(/\\/g, "/")).join("/");
|
|
227
227
|
|
|
228
|
-
const sourceDir = sourceId.replace(/[
|
|
228
|
+
const sourceDir = sourceId.replace(/[^/]*$/, ""); // directory of source
|
|
229
229
|
const parts = normalized.split("/");
|
|
230
230
|
const isRootRelative = normalized.startsWith("/");
|
|
231
231
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import {
|
|
3
|
-
constants,
|
|
4
3
|
chmodSync,
|
|
5
4
|
closeSync,
|
|
5
|
+
constants,
|
|
6
6
|
existsSync,
|
|
7
7
|
fstatSync,
|
|
8
8
|
fsyncSync,
|
|
@@ -10,8 +10,8 @@ import {
|
|
|
10
10
|
lstatSync,
|
|
11
11
|
mkdirSync,
|
|
12
12
|
openSync,
|
|
13
|
-
readFileSync,
|
|
14
13
|
readdirSync,
|
|
14
|
+
readFileSync,
|
|
15
15
|
renameSync,
|
|
16
16
|
unlinkSync,
|
|
17
17
|
utimesSync,
|
|
@@ -19,14 +19,14 @@ import {
|
|
|
19
19
|
} from "node:fs";
|
|
20
20
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
21
21
|
import {
|
|
22
|
+
createKnowledgeDocument,
|
|
22
23
|
type DiagnosticCode,
|
|
23
24
|
type KnowledgeDiagnostic,
|
|
24
|
-
createKnowledgeDocument,
|
|
25
25
|
parseKnowledgeDocument,
|
|
26
26
|
parseMarkdownFrontmatter,
|
|
27
27
|
serializeKnowledgeDocument,
|
|
28
28
|
} from "./knowledge-document.js";
|
|
29
|
-
import { type VaultPaths
|
|
29
|
+
import { readJson, type VaultPaths } from "./utils.js";
|
|
30
30
|
import { discoverKnowledgeDocuments, inspectVaultFormat } from "./vault-format.js";
|
|
31
31
|
|
|
32
32
|
const RECOVERABLE = new Set<DiagnosticCode>([
|
|
@@ -3,8 +3,8 @@ import {
|
|
|
3
3
|
existsSync,
|
|
4
4
|
lstatSync,
|
|
5
5
|
mkdirSync,
|
|
6
|
-
readFileSync,
|
|
7
6
|
readdirSync,
|
|
7
|
+
readFileSync,
|
|
8
8
|
renameSync,
|
|
9
9
|
rmSync,
|
|
10
10
|
writeFileSync,
|
|
@@ -12,11 +12,11 @@ import {
|
|
|
12
12
|
import { dirname, join } from "node:path";
|
|
13
13
|
import type { KnowledgeDiagnostic, KnowledgeDocument } from "./knowledge-document.js";
|
|
14
14
|
import {
|
|
15
|
-
type WikilinkIndex,
|
|
16
15
|
buildResolvedBacklinks,
|
|
17
16
|
buildWikilinkIndex,
|
|
17
|
+
type WikilinkIndex,
|
|
18
18
|
} from "./knowledge-links.js";
|
|
19
|
-
import {
|
|
19
|
+
import { isPathWithin, readJson, type VaultPaths } from "./utils.js";
|
|
20
20
|
import {
|
|
21
21
|
assertWritableVault,
|
|
22
22
|
compareCodePoint,
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
-
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
1
|
+
import type { ExtensionAPI, Theme } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import {
|
|
3
|
+
Container,
|
|
4
|
+
matchesKey,
|
|
5
|
+
type SelectItem,
|
|
6
|
+
SelectList,
|
|
7
|
+
type SelectListTheme,
|
|
8
|
+
Spacer,
|
|
9
|
+
Text,
|
|
10
|
+
} from "@mariozechner/pi-tui";
|
|
2
11
|
import type { Runtime } from "./runtime.js";
|
|
3
|
-
import {
|
|
12
|
+
import { parseModelRef, persistTaskModel, type TaskConfig } from "./task-config.js";
|
|
4
13
|
|
|
5
14
|
/**
|
|
6
15
|
* Model selection surface for the wiki background lane (issue #69, epic #63).
|
|
@@ -25,6 +34,12 @@ interface ModelLike {
|
|
|
25
34
|
/** Words that clear the override and revert to the session model. */
|
|
26
35
|
const CLEAR_WORDS = new Set(["session", "default", "reset", "clear", "none", "unset"]);
|
|
27
36
|
|
|
37
|
+
/** Sentinel value for the "use session model" picker row — not a valid provider/id. */
|
|
38
|
+
const SESSION_VALUE = "__session__";
|
|
39
|
+
|
|
40
|
+
/** Same window size as pi's built-in `/model` picker. */
|
|
41
|
+
const MAX_VISIBLE = 10;
|
|
42
|
+
|
|
28
43
|
/** The status-bar key for the active-model label (so we can update it in place). */
|
|
29
44
|
export const MODEL_STATUS_KEY = "llm-wiki-model";
|
|
30
45
|
|
|
@@ -43,6 +58,85 @@ function modelRef(m: ModelLike): string {
|
|
|
43
58
|
return `${m.provider}/${m.id}`;
|
|
44
59
|
}
|
|
45
60
|
|
|
61
|
+
function buildSelectTheme(theme: Theme): SelectListTheme {
|
|
62
|
+
return {
|
|
63
|
+
selectedPrefix: (text) => theme.fg("accent", text),
|
|
64
|
+
selectedText: (text) => theme.fg("accent", text),
|
|
65
|
+
description: (text) => theme.fg("dim", text),
|
|
66
|
+
scrollInfo: (text) => theme.fg("dim", text),
|
|
67
|
+
noMatch: (text) => theme.fg("muted", text),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function buildPickerItems(
|
|
72
|
+
pool: ModelLike[],
|
|
73
|
+
currentRef: string | undefined,
|
|
74
|
+
): { items: SelectItem[]; selectedIndex: number } {
|
|
75
|
+
const items: SelectItem[] = [
|
|
76
|
+
{
|
|
77
|
+
value: SESSION_VALUE,
|
|
78
|
+
label: "↩ Use session model (clear override)",
|
|
79
|
+
description: currentRef ? undefined : "current",
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
let selectedIndex = 0;
|
|
83
|
+
for (const m of pool) {
|
|
84
|
+
const ref = modelRef(m);
|
|
85
|
+
const isCurrent = currentRef === ref;
|
|
86
|
+
items.push({
|
|
87
|
+
value: ref,
|
|
88
|
+
label: ref,
|
|
89
|
+
description: isCurrent ? "current" : undefined,
|
|
90
|
+
});
|
|
91
|
+
if (isCurrent) selectedIndex = items.length - 1;
|
|
92
|
+
}
|
|
93
|
+
return { items, selectedIndex };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Editor-dock picker; `ui.custom` without overlay replaces the input slot like `/model`. */
|
|
97
|
+
class ModelPickerScreen extends Container {
|
|
98
|
+
private list: SelectList;
|
|
99
|
+
private doneFn: (result?: string) => void;
|
|
100
|
+
private closed = false;
|
|
101
|
+
|
|
102
|
+
constructor(
|
|
103
|
+
title: string,
|
|
104
|
+
items: SelectItem[],
|
|
105
|
+
selectedIndex: number,
|
|
106
|
+
theme: Theme,
|
|
107
|
+
done: (result?: string) => void,
|
|
108
|
+
) {
|
|
109
|
+
super();
|
|
110
|
+
this.doneFn = done;
|
|
111
|
+
this.addChild(new Text(title, 0, 0));
|
|
112
|
+
this.addChild(new Spacer(1));
|
|
113
|
+
this.list = new SelectList(
|
|
114
|
+
items,
|
|
115
|
+
Math.min(MAX_VISIBLE, Math.max(items.length, 1)),
|
|
116
|
+
buildSelectTheme(theme),
|
|
117
|
+
);
|
|
118
|
+
this.list.setSelectedIndex(selectedIndex);
|
|
119
|
+
this.list.onSelect = (item) => this.finish(item.value);
|
|
120
|
+
this.list.onCancel = () => this.finish(undefined);
|
|
121
|
+
this.addChild(this.list);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
handleInput(data: string): void {
|
|
125
|
+
// matchesKey covers kitty CSI-u Esc; SelectList cancel is the fallback.
|
|
126
|
+
if (matchesKey(data, "escape")) {
|
|
127
|
+
this.finish(undefined);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
this.list.handleInput(data);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private finish(result?: string): void {
|
|
134
|
+
if (this.closed) return;
|
|
135
|
+
this.closed = true;
|
|
136
|
+
this.doneFn(result);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
46
140
|
/**
|
|
47
141
|
* Register the `/wiki-model` slash command. Lets the user view the active
|
|
48
142
|
* background task model and choose another (or revert to the session model).
|
|
@@ -108,15 +202,31 @@ export function registerWikiModelCommand(pi: ExtensionAPI, runtime: Runtime): vo
|
|
|
108
202
|
return;
|
|
109
203
|
}
|
|
110
204
|
|
|
205
|
+
if (typeof ctx.ui.custom !== "function") {
|
|
206
|
+
ctx.ui.notify(
|
|
207
|
+
`LLM Wiki: this host does not support the model picker. Pass provider/id (e.g. anthropic/claude-haiku) or "session". Active: ${current}.`,
|
|
208
|
+
"warning",
|
|
209
|
+
);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
111
213
|
const available = (ctx.modelRegistry.getAvailable() as ModelLike[]) ?? [];
|
|
112
214
|
const pool = available.length > 0 ? available : (ctx.modelRegistry.getAll() as ModelLike[]);
|
|
113
|
-
const
|
|
114
|
-
const
|
|
215
|
+
const currentRef = runtime.config.taskModel ? modelRef(runtime.config.taskModel) : undefined;
|
|
216
|
+
const { items, selectedIndex } = buildPickerItems(pool, currentRef);
|
|
115
217
|
|
|
116
|
-
const picked = await ctx.ui.
|
|
218
|
+
const picked = await ctx.ui.custom<string | undefined>((_tui, theme, _keybindings, done) => {
|
|
219
|
+
return new ModelPickerScreen(
|
|
220
|
+
`Wiki background model (current: ${current})`,
|
|
221
|
+
items,
|
|
222
|
+
selectedIndex,
|
|
223
|
+
theme,
|
|
224
|
+
done,
|
|
225
|
+
);
|
|
226
|
+
});
|
|
117
227
|
if (picked === undefined) return; // cancelled
|
|
118
228
|
|
|
119
|
-
if (picked ===
|
|
229
|
+
if (picked === SESSION_VALUE) {
|
|
120
230
|
apply(undefined);
|
|
121
231
|
return;
|
|
122
232
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
-
import type { ExtensionAPI
|
|
2
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
3
3
|
import { Type } from "typebox";
|
|
4
4
|
import { scheduleReindex } from "./indexing.js";
|
|
5
5
|
import { createKnowledgeDocument, writeKnowledgeDocumentFile } from "./knowledge-document.js";
|
|
6
6
|
import { appendEvent, rebuildMetadataLight } from "./metadata.js";
|
|
7
7
|
import type { Runtime } from "./runtime.js";
|
|
8
|
-
import {
|
|
8
|
+
import { fmtDate, resolveVaultPaths, type VaultPaths } from "./utils.js";
|
|
9
9
|
import { assertWritableVault, inspectWritableVault } from "./vault-format.js";
|
|
10
10
|
|
|
11
11
|
// ─── Types ─────────────────────────────────────────────
|
|
@@ -3,9 +3,9 @@ import { join } from "node:path";
|
|
|
3
3
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
4
4
|
import { Type } from "typebox";
|
|
5
5
|
import {
|
|
6
|
+
cosineSimilarity,
|
|
6
7
|
type Embedder,
|
|
7
8
|
type EmbeddingStore,
|
|
8
|
-
cosineSimilarity,
|
|
9
9
|
normalizeVector,
|
|
10
10
|
readEmbeddingStore,
|
|
11
11
|
resolveEmbedder,
|
|
@@ -15,11 +15,11 @@ import type { Registry } from "./metadata.js";
|
|
|
15
15
|
import type { Runtime } from "./runtime.js";
|
|
16
16
|
import type { TaskConfig } from "./task-config.js";
|
|
17
17
|
import {
|
|
18
|
-
type VaultPaths,
|
|
19
18
|
getPersonalWikiPaths,
|
|
20
19
|
isPersonalVault,
|
|
21
20
|
readJson,
|
|
22
21
|
resolveVaultPaths,
|
|
22
|
+
type VaultPaths,
|
|
23
23
|
} from "./utils.js";
|
|
24
24
|
import { inspectVaultFormat } from "./vault-format.js";
|
|
25
25
|
|
|
@@ -111,7 +111,7 @@ function normalizeText(value: unknown): string {
|
|
|
111
111
|
return flattenSearchValue(value)
|
|
112
112
|
.toLowerCase()
|
|
113
113
|
.normalize("NFKC")
|
|
114
|
-
.replace(/[
|
|
114
|
+
.replace(/[-_./\\]+/g, " ")
|
|
115
115
|
.replace(/[\p{P}\p{S}]+/gu, " ")
|
|
116
116
|
.replace(/\s+/g, " ")
|
|
117
117
|
.trim();
|
|
@@ -333,7 +333,7 @@ function chunkPreview(heading: string, content: string): string {
|
|
|
333
333
|
function extractExpansionTerms(
|
|
334
334
|
scored: Scored[],
|
|
335
335
|
originalQuery: string,
|
|
336
|
-
|
|
336
|
+
_paths: VaultPaths,
|
|
337
337
|
maxTerms = 6,
|
|
338
338
|
): string[] {
|
|
339
339
|
const topResults = scored.slice(0, Math.min(3, scored.length));
|
|
@@ -7,7 +7,7 @@ import { applyWikilinkGate, buildWikilinkIndex } from "./knowledge-links.js";
|
|
|
7
7
|
import { appendEvent, rebuildMetadataLight } from "./metadata.js";
|
|
8
8
|
import type { Runtime } from "./runtime.js";
|
|
9
9
|
import { loadTaskConfig, resolveWikilinkValidation } from "./task-config.js";
|
|
10
|
-
import {
|
|
10
|
+
import { fmtDate, readJson, resolveVaultPaths, type VaultPaths } from "./utils.js";
|
|
11
11
|
import { assertWritableVault, inspectWritableVault } from "./vault-format.js";
|
|
12
12
|
|
|
13
13
|
// ─── Public API ────────────────────────────────────────
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
-
import { TASK_DEFAULTS, type TaskConfig
|
|
2
|
+
import { loadTaskConfig, noticesEnabled, TASK_DEFAULTS, type TaskConfig } from "./task-config.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Background-task runtime for the LLM Wiki (issue #64, part of #63).
|
|
@@ -11,11 +11,11 @@ import {
|
|
|
11
11
|
} from "@mariozechner/pi-tui";
|
|
12
12
|
import type { Runtime } from "./runtime.js";
|
|
13
13
|
import {
|
|
14
|
-
type SettingScope,
|
|
15
14
|
loadTaskConfig,
|
|
16
15
|
loadTaskConfigSources,
|
|
17
16
|
parseModelRef,
|
|
18
17
|
persistSetting,
|
|
18
|
+
type SettingScope,
|
|
19
19
|
trajectoriesEnabled,
|
|
20
20
|
} from "./task-config.js";
|
|
21
21
|
|
|
@@ -49,6 +49,7 @@ interface Ui {
|
|
|
49
49
|
keybindings: unknown,
|
|
50
50
|
done: (result?: unknown) => void,
|
|
51
51
|
) => unknown,
|
|
52
|
+
options?: { overlay?: boolean; overlayOptions?: Record<string, unknown> },
|
|
52
53
|
): Promise<unknown>;
|
|
53
54
|
}
|
|
54
55
|
|
|
@@ -363,6 +364,12 @@ function scopeTitle(scope: SettingScope): string {
|
|
|
363
364
|
return `\u{1F9E0} LLM Wiki Settings \u2014 ${scope === "global" ? "Global" : "Project"} (Esc to close)`;
|
|
364
365
|
}
|
|
365
366
|
|
|
367
|
+
// ponytail: overlay options matching pi-blackhole's config modal style
|
|
368
|
+
const OVERLAY_OPTS = {
|
|
369
|
+
overlay: true,
|
|
370
|
+
overlayOptions: { anchor: "center", width: "92%", maxHeight: "95%" },
|
|
371
|
+
};
|
|
372
|
+
|
|
366
373
|
async function showSettingsTui(ui: Ui, cwd: string, scope: SettingScope): Promise<void> {
|
|
367
374
|
if (typeof ui.custom !== "function") {
|
|
368
375
|
ui.notify(
|
|
@@ -445,7 +452,7 @@ async function showSettingsTui(ui: Ui, cwd: string, scope: SettingScope): Promis
|
|
|
445
452
|
);
|
|
446
453
|
screenRef = new SettingsScreen(scopeTitle(writeScope), list);
|
|
447
454
|
return screenRef;
|
|
448
|
-
});
|
|
455
|
+
}, OVERLAY_OPTS);
|
|
449
456
|
}
|
|
450
457
|
|
|
451
458
|
/**
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { open } from "node:fs/promises";
|
|
2
|
-
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
3
2
|
import { NodeHtmlMarkdown } from "node-html-markdown";
|
|
4
3
|
import { type ExecApi, exec } from "./utils.js";
|
|
5
4
|
|
|
@@ -270,8 +269,8 @@ async function extractWithMarkItDown(
|
|
|
270
269
|
if (!(await hasMarkItDown(pi, signal))) return "";
|
|
271
270
|
const mdResult = await exec(
|
|
272
271
|
pi,
|
|
273
|
-
"
|
|
274
|
-
["
|
|
272
|
+
"uvx",
|
|
273
|
+
["--from", "markitdown[docx,pdf]", "markitdown", source],
|
|
275
274
|
{ signal, timeout: markitdownTimeoutMs() },
|
|
276
275
|
);
|
|
277
276
|
return mdResult.stdout.trim() ? mdResult.stdout : "";
|
|
@@ -281,13 +280,21 @@ async function extractWithMarkItDown(
|
|
|
281
280
|
}
|
|
282
281
|
|
|
283
282
|
async function hasMarkItDown(pi: ExecApi, signal?: AbortSignal): Promise<boolean> {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
"
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
283
|
+
// Probe without shell to avoid CWE-78; try which/where then uvx --version
|
|
284
|
+
try {
|
|
285
|
+
await exec(pi, "which", ["uvx"], { signal });
|
|
286
|
+
return true;
|
|
287
|
+
} catch {}
|
|
288
|
+
try {
|
|
289
|
+
await exec(pi, "where", ["uvx"], { signal });
|
|
290
|
+
return true;
|
|
291
|
+
} catch {}
|
|
292
|
+
try {
|
|
293
|
+
await exec(pi, "uvx", ["--version"], { signal });
|
|
294
|
+
return true;
|
|
295
|
+
} catch {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
291
298
|
}
|
|
292
299
|
|
|
293
300
|
async function fetchTextUrl(pi: ExecApi, url: string, signal?: AbortSignal): Promise<string> {
|
|
@@ -350,7 +357,8 @@ function decodeHtmlEntities(text: string): string {
|
|
|
350
357
|
};
|
|
351
358
|
const lower = entity.toLowerCase();
|
|
352
359
|
if (map[lower]) return map[lower];
|
|
353
|
-
if (lower.startsWith("&#"))
|
|
360
|
+
if (lower.startsWith("&#"))
|
|
361
|
+
return String.fromCodePoint(Number.parseInt(entity.slice(2, -1), 10));
|
|
354
362
|
return entity;
|
|
355
363
|
});
|
|
356
364
|
}
|
|
@@ -369,7 +377,7 @@ function xmlToMarkdown(xml: string): string {
|
|
|
369
377
|
let prev = "";
|
|
370
378
|
while (prev !== text) {
|
|
371
379
|
prev = text;
|
|
372
|
-
text = text.replace(/<[a-zA-Z
|
|
380
|
+
text = text.replace(/<[a-zA-Z/!?][^>]*>/g, "");
|
|
373
381
|
}
|
|
374
382
|
text = text.replace(/</g, "");
|
|
375
383
|
|
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { copyFile } from "node:fs/promises";
|
|
3
3
|
import { extname, join } from "node:path";
|
|
4
|
-
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
5
4
|
import { createKnowledgeDocument, serializeKnowledgeDocument } from "./knowledge-document.js";
|
|
6
5
|
import { appendEvent } from "./metadata.js";
|
|
7
6
|
import {
|
|
8
|
-
type ExtractedContent,
|
|
9
7
|
binaryExtractionFailureMessage,
|
|
10
8
|
detectBinaryMagicBytes,
|
|
9
|
+
type ExtractedContent,
|
|
11
10
|
extractUrlContent,
|
|
12
11
|
fileExtractorFor,
|
|
13
12
|
} from "./source-extractors.js";
|
|
14
13
|
import {
|
|
15
14
|
type ExecApi,
|
|
16
|
-
type VaultPaths,
|
|
17
15
|
exec,
|
|
18
16
|
fmtDate,
|
|
19
17
|
nextSourceId,
|
|
20
18
|
readText,
|
|
19
|
+
type VaultPaths,
|
|
21
20
|
writeJson,
|
|
22
21
|
} from "./utils.js";
|
|
23
22
|
import { assertWritableVault } from "./vault-format.js";
|
|
@@ -2,8 +2,8 @@ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
3
|
import { parse as parseYaml } from "yaml";
|
|
4
4
|
import {
|
|
5
|
-
type HostKind,
|
|
6
5
|
detectHost,
|
|
6
|
+
type HostKind,
|
|
7
7
|
listGlobalSettingsFiles,
|
|
8
8
|
listProjectSettingsFiles,
|
|
9
9
|
resolveGlobalSettingsPath,
|
|
@@ -159,6 +159,16 @@ export interface TaskConfig {
|
|
|
159
159
|
* Default "warn". See `resolveWikilinkValidation`.
|
|
160
160
|
*/
|
|
161
161
|
wikilinkValidation?: WikilinkValidationMode;
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* User-defined page types for wiki_ensure_page (issue #169). Merges with
|
|
165
|
+
* the 7 built-in types (entity, concept, synthesis, analysis, requirement,
|
|
166
|
+
* skill, case). Each key is the type name; each value is the folder name
|
|
167
|
+
* inside wiki/. Example: { "decision": "decisions", "metric": "metrics" }
|
|
168
|
+
* → wiki_ensure_page(type="decision") creates wiki/decisions/<slug>.md.
|
|
169
|
+
* Custom types use a generic page template (no per-type templates).
|
|
170
|
+
*/
|
|
171
|
+
customTypes?: Record<string, string>;
|
|
162
172
|
}
|
|
163
173
|
|
|
164
174
|
export const TASK_DEFAULTS: TaskConfig = {};
|
|
@@ -286,6 +296,18 @@ function readNamespacedConfig(path: string): Partial<TaskConfig> {
|
|
|
286
296
|
out.wikilinkValidation = wl as WikilinkValidationMode;
|
|
287
297
|
}
|
|
288
298
|
|
|
299
|
+
const ct = section.customTypes;
|
|
300
|
+
if (ct && typeof ct === "object" && !Array.isArray(ct)) {
|
|
301
|
+
const entries = Object.entries(ct as Record<string, unknown>);
|
|
302
|
+
const valid: Record<string, string> = {};
|
|
303
|
+
for (const [k, v] of entries) {
|
|
304
|
+
if (typeof k === "string" && typeof v === "string" && k && v) {
|
|
305
|
+
valid[k] = v;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (Object.keys(valid).length) out.customTypes = valid;
|
|
309
|
+
}
|
|
310
|
+
|
|
289
311
|
return out;
|
|
290
312
|
} catch {
|
|
291
313
|
return {};
|
|
@@ -474,6 +496,7 @@ const KNOWN_KEYS = [
|
|
|
474
496
|
"synthesisLanguage",
|
|
475
497
|
"synthesisMaxTokens",
|
|
476
498
|
"wikilinkValidation",
|
|
499
|
+
"customTypes",
|
|
477
500
|
] as const;
|
|
478
501
|
|
|
479
502
|
export function loadTaskConfigSources(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, mkdirSync,
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join, relative } from "node:path";
|
|
3
3
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
4
4
|
import { Type } from "typebox";
|
|
@@ -17,18 +17,18 @@ import {
|
|
|
17
17
|
buildWikilinkIndex,
|
|
18
18
|
} from "./knowledge-links.js";
|
|
19
19
|
import { repairLegacyKnowledgeDocuments } from "./legacy-repair.js";
|
|
20
|
-
import { type Registry,
|
|
20
|
+
import { appendEvent, type Registry, rebuildMetadata, rebuildMetadataLight } from "./metadata.js";
|
|
21
21
|
import type { Runtime } from "./runtime.js";
|
|
22
22
|
import { captureFile, captureText, captureUrl } from "./source-packet.js";
|
|
23
23
|
import { loadTaskConfig, parseModelRef, resolveWikilinkValidation } from "./task-config.js";
|
|
24
24
|
import {
|
|
25
|
-
type VaultPaths,
|
|
26
25
|
detectVaultFormat,
|
|
27
26
|
fmtDate,
|
|
28
27
|
getVaultPaths,
|
|
29
28
|
readJson,
|
|
30
29
|
resolveVaultPaths,
|
|
31
30
|
slugify,
|
|
31
|
+
type VaultPaths,
|
|
32
32
|
writeJson,
|
|
33
33
|
} from "./utils.js";
|
|
34
34
|
import {
|
|
@@ -519,7 +519,7 @@ export function registerWikiEnsurePage(pi: ExtensionAPI, runtime?: Runtime): voi
|
|
|
519
519
|
parameters: Type.Object({
|
|
520
520
|
type: Type.String({
|
|
521
521
|
description:
|
|
522
|
-
"Page type: entity | concept | synthesis | analysis | requirement | skill | case",
|
|
522
|
+
"Page type: entity | concept | synthesis | analysis | requirement | skill | case (built-in) or any user-defined type from llm-wiki.customTypes config",
|
|
523
523
|
}),
|
|
524
524
|
title: Type.String({ description: "Page title" }),
|
|
525
525
|
content: Type.Optional(
|
|
@@ -542,17 +542,8 @@ export function registerWikiEnsurePage(pi: ExtensionAPI, runtime?: Runtime): voi
|
|
|
542
542
|
};
|
|
543
543
|
}
|
|
544
544
|
|
|
545
|
-
const
|
|
546
|
-
|
|
547
|
-
| "concept"
|
|
548
|
-
| "synthesis"
|
|
549
|
-
| "analysis"
|
|
550
|
-
| "requirement"
|
|
551
|
-
| "skill"
|
|
552
|
-
| "case";
|
|
553
|
-
const slug = slugify(params.title);
|
|
554
|
-
|
|
555
|
-
const folderMap: Record<string, string> = {
|
|
545
|
+
const config = loadTaskConfig(ctx.cwd);
|
|
546
|
+
const builtInFolderMap: Record<string, string> = {
|
|
556
547
|
entity: "entities",
|
|
557
548
|
concept: "concepts",
|
|
558
549
|
synthesis: "syntheses",
|
|
@@ -561,6 +552,9 @@ export function registerWikiEnsurePage(pi: ExtensionAPI, runtime?: Runtime): voi
|
|
|
561
552
|
skill: "skills",
|
|
562
553
|
case: "cases",
|
|
563
554
|
};
|
|
555
|
+
const folderMap = { ...builtInFolderMap, ...config.customTypes };
|
|
556
|
+
const type = params.type as string;
|
|
557
|
+
const slug = slugify(params.title);
|
|
564
558
|
const folder = folderMap[type] || "concepts";
|
|
565
559
|
const pagePath = join(paths.wiki, folder, `${slug}.md`);
|
|
566
560
|
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { existsSync, mkdirSync,
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
4
4
|
import { Type } from "typebox";
|
|
5
5
|
import { appendEvent, rebuildMetadataLight } from "./metadata.js";
|
|
6
6
|
import { searchWikiLayered } from "./recall.js";
|
|
7
7
|
import {
|
|
8
|
-
type VaultPaths,
|
|
9
8
|
fmtDate,
|
|
10
9
|
nextTrajectoryId,
|
|
11
10
|
readJson,
|
|
12
11
|
resolveVaultPaths,
|
|
12
|
+
type VaultPaths,
|
|
13
13
|
writeJson,
|
|
14
14
|
} from "./utils.js";
|
|
15
15
|
import { assertWritableVault, inspectWritableVault } from "./vault-format.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, lstatSync,
|
|
1
|
+
import { existsSync, lstatSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { isAbsolute, join, relative, sep } from "node:path";
|
|
3
3
|
import {
|
|
4
4
|
type KnowledgeDiagnostic,
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
parseKnowledgeDocument,
|
|
7
7
|
parseMarkdownFrontmatter,
|
|
8
8
|
} from "./knowledge-document.js";
|
|
9
|
-
import { type VaultPaths
|
|
9
|
+
import { relativePhysicalPath, type VaultPaths } from "./utils.js";
|
|
10
10
|
|
|
11
11
|
export type KnowledgeFormat = "legacy" | "okf-0.2";
|
|
12
12
|
|