pi-vault-mind 0.7.0
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/LICENSE +21 -0
- package/README.md +428 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/src/commands.d.ts +9 -0
- package/dist/src/commands.js +813 -0
- package/dist/src/events.d.ts +13 -0
- package/dist/src/events.js +236 -0
- package/dist/src/graph.d.ts +3 -0
- package/dist/src/graph.js +234 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +61 -0
- package/dist/src/lance.d.ts +40 -0
- package/dist/src/lance.js +409 -0
- package/dist/src/server.d.ts +25 -0
- package/dist/src/server.js +180 -0
- package/dist/src/settings-ui.d.ts +9 -0
- package/dist/src/settings-ui.js +313 -0
- package/dist/src/state.d.ts +2 -0
- package/dist/src/state.js +16 -0
- package/dist/src/tools.d.ts +2 -0
- package/dist/src/tools.js +772 -0
- package/dist/src/types.d.ts +103 -0
- package/dist/src/types.js +51 -0
- package/dist/src/utils.d.ts +17 -0
- package/dist/src/utils.js +102 -0
- package/dist/src/vault-writer.d.ts +17 -0
- package/dist/src/vault-writer.js +141 -0
- package/dist/src/watcher.d.ts +91 -0
- package/dist/src/watcher.js +411 -0
- package/dist/src/widget.d.ts +3 -0
- package/dist/src/widget.js +12 -0
- package/dist/test/index.test.d.ts +1 -0
- package/dist/test/index.test.js +368 -0
- package/package.json +83 -0
- package/skills/vault-mind/SKILL.md +260 -0
- package/skills/vault-mind/references/tool-reference.md +53 -0
- package/skills/vault-mind-broadcaster/SKILL.md +112 -0
- package/skills/vault-mind-heavy-lifter/SKILL.md +34 -0
- package/skills/vault-mind-manager/SKILL.md +35 -0
- package/skills/vault-mind-miner/SKILL.md +40 -0
- package/skills/vault-mind-setup/SKILL.md +385 -0
- package/skills/vault-mind-setup/references/obsidian-cli-and-plugins.md +269 -0
- package/skills/vault-mind-setup/references/obsidian-vault-structure.md +106 -0
- package/skills/vault-mind-setup/references/pi-extension-wiring.md +236 -0
- package/skills/vault-mind-setup/references/troubleshooting-tree.md +147 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { testOllamaConnection } from "./lance.js";
|
|
4
|
+
import { GLOBAL_CONFIG_PATH, collectionNames, findConfig, loadConfig, } from "./utils.js";
|
|
5
|
+
export const createCollectionWizard = async (ctx) => {
|
|
6
|
+
const { project: cfgPath } = findConfig(ctx.cwd);
|
|
7
|
+
if (!cfgPath) {
|
|
8
|
+
ctx.ui.notify("No config found. Run /wiki init first.", "error");
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
if (!ctx.hasUI) {
|
|
12
|
+
ctx.ui.notify("TUI required for interactive wizard.", "error");
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const name = await ctx.ui.input("Collection Name:", "e.g., experiments");
|
|
16
|
+
if (!name)
|
|
17
|
+
return;
|
|
18
|
+
const defaultPath = `collections/${name}.jsonl`;
|
|
19
|
+
const p = await ctx.ui.input("File Path:", defaultPath);
|
|
20
|
+
if (!p)
|
|
21
|
+
return;
|
|
22
|
+
const schemaStr = await ctx.ui.input("Schema Fields (comma-separated):", "id, date, content");
|
|
23
|
+
if (!schemaStr)
|
|
24
|
+
return;
|
|
25
|
+
const schema = schemaStr
|
|
26
|
+
.split(",")
|
|
27
|
+
.map((s) => s.trim())
|
|
28
|
+
.filter(Boolean);
|
|
29
|
+
const dedupField = await ctx.ui.input("Dedup Field (optional):", "");
|
|
30
|
+
const existing = JSON.parse(fs.readFileSync(cfgPath, "utf-8"));
|
|
31
|
+
existing.collections = existing.collections || {};
|
|
32
|
+
existing.collections[name] = {
|
|
33
|
+
path: p,
|
|
34
|
+
schema,
|
|
35
|
+
...(dedupField ? { dedupField } : {}),
|
|
36
|
+
};
|
|
37
|
+
fs.writeFileSync(cfgPath, `${JSON.stringify(existing, null, 2)}\n`, "utf-8");
|
|
38
|
+
// ensure dir and file
|
|
39
|
+
const fullPath = path.isAbsolute(p) ? p : path.join(ctx.cwd, p);
|
|
40
|
+
const dir = path.dirname(fullPath);
|
|
41
|
+
if (!fs.existsSync(dir))
|
|
42
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
43
|
+
if (!fs.existsSync(fullPath)) {
|
|
44
|
+
fs.writeFileSync(fullPath, "", "utf-8");
|
|
45
|
+
}
|
|
46
|
+
ctx.ui.notify(`✅ Collection "${name}" created and configured!`, "info");
|
|
47
|
+
};
|
|
48
|
+
export const createInjectorWizard = async (ctx) => {
|
|
49
|
+
const { project: cfgPath } = findConfig(ctx.cwd);
|
|
50
|
+
if (!cfgPath) {
|
|
51
|
+
ctx.ui.notify("No config found. Run /wiki init first.", "error");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (!ctx.hasUI) {
|
|
55
|
+
ctx.ui.notify("TUI required for interactive wizard.", "error");
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const name = await ctx.ui.input("Injector Name:", "e.g., meeting-notes");
|
|
59
|
+
if (!name)
|
|
60
|
+
return;
|
|
61
|
+
const regex = await ctx.ui.input("Regex Pattern:", "meeting\\s+(\\S+)");
|
|
62
|
+
if (!regex)
|
|
63
|
+
return;
|
|
64
|
+
const cfg = loadConfig(ctx.cwd);
|
|
65
|
+
const names = collectionNames(cfg);
|
|
66
|
+
if (names.length === 0) {
|
|
67
|
+
ctx.ui.notify("No collections configured to target. Create one first.", "error");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const collection = await ctx.ui.select("Target Collection:", names);
|
|
71
|
+
if (!collection)
|
|
72
|
+
return;
|
|
73
|
+
const filterField = await ctx.ui.input("Filter Field (optional):", "tag");
|
|
74
|
+
const existing = JSON.parse(fs.readFileSync(cfgPath, "utf-8"));
|
|
75
|
+
existing.injectors = existing.injectors || [];
|
|
76
|
+
existing.injectors.push({
|
|
77
|
+
name,
|
|
78
|
+
regex,
|
|
79
|
+
collection,
|
|
80
|
+
...(filterField ? { filterField } : {}),
|
|
81
|
+
});
|
|
82
|
+
fs.writeFileSync(cfgPath, `${JSON.stringify(existing, null, 2)}\n`, "utf-8");
|
|
83
|
+
ctx.ui.notify(`✅ Injector "${name}" created and configured!`, "info");
|
|
84
|
+
};
|
|
85
|
+
export const setupWizard = async (ctx, cliArgs) => {
|
|
86
|
+
const existingGlobal = fs.existsSync(GLOBAL_CONFIG_PATH);
|
|
87
|
+
// ── Non-interactive / CLI mode ──────────────────────────────────────────
|
|
88
|
+
if (cliArgs && (cliArgs.vault || cliArgs.provider)) {
|
|
89
|
+
const config = existingGlobal
|
|
90
|
+
? JSON.parse(fs.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"))
|
|
91
|
+
: { wiki: { embedding: {}, vaults: {} } };
|
|
92
|
+
config.wiki = config.wiki || {};
|
|
93
|
+
config.wiki.embedding = config.wiki.embedding || {};
|
|
94
|
+
config.wiki.vaults = config.wiki.vaults || {};
|
|
95
|
+
if (cliArgs.vault) {
|
|
96
|
+
config.wiki.vaults.default = { path: cliArgs.vault };
|
|
97
|
+
}
|
|
98
|
+
if (cliArgs.provider && ["ollama", "transformers"].includes(cliArgs.provider)) {
|
|
99
|
+
config.wiki.embedding.provider = cliArgs.provider;
|
|
100
|
+
}
|
|
101
|
+
if (cliArgs.model) {
|
|
102
|
+
config.wiki.embedding.ollamaModel = cliArgs.model;
|
|
103
|
+
}
|
|
104
|
+
const dir = path.dirname(GLOBAL_CONFIG_PATH);
|
|
105
|
+
if (!fs.existsSync(dir))
|
|
106
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
107
|
+
fs.writeFileSync(GLOBAL_CONFIG_PATH, `${JSON.stringify(config, null, 2)}\n`, "utf-8");
|
|
108
|
+
const lines = ["✅ Global config written:", ` ${GLOBAL_CONFIG_PATH}`, ""];
|
|
109
|
+
if (cliArgs.vault)
|
|
110
|
+
lines.push(` Vault: ${cliArgs.vault}`);
|
|
111
|
+
if (cliArgs.provider)
|
|
112
|
+
lines.push(` Embedding: ${cliArgs.provider}`);
|
|
113
|
+
if (cliArgs.model)
|
|
114
|
+
lines.push(` Model: ${cliArgs.model}`);
|
|
115
|
+
ctx.ui.notify(lines.join("\n"), "info");
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
// ── Interactive TUI mode ────────────────────────────────────────────────
|
|
119
|
+
if (!ctx.hasUI) {
|
|
120
|
+
ctx.ui.notify("Interactive setup requires a TUI session. Use --vault / --provider for CLI mode.", "error");
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
// If global config exists, offer status or re-setup
|
|
124
|
+
if (existingGlobal) {
|
|
125
|
+
const choice = await ctx.ui.select("pi-vault-mind Setup", [
|
|
126
|
+
"View current config",
|
|
127
|
+
"Reconfigure",
|
|
128
|
+
"Exit",
|
|
129
|
+
]);
|
|
130
|
+
if (!choice || choice === "Exit")
|
|
131
|
+
return;
|
|
132
|
+
if (choice === "View current config") {
|
|
133
|
+
const cfg = JSON.parse(fs.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
|
|
134
|
+
ctx.ui.notify(JSON.stringify(cfg, null, 2), "info");
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// ── Step 1: Vault path ─────────────────────────────────────────────────
|
|
139
|
+
const vaultPath = await ctx.ui.input("Obsidian vault path (absolute):", `/home/${process.env.USER || "you"}/Obsidian/`);
|
|
140
|
+
if (!vaultPath) {
|
|
141
|
+
ctx.ui.notify("Setup cancelled — vault path is required.", "warning");
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (!fs.existsSync(vaultPath)) {
|
|
145
|
+
const create = await ctx.ui.confirm("Directory not found", `${vaultPath} does not exist. Create it?`);
|
|
146
|
+
if (create) {
|
|
147
|
+
fs.mkdirSync(vaultPath, { recursive: true });
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
ctx.ui.notify("Setup cancelled — vault path must exist.", "warning");
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// ── Step 2: Embedding provider ──────────────────────────────────────────
|
|
155
|
+
const provider = await ctx.ui.select("Embedding provider:", [
|
|
156
|
+
"transformers (all-MiniLM-L6-v2, offline, zero config)",
|
|
157
|
+
"ollama (embeddinggemma, higher quality, requires Ollama)",
|
|
158
|
+
]);
|
|
159
|
+
if (!provider) {
|
|
160
|
+
ctx.ui.notify("Setup cancelled.", "warning");
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
let ollamaModel = "embeddinggemma";
|
|
164
|
+
if (provider.startsWith("ollama")) {
|
|
165
|
+
const conn = await testOllamaConnection();
|
|
166
|
+
if (!conn.reachable) {
|
|
167
|
+
ctx.ui.notify("⚠️ Ollama not reachable. Install it (ollama.ai) and pull a model first.\nConfig saved anyway — you can switch providers later.", "warning");
|
|
168
|
+
}
|
|
169
|
+
else if (conn.models.length > 0) {
|
|
170
|
+
const modelNames = conn.models.map((m) => m.name);
|
|
171
|
+
const chosen = await ctx.ui.select("Select embedding model:", modelNames);
|
|
172
|
+
if (chosen)
|
|
173
|
+
ollamaModel = chosen;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// ── Write config ────────────────────────────────────────────────────────
|
|
177
|
+
const config = existingGlobal
|
|
178
|
+
? JSON.parse(fs.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"))
|
|
179
|
+
: { wiki: { graph: { enabled: true, canvasSync: true }, ftsEnabled: true } };
|
|
180
|
+
config.wiki = config.wiki || {};
|
|
181
|
+
config.wiki.embedding = config.wiki.embedding || {};
|
|
182
|
+
config.wiki.vaults = config.wiki.vaults || {};
|
|
183
|
+
config.wiki.embedding.provider = provider.startsWith("ollama") ? "ollama" : "transformers";
|
|
184
|
+
if (provider.startsWith("ollama")) {
|
|
185
|
+
config.wiki.embedding.ollamaModel = ollamaModel;
|
|
186
|
+
config.wiki.embedding.ollamaHost = config.wiki.embedding.ollamaHost || "http://127.0.0.1:11434";
|
|
187
|
+
}
|
|
188
|
+
config.wiki.vaults.default = { path: vaultPath, autoSync: true };
|
|
189
|
+
config.wiki.graph = config.wiki.graph || { enabled: true, canvasSync: true };
|
|
190
|
+
config.wiki.ftsEnabled = config.wiki.ftsEnabled !== false;
|
|
191
|
+
const dir = path.dirname(GLOBAL_CONFIG_PATH);
|
|
192
|
+
if (!fs.existsSync(dir))
|
|
193
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
194
|
+
fs.writeFileSync(GLOBAL_CONFIG_PATH, `${JSON.stringify(config, null, 2)}\n`, "utf-8");
|
|
195
|
+
ctx.ui.notify([
|
|
196
|
+
"✅ Global config written!",
|
|
197
|
+
` ${GLOBAL_CONFIG_PATH}`,
|
|
198
|
+
"",
|
|
199
|
+
` Vault: ${vaultPath}`,
|
|
200
|
+
` Embedding: ${config.wiki.embedding.provider}`,
|
|
201
|
+
...(config.wiki.embedding.provider === "ollama"
|
|
202
|
+
? [` Model: ${config.wiki.embedding.ollamaModel}`]
|
|
203
|
+
: []),
|
|
204
|
+
"",
|
|
205
|
+
"Next: /wiki watcher start (or restart pi for auto-start)",
|
|
206
|
+
].join("\n"), "info");
|
|
207
|
+
};
|
|
208
|
+
export const openSettingsDashboard = async (ctx) => {
|
|
209
|
+
if (!ctx.hasUI) {
|
|
210
|
+
ctx.ui.notify("TUI required for settings dashboard.", "error");
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const { project: cfgPath } = findConfig(ctx.cwd);
|
|
214
|
+
if (!cfgPath) {
|
|
215
|
+
ctx.ui.notify("No config found. Run /wiki init first.", "error");
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
let running = true;
|
|
219
|
+
while (running) {
|
|
220
|
+
const choice = await ctx.ui.select("pi-vault-mind Settings", [
|
|
221
|
+
"Manage Collections",
|
|
222
|
+
"Manage Injectors",
|
|
223
|
+
"Extension Integrations",
|
|
224
|
+
"Exit",
|
|
225
|
+
]);
|
|
226
|
+
if (!choice || choice === "Exit") {
|
|
227
|
+
running = false;
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
if (choice === "Manage Collections") {
|
|
231
|
+
await manageCollections(ctx, cfgPath);
|
|
232
|
+
}
|
|
233
|
+
else if (choice === "Manage Injectors") {
|
|
234
|
+
await manageInjectors(ctx, cfgPath);
|
|
235
|
+
}
|
|
236
|
+
else if (choice === "Extension Integrations") {
|
|
237
|
+
await manageExtensions(ctx, cfgPath);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
const manageCollections = async (ctx, cfgPath) => {
|
|
242
|
+
const existing = JSON.parse(fs.readFileSync(cfgPath, "utf-8"));
|
|
243
|
+
const names = Object.keys(existing.collections || {});
|
|
244
|
+
const choice = await ctx.ui.select("Collections", [
|
|
245
|
+
...names.map((n) => `Edit: ${n}`),
|
|
246
|
+
"Create New Collection",
|
|
247
|
+
"Back",
|
|
248
|
+
]);
|
|
249
|
+
if (!choice || choice === "Back")
|
|
250
|
+
return;
|
|
251
|
+
if (choice === "Create New Collection") {
|
|
252
|
+
await createCollectionWizard(ctx);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (choice.startsWith("Edit: ")) {
|
|
256
|
+
const name = choice.replace("Edit: ", "");
|
|
257
|
+
const dedup = await ctx.ui.input(`Dedup Field for ${name} (currently ${existing.collections[name].dedupField || "none"}):`, "");
|
|
258
|
+
if (dedup !== undefined) {
|
|
259
|
+
existing.collections[name].dedupField = dedup || undefined;
|
|
260
|
+
fs.writeFileSync(cfgPath, `${JSON.stringify(existing, null, 2)}\n`, "utf-8");
|
|
261
|
+
ctx.ui.notify(`Updated ${name}`, "info");
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
const manageInjectors = async (ctx, cfgPath) => {
|
|
266
|
+
const existing = JSON.parse(fs.readFileSync(cfgPath, "utf-8"));
|
|
267
|
+
const injectors = existing.injectors || [];
|
|
268
|
+
const choice = await ctx.ui.select("Injectors", [
|
|
269
|
+
...injectors.map((i) => `Delete: ${i.name}`),
|
|
270
|
+
"Create New Injector",
|
|
271
|
+
"Back",
|
|
272
|
+
]);
|
|
273
|
+
if (!choice || choice === "Back")
|
|
274
|
+
return;
|
|
275
|
+
if (choice === "Create New Injector") {
|
|
276
|
+
await createInjectorWizard(ctx);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (choice.startsWith("Delete: ")) {
|
|
280
|
+
const name = choice.replace("Delete: ", "");
|
|
281
|
+
const ok = await ctx.ui.confirm("Confirm", `Delete injector ${name}?`);
|
|
282
|
+
if (ok) {
|
|
283
|
+
existing.injectors = injectors.filter((i) => i.name !== name);
|
|
284
|
+
fs.writeFileSync(cfgPath, `${JSON.stringify(existing, null, 2)}\n`, "utf-8");
|
|
285
|
+
ctx.ui.notify(`Deleted ${name}`, "info");
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
const manageExtensions = async (ctx, cfgPath) => {
|
|
290
|
+
const existing = JSON.parse(fs.readFileSync(cfgPath, "utf-8"));
|
|
291
|
+
existing.extensionCompatibility = existing.extensionCompatibility || {};
|
|
292
|
+
const piCtx = existing.extensionCompatibility["pi-context"] || {};
|
|
293
|
+
const choice = await ctx.ui.select("pi-context Integration", [
|
|
294
|
+
`Toggle Enabled (currently ${piCtx.enabled ? "ON" : "OFF"})`,
|
|
295
|
+
`Toggle Auto-ACM (currently ${piCtx.autoEnableAcm !== false ? "ON" : "OFF"})`,
|
|
296
|
+
`Toggle Event Indexing (currently ${piCtx.indexContextEvents !== false ? "ON" : "OFF"})`,
|
|
297
|
+
"Back",
|
|
298
|
+
]);
|
|
299
|
+
if (!choice || choice === "Back")
|
|
300
|
+
return;
|
|
301
|
+
if (choice.startsWith("Toggle Enabled")) {
|
|
302
|
+
piCtx.enabled = !piCtx.enabled;
|
|
303
|
+
}
|
|
304
|
+
else if (choice.startsWith("Toggle Auto-ACM")) {
|
|
305
|
+
piCtx.autoEnableAcm = piCtx.autoEnableAcm === false;
|
|
306
|
+
}
|
|
307
|
+
else if (choice.startsWith("Toggle Event Indexing")) {
|
|
308
|
+
piCtx.indexContextEvents = piCtx.indexContextEvents === false;
|
|
309
|
+
}
|
|
310
|
+
existing.extensionCompatibility["pi-context"] = piCtx;
|
|
311
|
+
fs.writeFileSync(cfgPath, `${JSON.stringify(existing, null, 2)}\n`, "utf-8");
|
|
312
|
+
ctx.ui.notify("Settings updated", "info");
|
|
313
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { loadConfig } from "./utils.js";
|
|
2
|
+
let activeCollection = null;
|
|
3
|
+
export const getActiveCollection = (cwd) => {
|
|
4
|
+
if (activeCollection) {
|
|
5
|
+
return activeCollection;
|
|
6
|
+
}
|
|
7
|
+
const config = loadConfig(cwd);
|
|
8
|
+
if (config.collections.main) {
|
|
9
|
+
return "main";
|
|
10
|
+
}
|
|
11
|
+
const keys = Object.keys(config.collections);
|
|
12
|
+
return keys.length > 0 ? keys[0] : "main";
|
|
13
|
+
};
|
|
14
|
+
export const setActiveCollection = (collection) => {
|
|
15
|
+
activeCollection = collection;
|
|
16
|
+
};
|