@zosmaai/pi-llm-wiki 0.11.3 → 0.11.5
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 +10 -0
- package/README.de.md +8 -0
- package/README.es.md +8 -0
- package/README.fr.md +8 -0
- package/README.hi.md +8 -0
- package/README.ja.md +8 -0
- package/README.ko.md +8 -0
- package/README.md +88 -2
- package/README.pt.md +8 -0
- package/README.ru.md +8 -0
- package/README.zh.md +8 -0
- package/assets/wiki-dashboard.png +0 -0
- package/commands/wiki-digest.md +28 -0
- package/commands/wiki-discover.md +30 -0
- package/commands/wiki-ingest.md +37 -0
- package/commands/wiki-init.md +30 -0
- package/commands/wiki-lint.md +25 -0
- package/commands/wiki-query.md +37 -0
- package/commands/wiki-record.md +36 -0
- package/commands/wiki-req.md +56 -0
- package/commands/wiki-retro.md +35 -0
- package/commands/wiki-run.md +31 -0
- package/commands/wiki-skills.md +26 -0
- package/commands/wiki-status.md +16 -0
- package/dist/extensions/llm-wiki/lib/dashboard-command.js +86 -0
- package/dist/extensions/llm-wiki/lib/dashboard.js +175 -0
- package/dist/extensions/llm-wiki/lib/guardrails.js +30 -1
- package/dist/extensions/llm-wiki/lib/host.js +117 -0
- package/dist/extensions/llm-wiki/lib/ingest-worker.js +2 -1
- package/dist/extensions/llm-wiki/lib/knowledge-document.js +20 -2
- package/dist/extensions/llm-wiki/lib/knowledge-links.js +6 -3
- package/dist/extensions/llm-wiki/lib/metadata.js +1 -1
- package/dist/extensions/llm-wiki/lib/observation.js +31 -3
- package/dist/extensions/llm-wiki/lib/settings-command.js +377 -0
- package/dist/extensions/llm-wiki/lib/task-config.js +145 -43
- package/dist/extensions/llm-wiki/lib/utils.js +59 -16
- package/docs/api.md +24 -1
- package/docs/commands.md +6 -1
- package/docs/configuration.md +62 -11
- package/docs/superpowers/plans/2026-08-09-qmd-retrieval-phase-1-quality-baseline-and-compatibility.md +1520 -0
- package/docs/superpowers/roadmaps/2026-08-09-qmd-retrieval-roadmap.md +448 -0
- package/docs/superpowers/specs/2026-08-08-qmd-retrieval-design.md +806 -0
- package/extensions/llm-wiki/index.ts +48 -6
- package/extensions/llm-wiki/lib/dashboard-command.ts +106 -0
- package/extensions/llm-wiki/lib/dashboard.ts +210 -0
- package/extensions/llm-wiki/lib/guardrails.ts +26 -1
- package/extensions/llm-wiki/lib/host.ts +145 -0
- package/extensions/llm-wiki/lib/ingest-worker.ts +4 -0
- package/extensions/llm-wiki/lib/knowledge-document.ts +20 -2
- package/extensions/llm-wiki/lib/knowledge-links.ts +7 -3
- package/extensions/llm-wiki/lib/metadata.ts +1 -1
- package/extensions/llm-wiki/lib/observation.ts +37 -4
- package/extensions/llm-wiki/lib/settings-command.ts +483 -0
- package/extensions/llm-wiki/lib/task-config.ts +208 -46
- package/extensions/llm-wiki/lib/utils.ts +55 -14
- package/package.json +15 -4
- package/prompts/wiki-ingest.md +1 -0
- package/prompts/wiki-req.md +1 -0
- package/prompts/wiki-retro.md +1 -0
- package/skills/llm-wiki/SKILL.md +11 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname
|
|
3
|
-
import {
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { parse as parseYaml } from "yaml";
|
|
4
|
+
import { detectHost, listGlobalSettingsFiles, listProjectSettingsFiles, resolveGlobalSettingsPath, resolveProjectSettingsPath, } from "./host.js";
|
|
4
5
|
export const TASK_DEFAULTS = {};
|
|
5
6
|
/**
|
|
6
7
|
* Resolve whether user-facing wiki notices are enabled (issue #77). Defaults
|
|
@@ -9,6 +10,16 @@ export const TASK_DEFAULTS = {};
|
|
|
9
10
|
export function noticesEnabled(config) {
|
|
10
11
|
return config?.notices !== false;
|
|
11
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Resolve whether the personal vault may serve as this project's ambient
|
|
15
|
+
* vault. Explicit `ambientPersonalVault` wins; otherwise the host decides
|
|
16
|
+
* (see the field docs on {@link TaskConfig.ambientPersonalVault}).
|
|
17
|
+
*/
|
|
18
|
+
export function personalVaultIsAmbient(config, host = detectHost()) {
|
|
19
|
+
if (typeof config?.ambientPersonalVault === "boolean")
|
|
20
|
+
return config.ambientPersonalVault;
|
|
21
|
+
return host === "pi";
|
|
22
|
+
}
|
|
12
23
|
/**
|
|
13
24
|
* Resolve whether agent-trajectory working-memory is enabled (issue #80).
|
|
14
25
|
* INVERSE polarity of `noticesEnabled`: defaults to `false`; only an explicit
|
|
@@ -64,6 +75,9 @@ function readNamespacedConfig(path) {
|
|
|
64
75
|
if (typeof section.notices === "boolean") {
|
|
65
76
|
out.notices = section.notices;
|
|
66
77
|
}
|
|
78
|
+
if (typeof section.ambientPersonalVault === "boolean") {
|
|
79
|
+
out.ambientPersonalVault = section.ambientPersonalVault;
|
|
80
|
+
}
|
|
67
81
|
if (typeof section.trajectories === "boolean") {
|
|
68
82
|
out.trajectories = section.trajectories;
|
|
69
83
|
}
|
|
@@ -73,6 +87,10 @@ function readNamespacedConfig(path) {
|
|
|
73
87
|
if (canonical)
|
|
74
88
|
out.synthesisLanguage = canonical;
|
|
75
89
|
}
|
|
90
|
+
const maxTokens = section.synthesisMaxTokens;
|
|
91
|
+
if (typeof maxTokens === "number" && Number.isFinite(maxTokens) && maxTokens > 0) {
|
|
92
|
+
out.synthesisMaxTokens = Math.floor(maxTokens);
|
|
93
|
+
}
|
|
76
94
|
return out;
|
|
77
95
|
}
|
|
78
96
|
catch {
|
|
@@ -121,14 +139,19 @@ export function validateSynthesisLanguage(tag) {
|
|
|
121
139
|
return canonical[0];
|
|
122
140
|
}
|
|
123
141
|
/**
|
|
124
|
-
* Read a settings
|
|
142
|
+
* Read a settings file as a plain object, or `{}` when it is absent or
|
|
125
143
|
* corrupt. Reads directly (no `existsSync` pre-check) so there is no
|
|
126
144
|
* check-then-use race: a missing file throws ENOENT, which the catch treats
|
|
127
145
|
* the same as an empty file.
|
|
146
|
+
*
|
|
147
|
+
* `config.yml` / `config.yaml` are parsed as YAML — that is the format oh-my-pi
|
|
148
|
+
* migrates its settings to. Everything else is JSON. JSON is a YAML subset, so
|
|
149
|
+
* the YAML parser also accepts a `.yml` file that actually holds JSON.
|
|
128
150
|
*/
|
|
129
151
|
function readSettingsObject(path) {
|
|
130
152
|
try {
|
|
131
|
-
const
|
|
153
|
+
const text = readFileSync(path, "utf-8");
|
|
154
|
+
const parsed = path.endsWith(".yml") || path.endsWith(".yaml") ? parseYaml(text) : JSON.parse(text);
|
|
132
155
|
if (parsed && typeof parsed === "object")
|
|
133
156
|
return parsed;
|
|
134
157
|
}
|
|
@@ -138,64 +161,143 @@ function readSettingsObject(path) {
|
|
|
138
161
|
return {};
|
|
139
162
|
}
|
|
140
163
|
/**
|
|
141
|
-
*
|
|
142
|
-
* file `<cwd>/.pi/settings.json` under the namespaced `llm-wiki` key (issue
|
|
143
|
-
* #69). Project settings win over global in `loadTaskConfig`, so this takes
|
|
144
|
-
* effect immediately on the next config load. Other top-level keys and other
|
|
145
|
-
* `llm-wiki` settings are preserved; passing `undefined` removes the key
|
|
146
|
-
* (reverting to the session model).
|
|
164
|
+
* Rewrite the `llm-wiki` section of the global settings file.
|
|
147
165
|
*/
|
|
148
|
-
|
|
149
|
-
const settingsPath =
|
|
166
|
+
function updateGlobalSection(mutate) {
|
|
167
|
+
const settingsPath = resolveGlobalSettingsPath();
|
|
150
168
|
const raw = readSettingsObject(settingsPath);
|
|
151
169
|
const existing = raw[SETTINGS_KEY];
|
|
152
170
|
const section = existing && typeof existing === "object" ? { ...existing } : {};
|
|
153
|
-
|
|
154
|
-
section.taskModel = { provider: model.provider, id: model.id };
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
// biome-ignore lint/performance/noDelete: one-off settings rewrite, not a hot path; removing the key (vs setting undefined) keeps the JSON clean
|
|
158
|
-
delete section.taskModel;
|
|
159
|
-
}
|
|
171
|
+
mutate(section);
|
|
160
172
|
raw[SETTINGS_KEY] = section;
|
|
161
173
|
mkdirSync(dirname(settingsPath), { recursive: true });
|
|
162
174
|
writeFileSync(settingsPath, `${JSON.stringify(raw, null, 2)}\n`, "utf-8");
|
|
163
175
|
}
|
|
164
176
|
/**
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
177
|
+
* Rewrite the `llm-wiki` section of the project settings file, preserving every
|
|
178
|
+
* other top-level key and every other setting in the section.
|
|
179
|
+
*
|
|
180
|
+
* The target file is chosen by `resolveProjectSettingsPath` — `.pi/settings.json`
|
|
181
|
+
* or `.omp/settings.json` depending on host and on what already exists — and is
|
|
182
|
+
* always JSON, which both hosts read.
|
|
170
183
|
*/
|
|
171
|
-
|
|
172
|
-
const settingsPath =
|
|
184
|
+
function updateProjectSection(cwd, mutate) {
|
|
185
|
+
const settingsPath = resolveProjectSettingsPath(cwd);
|
|
173
186
|
const raw = readSettingsObject(settingsPath);
|
|
174
187
|
const existing = raw[SETTINGS_KEY];
|
|
175
188
|
const section = existing && typeof existing === "object" ? { ...existing } : {};
|
|
176
|
-
|
|
177
|
-
section.trajectories = true;
|
|
178
|
-
}
|
|
179
|
-
else {
|
|
180
|
-
// biome-ignore lint/performance/noDelete: one-off settings rewrite, not a hot path; removing the key keeps the JSON clean (default is off)
|
|
181
|
-
delete section.trajectories;
|
|
182
|
-
}
|
|
189
|
+
mutate(section);
|
|
183
190
|
raw[SETTINGS_KEY] = section;
|
|
184
191
|
mkdirSync(dirname(settingsPath), { recursive: true });
|
|
185
192
|
writeFileSync(settingsPath, `${JSON.stringify(raw, null, 2)}\n`, "utf-8");
|
|
186
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* Persist (or clear) the wiki background `taskModel` in the PROJECT settings
|
|
196
|
+
* file under the namespaced `llm-wiki` key (issue #69). Project settings win
|
|
197
|
+
* over global in `loadTaskConfig`, so this takes effect immediately on the next
|
|
198
|
+
* config load. Passing `undefined` removes the key (reverting to the session
|
|
199
|
+
* model).
|
|
200
|
+
*/
|
|
201
|
+
export function persistTaskModel(cwd, model) {
|
|
202
|
+
updateProjectSection(cwd, (section) => {
|
|
203
|
+
if (model) {
|
|
204
|
+
section.taskModel = { provider: model.provider, id: model.id };
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
// biome-ignore lint/performance/noDelete: one-off settings rewrite, not a hot path; removing the key (vs setting undefined) keeps the JSON clean
|
|
208
|
+
delete section.taskModel;
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Persist the agent-trajectory flag in the PROJECT settings file under the
|
|
214
|
+
* namespaced `llm-wiki` key (issue #80). Mirrors `persistTaskModel`: `true`
|
|
215
|
+
* writes `trajectories: true`; `false` removes the key (reverting to the
|
|
216
|
+
* default-off behavior).
|
|
217
|
+
*/
|
|
218
|
+
export function persistTrajectoriesEnabled(cwd, enabled) {
|
|
219
|
+
updateProjectSection(cwd, (section) => {
|
|
220
|
+
if (enabled) {
|
|
221
|
+
section.trajectories = true;
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
// biome-ignore lint/performance/noDelete: one-off settings rewrite, not a hot path; removing the key keeps the JSON clean (default is off)
|
|
225
|
+
delete section.trajectories;
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Merge the `llm-wiki` section from every settings file both hosts may use,
|
|
231
|
+
* lowest precedence first: built-in defaults, then user-level files, then
|
|
232
|
+
* project-level files. Absent files contribute nothing.
|
|
233
|
+
*/
|
|
187
234
|
export function loadTaskConfig(cwd) {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
235
|
+
const config = { ...TASK_DEFAULTS };
|
|
236
|
+
for (const path of listGlobalSettingsFiles()) {
|
|
237
|
+
Object.assign(config, readNamespacedConfig(path));
|
|
191
238
|
}
|
|
192
|
-
|
|
193
|
-
|
|
239
|
+
for (const path of listProjectSettingsFiles(cwd)) {
|
|
240
|
+
Object.assign(config, readNamespacedConfig(path));
|
|
194
241
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
242
|
+
return config;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Resolve where each setting is defined: project > global > default.
|
|
246
|
+
*/
|
|
247
|
+
/** All known setting keys — needed because TASK_DEFAULTS is {} (zero-config). */
|
|
248
|
+
const KNOWN_KEYS = [
|
|
249
|
+
"taskModel",
|
|
250
|
+
"embeddingProvider",
|
|
251
|
+
"embeddingModel",
|
|
252
|
+
"embeddingBaseUrl",
|
|
253
|
+
"embeddingApiKey",
|
|
254
|
+
"embeddingApiKeyEnv",
|
|
255
|
+
"semanticWeight",
|
|
256
|
+
"recallLinksThreshold",
|
|
257
|
+
"recallSkillInlineMax",
|
|
258
|
+
"notices",
|
|
259
|
+
"ambientPersonalVault",
|
|
260
|
+
"trajectories",
|
|
261
|
+
"synthesisLanguage",
|
|
262
|
+
"synthesisMaxTokens",
|
|
263
|
+
];
|
|
264
|
+
export function loadTaskConfigSources(cwd) {
|
|
265
|
+
const globalResult = {};
|
|
266
|
+
for (const path of listGlobalSettingsFiles()) {
|
|
267
|
+
Object.assign(globalResult, readNamespacedConfig(path));
|
|
268
|
+
}
|
|
269
|
+
const projectResult = {};
|
|
270
|
+
for (const path of listProjectSettingsFiles(cwd)) {
|
|
271
|
+
Object.assign(projectResult, readNamespacedConfig(path));
|
|
272
|
+
}
|
|
273
|
+
const effective = loadTaskConfig(cwd);
|
|
274
|
+
const out = {};
|
|
275
|
+
for (const key of KNOWN_KEYS) {
|
|
276
|
+
if (key in projectResult)
|
|
277
|
+
out[key] = { value: projectResult[key], source: "project" };
|
|
278
|
+
else if (key in globalResult)
|
|
279
|
+
out[key] = { value: globalResult[key], source: "global" };
|
|
280
|
+
else
|
|
281
|
+
out[key] = { value: effective[key], source: "default" };
|
|
282
|
+
}
|
|
283
|
+
return out;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Generic setting persist: writes any single setting to the chosen scope.
|
|
287
|
+
*/
|
|
288
|
+
export function persistSetting(cwd, scope, key, value) {
|
|
289
|
+
const mutate = (section) => {
|
|
290
|
+
if (value === undefined || value === null) {
|
|
291
|
+
delete section[key];
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
section[key] = value;
|
|
295
|
+
}
|
|
200
296
|
};
|
|
297
|
+
if (scope === "project") {
|
|
298
|
+
updateProjectSection(cwd, mutate);
|
|
299
|
+
}
|
|
300
|
+
else if (scope === "global") {
|
|
301
|
+
updateGlobalSection(mutate);
|
|
302
|
+
}
|
|
201
303
|
}
|
|
@@ -82,20 +82,46 @@ export function migrateDoubledPersonalVault(parentRoot = getPersonalWikiRoot())
|
|
|
82
82
|
/**
|
|
83
83
|
* Check if a vault is the personal wiki location.
|
|
84
84
|
* Used in layered recall to avoid double-counting.
|
|
85
|
+
*
|
|
86
|
+
* Compares PHYSICAL paths, not strings. On image-based ("atomic") Linux
|
|
87
|
+
* distributions `/home` is a symlink to `var/home`, so `homedir()` yields the
|
|
88
|
+
* `$HOME` string (`/home/u`) while `process.cwd()` — and therefore the root
|
|
89
|
+
* `resolveVaultRoot()` walks up to — yields `/var/home/u`. A string compare
|
|
90
|
+
* calls the personal vault a project vault, which makes layered recall search
|
|
91
|
+
* the same vault twice and `vaultPageCount()` double-count it.
|
|
92
|
+
*
|
|
93
|
+
* Exact equality, NOT containment: a vault nested under the home directory
|
|
94
|
+
* (`~/projects/foo/.llm-wiki`) is a project vault and must stay one.
|
|
85
95
|
*/
|
|
86
96
|
export function isPersonalVault(paths) {
|
|
87
|
-
|
|
97
|
+
const personalRoot = getPersonalWikiRoot();
|
|
98
|
+
// Fast path: identical strings need no filesystem syscalls.
|
|
99
|
+
if (paths.root === personalRoot)
|
|
100
|
+
return true;
|
|
101
|
+
try {
|
|
102
|
+
return relativePhysicalPath(personalRoot, paths.root) === "";
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// Unresolvable path (permissions, symlink cycle): fall back to "not
|
|
106
|
+
// personal" so layered recall degrades to searching both vaults rather
|
|
107
|
+
// than silently dropping the personal layer.
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
88
110
|
}
|
|
89
111
|
/**
|
|
90
|
-
* Resolve vault root
|
|
112
|
+
* Resolve the vault root that belongs to THIS project, or `null` when the
|
|
113
|
+
* project has none.
|
|
91
114
|
*
|
|
92
115
|
* Priority:
|
|
93
|
-
* 1. cwd has
|
|
94
|
-
* 2.
|
|
95
|
-
* 3.
|
|
96
|
-
*
|
|
116
|
+
* 1. cwd has `.llm-wiki/` (or legacy `.wiki/`) → project wiki (explicit)
|
|
117
|
+
* 2. `WIKI_HOME` → user-selected root, explicit enough to count as the project's
|
|
118
|
+
* 3. Walk up from cwd → parent project wiki (monorepo / nested workspace)
|
|
119
|
+
*
|
|
120
|
+
* Deliberately does NOT fall back to the personal wiki: callers that need the
|
|
121
|
+
* fallback use {@link resolveVaultRoot}, callers that must distinguish "this
|
|
122
|
+
* project has a wiki" from "some wiki exists somewhere" use this.
|
|
97
123
|
*/
|
|
98
|
-
export function
|
|
124
|
+
export function resolveProjectVaultRoot(cwd) {
|
|
99
125
|
// A vault rooted at cwd is always the project-local choice.
|
|
100
126
|
if (detectVaultFormat(cwd) !== "none")
|
|
101
127
|
return cwd;
|
|
@@ -103,19 +129,36 @@ export function resolveVaultRoot(cwd) {
|
|
|
103
129
|
// over an unrelated personal vault found while walking parent directories.
|
|
104
130
|
if (process.env.WIKI_HOME)
|
|
105
131
|
return process.env.WIKI_HOME;
|
|
106
|
-
// Walk up looking for a vault sentinel (new or legacy)
|
|
132
|
+
// Walk up looking for a vault sentinel (new or legacy).
|
|
107
133
|
let dir = cwd;
|
|
108
134
|
while (dir !== dirname(dir)) {
|
|
109
135
|
dir = dirname(dir);
|
|
110
|
-
if (detectVaultFormat(dir)
|
|
111
|
-
|
|
136
|
+
if (detectVaultFormat(dir) === "none")
|
|
137
|
+
continue;
|
|
138
|
+
// Skip the personal vault: it is an ancestor of EVERY project under the
|
|
139
|
+
// home directory (`~/projects/foo`, and on Windows even the temp dir), so
|
|
140
|
+
// counting it here would report a project vault for directories that have
|
|
141
|
+
// none. `resolveVaultRoot` still falls back to it explicitly.
|
|
142
|
+
if (isPersonalVault(getVaultPaths(dir)))
|
|
143
|
+
continue;
|
|
144
|
+
return dir;
|
|
112
145
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Resolve vault root from cwd with personal fallback.
|
|
150
|
+
*
|
|
151
|
+
* Priority:
|
|
152
|
+
* 1-3. {@link resolveProjectVaultRoot}
|
|
153
|
+
* 4. Personal wiki root (`~`, or `WIKI_HOME`) — used whether or not it already
|
|
154
|
+
* holds a vault, so first-run bootstrap has somewhere to write.
|
|
155
|
+
*/
|
|
156
|
+
export function resolveVaultRoot(cwd) {
|
|
157
|
+
// Realpath the personal fallback so a symlinked `$HOME` (atomic-OS layouts)
|
|
158
|
+
// yields the PHYSICAL root the ancestor walk used to return — the #145
|
|
159
|
+
// regression guard pins that. `realpathWithMissingTail` also covers first-run
|
|
160
|
+
// bootstrap, where the personal root does not exist on disk yet.
|
|
161
|
+
return resolveProjectVaultRoot(cwd) ?? realpathWithMissingTail(getPersonalWikiRoot());
|
|
119
162
|
}
|
|
120
163
|
/** Get all vault paths for the new (.llm-wiki) layout. */
|
|
121
164
|
export function getVaultPaths(root) {
|
package/docs/api.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
All tools registered by the extension. Parameters marked `?` are optional.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
14 tools are always registered. The 3 agent-trajectory tools
|
|
6
6
|
(`wiki_capture_trajectory`, `wiki_distill_skills`, `wiki_recall_skill`) are **opt-in,
|
|
7
7
|
off by default** (issue #80) — they are only registered when `llm-wiki.trajectories`
|
|
8
8
|
is `true`; enable with `/wiki-trajectories on`.
|
|
@@ -458,6 +458,29 @@ Returns empty `matches: []` with a hint to capture work via `wiki_capture_trajec
|
|
|
458
458
|
|
|
459
459
|
---
|
|
460
460
|
|
|
461
|
+
## wiki_reindex_embeddings
|
|
462
|
+
|
|
463
|
+
Backfill or refresh semantic embeddings for the vault. Embeds pages that are new or stale
|
|
464
|
+
(content changed); pass `force` to re-embed everything. No-op when no embedding provider is
|
|
465
|
+
configured — set `llm-wiki.embeddingProvider` first (see `docs/configuration.md`).
|
|
466
|
+
|
|
467
|
+
**Parameters**
|
|
468
|
+
|
|
469
|
+
| Name | Type | Required | Description |
|
|
470
|
+
|------|------|----------|-------------|
|
|
471
|
+
| `force` | `boolean` | — | Re-embed every page, ignoring staleness (default: `false`) |
|
|
472
|
+
|
|
473
|
+
**Returns**
|
|
474
|
+
|
|
475
|
+
```
|
|
476
|
+
details: { enabled: true, model: string, embedded: number, skipped: number, pruned: number }
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
Fails soft with `details: { enabled: false }` plus a hint to configure `embeddingProvider` when
|
|
480
|
+
no embedding provider is set.
|
|
481
|
+
|
|
482
|
+
---
|
|
483
|
+
|
|
461
484
|
## Error Shape
|
|
462
485
|
|
|
463
486
|
All tools return `isError: true` in their result when a hard error occurs (no vault found, missing
|
package/docs/commands.md
CHANGED
|
@@ -17,10 +17,13 @@
|
|
|
17
17
|
| `/wiki-trajectories` | Enable/disable agent working-memory (`on`/`off`, opt-in) |
|
|
18
18
|
| `/wiki-record` | Capture the completed task's trajectory (requires trajectories enabled) |
|
|
19
19
|
| `/wiki-skills` | Search distilled skills + past cases (requires trajectories enabled) |
|
|
20
|
+
| `/wiki-req` | Decompose a concept into atomic requirement pages |
|
|
21
|
+
| `/wiki-settings` | Browse or change all `llm-wiki` settings (project or global scope) |
|
|
22
|
+
| `/wiki-dashboard` | Read-only vault health: pages, freshness, activity, ingest queue, backlinks, embeddings |
|
|
20
23
|
|
|
21
24
|
## Extension Tools
|
|
22
25
|
|
|
23
|
-
The extension always registers
|
|
26
|
+
The extension always registers 14 tools the LLM can call directly. The 3 agent-trajectory
|
|
24
27
|
tools (`wiki_capture_trajectory`, `wiki_distill_skills`, `wiki_recall_skill`) are **opt-in,
|
|
25
28
|
off by default** (issue #80) — registered only when `llm-wiki.trajectories` is enabled
|
|
26
29
|
(`/wiki-trajectories on`).
|
|
@@ -36,7 +39,9 @@ off by default** (issue #80) — registered only when `llm-wiki.trajectories` is
|
|
|
36
39
|
| `wiki_search` | Search the wiki registry |
|
|
37
40
|
| `wiki_lint` | Health check with auto-fix |
|
|
38
41
|
| `wiki_status` | Instant stats |
|
|
42
|
+
| `wiki_observe` | Record a timestamped observation from the current session |
|
|
39
43
|
| `wiki_rebuild_meta` | Force metadata rebuild |
|
|
44
|
+
| `wiki_reindex_embeddings` | Refresh semantic embeddings (no-op when no embedding provider) |
|
|
40
45
|
| `wiki_log_event` | Record custom event |
|
|
41
46
|
| `wiki_watch` | Schedule auto-updates |
|
|
42
47
|
| `wiki_capture_trajectory` | Capture the completed task's tool-call trajectory |
|
package/docs/configuration.md
CHANGED
|
@@ -31,17 +31,44 @@ The personal vault lives at `~/.llm-wiki/` (or `$WIKI_HOME`) and is always avail
|
|
|
31
31
|
| ----------------------------- | ----------- | ----------------------------------------------- |
|
|
32
32
|
| `WIKI_HOME` | `~/.llm-wiki` | Override the personal wiki vault location |
|
|
33
33
|
| `WIKI_MARKITDOWN_TIMEOUT_MS` | 180000 | Timeout (ms) for MarkItDown PDF/text extraction |
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
34
|
+
| `LLM_WIKI_HOST` | auto | Force the host layout: `pi` or `omp` |
|
|
35
|
+
|
|
36
|
+
## Agent Settings
|
|
37
|
+
|
|
38
|
+
Runtime settings for the wiki's background tasks live under the `llm-wiki`
|
|
39
|
+
namespace of the host's settings file. Both host layouts are read and merged,
|
|
40
|
+
lowest precedence first:
|
|
41
|
+
|
|
42
|
+
1. `<agentDir>/settings.json`, then `config.yml` / `config.yaml`
|
|
43
|
+
— `~/.pi/agent` under pi, `~/.omp/agent` under oh-my-pi
|
|
44
|
+
2. `<cwd>/.pi/{settings.json,config.yml,config.yaml}`
|
|
45
|
+
3. `<cwd>/.omp/{settings.json,config.yml,config.yaml}`
|
|
46
|
+
|
|
47
|
+
The **host-native** project directory is applied last, so it wins: `.omp` under
|
|
48
|
+
oh-my-pi, `.pi` under pi. Reading the other host's directory means a vault
|
|
49
|
+
configured under pi keeps working after `omp` takes over the repository.
|
|
50
|
+
|
|
51
|
+
`/wiki-model` and `/wiki-trajectories` write JSON only, into whichever project
|
|
52
|
+
config directory already exists (host-native first, created if neither is
|
|
53
|
+
present). A hand-authored `config.yml` is read but never rewritten.
|
|
54
|
+
|
|
55
|
+
| Setting | Default | Description |
|
|
56
|
+
All of the above are viewable and editable in the `/wiki-settings` TUI (persists to project or global settings).
|
|
57
|
+
| ---------------------- | ---------- | ------------------------------------------------------------ |
|
|
58
|
+
| `taskModel` | — | Model for background tasks (`{ provider: "openai", id: "gpt-4o" }`) |
|
|
59
|
+
| `synthesisLanguage` | — | BCP 47 language tag for ingest synthesis (e.g. `"ru"`, `"fr"`). When unset, synthesis defaults to English. |
|
|
60
|
+
| `synthesisMaxTokens` | 16384 | Max output tokens for ingest/synthesis runs (stored as a plain number) |
|
|
61
|
+
| `trajectories` | false | Enable agent-trajectory working-memory |
|
|
62
|
+
| `notices` | true | Show wiki activity notices in chat |
|
|
63
|
+
| `ambientPersonalVault` | host-dependent | Let the personal vault act as the ambient vault in projects that have no wiki. `true` under pi, `false` under oh-my-pi — see below. |
|
|
64
|
+
| `semanticWeight` | 0.5 | Weight of the semantic sub-score in hybrid recall (clamped 0–1) |
|
|
65
|
+
| `recallLinksThreshold` | 50 | Page-count gate for two-stage links-first recall (0 = always links-first, issue #68) |
|
|
66
|
+
| `recallSkillInlineMax` | 1600 | Max chars of a skill/case body inlined into recall output (0 = links only) |
|
|
67
|
+
| `embeddingProvider` | — | Embedding provider (e.g. `openai`); embeddings stay off until this is set |
|
|
68
|
+
| `embeddingModel` | — | Embedding model name (provider-specific) |
|
|
69
|
+
| `embeddingBaseUrl` | — | Optional API base URL override for the embedding provider |
|
|
70
|
+
| `embeddingApiKey` | — | API key literal — prefer `embeddingApiKeyEnv` so no secret lands in settings |
|
|
71
|
+
| `embeddingApiKeyEnv` | — | Name of the environment variable holding the embedding API key |
|
|
45
72
|
|
|
46
73
|
Example:
|
|
47
74
|
|
|
@@ -66,6 +93,30 @@ The vault root is resolved in this priority order:
|
|
|
66
93
|
|
|
67
94
|
This means when you're in a project with its own `.llm-wiki/`, that project wiki is active. When you're outside any project wiki, your personal `~/.llm-wiki/` takes over automatically.
|
|
68
95
|
|
|
96
|
+
### Ambient surfaces in projects without a wiki
|
|
97
|
+
|
|
98
|
+
Three surfaces fire without being asked: the session notice, the periodic
|
|
99
|
+
observe/retro reminder, and the `before_agent_start` recall injection (plus its
|
|
100
|
+
`<wiki_status>` system-prompt footer).
|
|
101
|
+
|
|
102
|
+
Because vault resolution falls back to the personal vault, those surfaces would
|
|
103
|
+
otherwise speak up in *every* directory as soon as `~/.llm-wiki/` exists —
|
|
104
|
+
injecting reminders and unrelated cross-project recall hits into repositories
|
|
105
|
+
where no wiki was ever initialized. Under oh-my-pi the plugin is installed once
|
|
106
|
+
and loads in every project, so that fallback is **off** by default there; under
|
|
107
|
+
pi the historical behaviour is kept.
|
|
108
|
+
|
|
109
|
+
`ambientPersonalVault` overrides the host default in either direction:
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{ "llm-wiki": { "ambientPersonalVault": true } }
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The gate only affects unprompted injections. Tools and slash commands are
|
|
116
|
+
always registered, so `/wiki-init` and `wiki_bootstrap` work in any directory —
|
|
117
|
+
and once a project has its own `.llm-wiki/`, every ambient surface turns back on
|
|
118
|
+
for it.
|
|
119
|
+
|
|
69
120
|
## Page Frontmatter
|
|
70
121
|
|
|
71
122
|
```yaml
|