pi-vault-mind 0.8.4 → 0.8.7
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.md +3 -3
- package/dist/src/commands.js +26 -23
- package/dist/src/events.js +2 -6
- package/dist/src/index.js +15 -12
- package/dist/src/server.d.ts +2 -2
- package/dist/src/server.js +2 -2
- package/dist/src/settings-ui.d.ts +1 -0
- package/dist/src/settings-ui.js +161 -35
- package/dist/src/types.d.ts +2 -0
- package/dist/src/types.js +1 -2
- package/dist/src/utils.js +2 -2
- package/dist/test/index.test.js +1 -1
- package/dist/test/settings-ui.test.js +71 -0
- package/package.json +88 -86
- package/scripts/check-deps.sh +0 -0
- package/scripts/fetch-modal-token.sh +0 -0
- package/scripts/setup-test-config.sh +0 -0
- package/scripts/setup-vault-pi.sh +32 -0
- package/scripts/vault-pi.sh +50 -0
- package/skills/vault-mind/SKILL.md +5 -5
- package/skills/vault-mind-setup/references/obsidian-cli-and-plugins.md +1 -1
- package/skills/vault-mind-setup/references/obsidian-vault-structure.md +1 -1
package/README.md
CHANGED
|
@@ -84,8 +84,8 @@ obsidian plugin:install id=obsidian-git enable
|
|
|
84
84
|
obsidian plugin:install id=obsidian-breadcrumbs enable
|
|
85
85
|
obsidian plugin:install id=graph-analysis enable
|
|
86
86
|
obsidian plugin:install id=actions-uri enable
|
|
87
|
-
obsidian plugin:install id=
|
|
88
|
-
|
|
87
|
+
obsidian plugin:install id=obsidian42-brat enable
|
|
88
|
+
obsidian plugin:install id=obsidian-pi-vault-mind enable
|
|
89
89
|
|
|
90
90
|
Full walkthrough: see [`docs/integrations/OBSIDIAN_SETUP.md`](docs/integrations/OBSIDIAN_SETUP.md).
|
|
91
91
|
|
|
@@ -97,7 +97,7 @@ Full walkthrough: see [`docs/integrations/OBSIDIAN_SETUP.md`](docs/integrations/
|
|
|
97
97
|
| **Breadcrumbs** | `obsidian-breadcrumbs` | Parses typed edges (`agent:related-to`, `agent:derived-from`) in YAML frontmatter. The recommended way to display the agent-extracted knowledge graph in Obsidian's UI. | Highly recommended |
|
|
98
98
|
| **Graph Analysis** | `graph-analysis` | Co-citation discovery, Jaccard similarity on the native Obsidian graph. Surfaces "always cited together but not yet linked" notes — a key agent discovery signal. | Highly recommended |
|
|
99
99
|
| **Actions URI** | `actions-uri` | `x-callback-url` endpoints so the Manager agent can trigger Obsidian UI commands (open notes, run commands) from pi. | Recommended |
|
|
100
|
-
| **
|
|
100
|
+
| **Vault Mind plugin** | `obsidian-pi-vault-mind` | Native setup/status/chat UI and HTTP bridge integration. | Recommended |
|
|
101
101
|
|
|
102
102
|
### Recommended: `notesmd-cli` (headless alternative)
|
|
103
103
|
|
package/dist/src/commands.js
CHANGED
|
@@ -52,10 +52,11 @@ const VM_USAGE = [
|
|
|
52
52
|
" /vm watcher cancel <id> Cancel a pending agent job",
|
|
53
53
|
" /vm server status Show HTTP server status + port",
|
|
54
54
|
" /vm personalize Analyze vault and suggest personality tuning for system.md + AGENTS.md",
|
|
55
|
-
" /vm setup
|
|
55
|
+
" /vm setup Onboarding flow: config + personalize (auto-uses cwd when it's a vault)",
|
|
56
56
|
" /vm setup --vault <path> CLI: set vault path",
|
|
57
57
|
" /vm setup --remoteUrl <url> CLI: set remote embedding URL",
|
|
58
58
|
" /vm setup --localUrl <url> CLI: set local embedding URL",
|
|
59
|
+
" /vm setup --workspace <slug> CLI: derive Modal URL + set workspace",
|
|
59
60
|
" /vm token Show API token source and masked value",
|
|
60
61
|
" /vm token show Show API token source and masked value",
|
|
61
62
|
" /vm token rotate Generate + persist a new API token (old token invalidated)",
|
|
@@ -237,9 +238,6 @@ const handleInit = async (_args, ctx, pi) => {
|
|
|
237
238
|
],
|
|
238
239
|
vaultMind: {
|
|
239
240
|
dataDir: ".lancedb",
|
|
240
|
-
embedding: {
|
|
241
|
-
model: "embeddinggemma",
|
|
242
|
-
},
|
|
243
241
|
ftsEnabled: true,
|
|
244
242
|
graph: { enabled: true, canvasSync: false },
|
|
245
243
|
},
|
|
@@ -289,22 +287,21 @@ const handleInit = async (_args, ctx, pi) => {
|
|
|
289
287
|
msg.push("", '💡 Tip: Tell the agent "Remember: [fact]" to auto-append to the main collection!');
|
|
290
288
|
if (ctx.hasUI) {
|
|
291
289
|
ctx.ui.notify(msg.join("\n"), "info");
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
}
|
|
290
|
+
}
|
|
291
|
+
if (hasPiContextTools(pi)) {
|
|
292
|
+
const existing = JSON.parse(fs.readFileSync(cfgDest, "utf-8"));
|
|
293
|
+
existing.extensionCompatibility = existing.extensionCompatibility || {};
|
|
294
|
+
existing.extensionCompatibility["pi-context"] = {
|
|
295
|
+
tagPatterns: [],
|
|
296
|
+
enhanceInjectors: false,
|
|
297
|
+
autoEnableAcm: true,
|
|
298
|
+
indexContextEvents: true,
|
|
299
|
+
...existing.extensionCompatibility["pi-context"],
|
|
300
|
+
enabled: true,
|
|
301
|
+
};
|
|
302
|
+
fs.writeFileSync(cfgDest, `${JSON.stringify(existing, null, 2)}\n`, "utf-8");
|
|
303
|
+
if (ctx.hasUI) {
|
|
304
|
+
ctx.ui.notify("✅ pi-context automation enabled (auto-ACM + event indexing)", "info");
|
|
308
305
|
}
|
|
309
306
|
}
|
|
310
307
|
};
|
|
@@ -1360,7 +1357,7 @@ const handleWatcher = async (args, ctx, pi) => {
|
|
|
1360
1357
|
}
|
|
1361
1358
|
};
|
|
1362
1359
|
// ── Setup ────────────────────────────────────────────────────────────────────
|
|
1363
|
-
const handleSetup = async (args, ctx) => {
|
|
1360
|
+
const handleSetup = async (args, ctx, pi) => {
|
|
1364
1361
|
// Parse optional CLI-style args: --vault <path> --remoteUrl <url> --localUrl <url> --model <name> --workspace <name>
|
|
1365
1362
|
const cliArgs = {};
|
|
1366
1363
|
const parts = args.trim().split(/\s+--/);
|
|
@@ -1382,7 +1379,13 @@ const handleSetup = async (args, ctx) => {
|
|
|
1382
1379
|
}
|
|
1383
1380
|
const hasCliArgs = cliArgs.vault || cliArgs.remoteUrl || cliArgs.localUrl || cliArgs.model || cliArgs.workspace;
|
|
1384
1381
|
await setupWizard(ctx, hasCliArgs ? cliArgs : undefined);
|
|
1385
|
-
ctx.
|
|
1382
|
+
if (ctx.hasUI) {
|
|
1383
|
+
ctx.ui.notify("Vault configured. Running personalization as part of onboarding...", "info");
|
|
1384
|
+
await runPersonalize(ctx, pi);
|
|
1385
|
+
}
|
|
1386
|
+
else {
|
|
1387
|
+
ctx.ui.notify("Vault is configured. Run /vm personalize in a TUI session to tune agent personality.", "info");
|
|
1388
|
+
}
|
|
1386
1389
|
};
|
|
1387
1390
|
// ── /vm intake ─────────────────────────────────────────────────────────────
|
|
1388
1391
|
const handleIntake = async (args, ctx) => {
|
|
@@ -1671,7 +1674,7 @@ export const registerCommands = (pi) => {
|
|
|
1671
1674
|
case "personalize":
|
|
1672
1675
|
return runPersonalize(ctx, pi);
|
|
1673
1676
|
case "setup":
|
|
1674
|
-
return handleSetup(rest, ctx);
|
|
1677
|
+
return handleSetup(rest, ctx, pi);
|
|
1675
1678
|
case "token":
|
|
1676
1679
|
return handleToken(rest, ctx);
|
|
1677
1680
|
default:
|
package/dist/src/events.js
CHANGED
|
@@ -51,12 +51,8 @@ export const handleBeforeAgentStart = async (pi, event, ctx) => {
|
|
|
51
51
|
}
|
|
52
52
|
// 2. pi-context integration: auto-ACM and tag-based triggers
|
|
53
53
|
const piContextCfg = getPiContextConfig(cfg);
|
|
54
|
-
if (!piContextCfg.enabled) {
|
|
55
|
-
// pi-context
|
|
56
|
-
}
|
|
57
|
-
else if (!hasPiContextTools(pi)) {
|
|
58
|
-
// pi-context enabled in config but tools not available — log once per turn_start
|
|
59
|
-
console.warn("[pi-vault-mind] pi-context enabled but tools not found. Install pi-context extension.");
|
|
54
|
+
if (!piContextCfg.enabled || !hasPiContextTools(pi)) {
|
|
55
|
+
// pi-context is disabled or unavailable — skip integration silently.
|
|
60
56
|
}
|
|
61
57
|
else {
|
|
62
58
|
if (piContextCfg.autoEnableAcm) {
|
package/dist/src/index.js
CHANGED
|
@@ -13,8 +13,7 @@ import { registerIntakeTool } from "./intake.js";
|
|
|
13
13
|
import { startServer, stopServer } from "./server.js";
|
|
14
14
|
import { clearEngineStatus, clearWatcherStatus, registerStatusCapture, setEngineStatus, } from "./status.js";
|
|
15
15
|
import { registerTools } from "./tools.js";
|
|
16
|
-
import { EXT_ROOT } from "./utils.js";
|
|
17
|
-
import { loadConfig } from "./utils.js";
|
|
16
|
+
import { EXT_ROOT, loadConfig } from "./utils.js";
|
|
18
17
|
import { registerVaultTools } from "./vault-tools.js";
|
|
19
18
|
import { registerAllExtensionTools } from "./vm-handlers.js";
|
|
20
19
|
import { startWatcher, stopWatcher, subscribeJobCompleted } from "./watcher.js";
|
|
@@ -80,27 +79,31 @@ export default function (pi) {
|
|
|
80
79
|
pi.on("turn_end", async (event, ctx) => {
|
|
81
80
|
return handleTurnEnd(pi, event, ctx);
|
|
82
81
|
});
|
|
83
|
-
/* watcher: auto-start
|
|
82
|
+
/* watcher and server: auto-start only for vaults with autoStart: true */
|
|
84
83
|
try {
|
|
85
84
|
const cfg = loadConfig(process.cwd());
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
const vaults = cfg.vaultMind.vaults;
|
|
86
|
+
const autoVaults = vaults
|
|
87
|
+
? Object.entries(vaults).filter(([, v]) => v.autoStart === true)
|
|
88
|
+
: [];
|
|
89
|
+
if (autoVaults.length > 0) {
|
|
90
|
+
serverState.port = cfg.vaultMind.httpPort || 11435;
|
|
91
|
+
startServer(pi, serverState, watcherState);
|
|
92
|
+
const autoVaultMap = {};
|
|
93
|
+
for (const [name, v] of autoVaults)
|
|
94
|
+
autoVaultMap[name] = v;
|
|
89
95
|
setTimeout(() => {
|
|
90
|
-
console.log("[pi-vault-mind] Auto-starting watcher for",
|
|
91
|
-
|
|
92
|
-
if (vaults)
|
|
93
|
-
startWatcher(pi, vaults, watcherState);
|
|
96
|
+
console.log("[pi-vault-mind] Auto-starting watcher for", autoVaults.length, "vault(s)");
|
|
97
|
+
startWatcher(pi, autoVaultMap, watcherState);
|
|
94
98
|
}, 2000);
|
|
95
99
|
}
|
|
96
|
-
/* auto-sync: off by default; only starts when modal.sync.autoSync is true */
|
|
97
100
|
if (cfg.vaultMind.embedding.remoteUrl && cfg.vaultMind.embedding.modal?.sync?.autoSync) {
|
|
98
101
|
stopAutoSync = startAutoSync(cfg.vaultMind.embedding.modal?.sync?.autoSyncIntervalMs ?? 300000);
|
|
99
102
|
console.log(`[pi-vault-mind] Modal auto-sync enabled (every ${cfg.vaultMind.embedding.modal?.sync?.autoSyncIntervalMs ?? 300000}ms)`);
|
|
100
103
|
}
|
|
101
104
|
}
|
|
102
105
|
catch {
|
|
103
|
-
/* config may not exist yet
|
|
106
|
+
/* config may not exist yet */
|
|
104
107
|
}
|
|
105
108
|
/* session_shutdown: stop the watcher, server, and engine */
|
|
106
109
|
pi.on("session_shutdown", async () => {
|
package/dist/src/server.d.ts
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
* Binds 127.0.0.1 only — no network exposure. Port defaults to 11435,
|
|
12
12
|
* configurable via vaultMind.httpPort in pi-vault-mind.config.json.
|
|
13
13
|
*
|
|
14
|
-
* Used by the
|
|
15
|
-
*
|
|
14
|
+
* Used by the Vault Mind Obsidian plugin and local automation scripts to notify
|
|
15
|
+
* pi of file saves via explicit push notifications.
|
|
16
16
|
*
|
|
17
17
|
* New Stage-1 routes (auth-gated by `PVM_API_TOKEN`):
|
|
18
18
|
* GET /vm/status, POST /vm/init, POST /vm/setup, POST /server/start,
|
package/dist/src/server.js
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
* Binds 127.0.0.1 only — no network exposure. Port defaults to 11435,
|
|
12
12
|
* configurable via vaultMind.httpPort in pi-vault-mind.config.json.
|
|
13
13
|
*
|
|
14
|
-
* Used by the
|
|
15
|
-
*
|
|
14
|
+
* Used by the Vault Mind Obsidian plugin and local automation scripts to notify
|
|
15
|
+
* pi of file saves via explicit push notifications.
|
|
16
16
|
*
|
|
17
17
|
* New Stage-1 routes (auth-gated by `PVM_API_TOKEN`):
|
|
18
18
|
* GET /vm/status, POST /vm/init, POST /vm/setup, POST /server/start,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
export declare const detectVaultFromCwd: (cwd: string) => string | null;
|
|
2
3
|
export declare const createCollectionWizard: (ctx: ExtensionContext) => Promise<void>;
|
|
3
4
|
export declare const createInjectorWizard: (ctx: ExtensionContext) => Promise<void>;
|
|
4
5
|
export declare const setupWizard: (ctx: ExtensionContext, cliArgs?: {
|
package/dist/src/settings-ui.js
CHANGED
|
@@ -2,6 +2,15 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { MODAL_TOKEN_ENV, createModalClient, modalUrl, resolveModalToken } from "./modal-config.js";
|
|
4
4
|
import { collectionNames, findConfig, getGlobalConfigPath, loadConfig, shrinkHome, } from "./utils.js";
|
|
5
|
+
export const detectVaultFromCwd = (cwd) => {
|
|
6
|
+
const obsidianDir = path.join(cwd, ".obsidian");
|
|
7
|
+
try {
|
|
8
|
+
return fs.statSync(obsidianDir).isDirectory() ? cwd : null;
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
5
14
|
export const createCollectionWizard = async (ctx) => {
|
|
6
15
|
const { project: cfgPath } = findConfig(ctx.cwd);
|
|
7
16
|
if (!cfgPath) {
|
|
@@ -84,6 +93,7 @@ export const createInjectorWizard = async (ctx) => {
|
|
|
84
93
|
};
|
|
85
94
|
export const setupWizard = async (ctx, cliArgs) => {
|
|
86
95
|
const existingGlobal = fs.existsSync(getGlobalConfigPath());
|
|
96
|
+
const detectedVaultPath = detectVaultFromCwd(ctx.cwd);
|
|
87
97
|
// ── Non-interactive / CLI mode ──────────────────────────────────────────
|
|
88
98
|
if (cliArgs && (cliArgs.vault || cliArgs.remoteUrl || cliArgs.localUrl || cliArgs.workspace)) {
|
|
89
99
|
const config = existingGlobal
|
|
@@ -92,8 +102,9 @@ export const setupWizard = async (ctx, cliArgs) => {
|
|
|
92
102
|
config.vaultMind = config.vaultMind || {};
|
|
93
103
|
config.vaultMind.embedding = config.vaultMind.embedding || {};
|
|
94
104
|
config.vaultMind.vaults = config.vaultMind.vaults || {};
|
|
95
|
-
|
|
96
|
-
|
|
105
|
+
const effectiveVaultPath = cliArgs.vault || detectedVaultPath || undefined;
|
|
106
|
+
if (effectiveVaultPath) {
|
|
107
|
+
config.vaultMind.vaults.default = { path: shrinkHome(effectiveVaultPath), autoSync: true };
|
|
97
108
|
}
|
|
98
109
|
if (cliArgs.remoteUrl) {
|
|
99
110
|
config.vaultMind.embedding.remoteUrl = cliArgs.remoteUrl;
|
|
@@ -110,16 +121,19 @@ export const setupWizard = async (ctx, cliArgs) => {
|
|
|
110
121
|
if (cliArgs.workspace) {
|
|
111
122
|
config.vaultMind.embedding.modal = config.vaultMind.embedding.modal || {};
|
|
112
123
|
config.vaultMind.embedding.modal.workspace = cliArgs.workspace;
|
|
124
|
+
if (!cliArgs.remoteUrl) {
|
|
125
|
+
config.vaultMind.embedding.remoteUrl = modalUrl(cliArgs.workspace);
|
|
126
|
+
}
|
|
113
127
|
}
|
|
114
128
|
const dir = path.dirname(getGlobalConfigPath());
|
|
115
129
|
if (!fs.existsSync(dir))
|
|
116
130
|
fs.mkdirSync(dir, { recursive: true });
|
|
117
131
|
fs.writeFileSync(getGlobalConfigPath(), `${JSON.stringify(config, null, 2)}\n`, "utf-8");
|
|
118
132
|
const lines = ["✅ Global config written:", ` ${getGlobalConfigPath()}`, ""];
|
|
119
|
-
if (
|
|
120
|
-
lines.push(` Vault: ${
|
|
121
|
-
if (
|
|
122
|
-
lines.push(` Remote URL: ${
|
|
133
|
+
if (effectiveVaultPath)
|
|
134
|
+
lines.push(` Vault: ${effectiveVaultPath}`);
|
|
135
|
+
if (config.vaultMind.embedding.remoteUrl)
|
|
136
|
+
lines.push(` Remote URL: ${config.vaultMind.embedding.remoteUrl}`);
|
|
123
137
|
if (cliArgs.localUrl)
|
|
124
138
|
lines.push(` Local URL: ${cliArgs.localUrl}`);
|
|
125
139
|
if (cliArgs.model)
|
|
@@ -152,7 +166,12 @@ export const setupWizard = async (ctx, cliArgs) => {
|
|
|
152
166
|
}
|
|
153
167
|
}
|
|
154
168
|
// ── Step 1: Vault path ─────────────────────────────────────────────────
|
|
155
|
-
const
|
|
169
|
+
const autoVaultPath = cliArgs?.vault?.trim() || detectedVaultPath || "";
|
|
170
|
+
if (autoVaultPath) {
|
|
171
|
+
ctx.ui.notify(`Using current vault path: ${autoVaultPath}`, "info");
|
|
172
|
+
}
|
|
173
|
+
const vaultPath = autoVaultPath ||
|
|
174
|
+
(await ctx.ui.input("Obsidian vault path (absolute):", `/home/${process.env.USER || "you"}/Obsidian/`));
|
|
156
175
|
if (!vaultPath) {
|
|
157
176
|
ctx.ui.notify("Setup cancelled — vault path is required.", "warning");
|
|
158
177
|
return;
|
|
@@ -168,28 +187,69 @@ export const setupWizard = async (ctx, cliArgs) => {
|
|
|
168
187
|
}
|
|
169
188
|
}
|
|
170
189
|
// ── Step 2: Embedding config ──────────────────────────────────────────
|
|
171
|
-
|
|
190
|
+
let remoteUrl = await ctx.ui.input("Remote Embedding URL (e.g. https://.../v1, optional):", "");
|
|
172
191
|
const localUrl = await ctx.ui.input("Local Embedding URL (e.g. http://127.0.0.1:11434/v1, optional):", "");
|
|
173
192
|
let useTransformers = false;
|
|
193
|
+
let guidedModalWorkspace;
|
|
174
194
|
if (!remoteUrl && !localUrl) {
|
|
175
|
-
const
|
|
176
|
-
|
|
195
|
+
const route = await ctx.ui.select("No embedding URL configured", [
|
|
196
|
+
"Use local transformers (offline)",
|
|
197
|
+
"Guide me through Modal setup",
|
|
198
|
+
"Cancel setup",
|
|
199
|
+
]);
|
|
200
|
+
if (!route || route === "Cancel setup") {
|
|
177
201
|
ctx.ui.notify("Setup cancelled — at least one URL or transformers is required.", "warning");
|
|
178
202
|
return;
|
|
179
203
|
}
|
|
180
|
-
|
|
204
|
+
if (route === "Use local transformers (offline)") {
|
|
205
|
+
useTransformers = true;
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
const workspaceSlug = await ctx.ui.input("Modal workspace slug:", "");
|
|
209
|
+
if (!workspaceSlug) {
|
|
210
|
+
ctx.ui.notify("Setup cancelled — workspace is required for Modal guidance.", "warning");
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
guidedModalWorkspace = workspaceSlug.trim();
|
|
214
|
+
remoteUrl = modalUrl(guidedModalWorkspace);
|
|
215
|
+
ctx.ui.notify([
|
|
216
|
+
"Modal endpoint discovery:",
|
|
217
|
+
`- Derived remote URL: ${remoteUrl}`,
|
|
218
|
+
"- Set token now with one of:",
|
|
219
|
+
" /vm remote token",
|
|
220
|
+
" ./scripts/fetch-modal-token.sh --write",
|
|
221
|
+
"- Reference docs:",
|
|
222
|
+
" docs/getting-started/NEW_VAULT_WALKTHROUGH.md",
|
|
223
|
+
" docs/integrations/MODAL_EMBEDDING.md",
|
|
224
|
+
].join("\n"), "info");
|
|
225
|
+
}
|
|
181
226
|
}
|
|
182
|
-
const model = await ctx.ui.input("
|
|
183
|
-
if (
|
|
184
|
-
ctx.ui.notify("Setup cancelled
|
|
227
|
+
const model = (await ctx.ui.input("Embedding model key (optional):", ""))?.trim();
|
|
228
|
+
if (model === undefined) {
|
|
229
|
+
ctx.ui.notify("Setup cancelled.", "warning");
|
|
185
230
|
return;
|
|
186
231
|
}
|
|
187
232
|
const dimStr = await ctx.ui.input("Dimensions (optional):", "");
|
|
188
233
|
const dim = dimStr ? Number.parseInt(dimStr, 10) : undefined;
|
|
189
|
-
let workspace;
|
|
190
|
-
if (remoteUrl?.includes("modal.run")) {
|
|
234
|
+
let workspace = guidedModalWorkspace;
|
|
235
|
+
if (!workspace && remoteUrl?.includes("modal.run")) {
|
|
191
236
|
workspace = await ctx.ui.input("Modal workspace (optional):", "");
|
|
192
237
|
}
|
|
238
|
+
// ── Step 3: Deterministic runtime settings ──────────────────────────────
|
|
239
|
+
const enableContextAutomation = await ctx.ui.confirm("Context automation", "Enable pi-context integration with auto-/acm trigger and event indexing?");
|
|
240
|
+
const enableAutoStart = await ctx.ui.confirm("Vault auto-start", "Auto-start watcher/server for this vault when pi session starts?");
|
|
241
|
+
const apply = await ctx.ui.confirm("Apply setup", [
|
|
242
|
+
`Vault: ${vaultPath}`,
|
|
243
|
+
`Remote URL: ${remoteUrl || "none"}`,
|
|
244
|
+
`Local URL: ${localUrl || "none"}`,
|
|
245
|
+
`Model: ${model || "(auto/server default)"}`,
|
|
246
|
+
`Context automation: ${enableContextAutomation ? "enabled" : "disabled"}`,
|
|
247
|
+
`Vault auto-start: ${enableAutoStart ? "enabled" : "disabled"}`,
|
|
248
|
+
].join("\n"));
|
|
249
|
+
if (!apply) {
|
|
250
|
+
ctx.ui.notify("Setup cancelled — no changes written.", "warning");
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
193
253
|
// ── Write config ────────────────────────────────────────────────────────
|
|
194
254
|
const config = existingGlobal
|
|
195
255
|
? JSON.parse(fs.readFileSync(getGlobalConfigPath(), "utf-8"))
|
|
@@ -201,7 +261,8 @@ export const setupWizard = async (ctx, cliArgs) => {
|
|
|
201
261
|
config.vaultMind.embedding.remoteUrl = remoteUrl;
|
|
202
262
|
if (localUrl)
|
|
203
263
|
config.vaultMind.embedding.localUrl = localUrl;
|
|
204
|
-
|
|
264
|
+
if (model)
|
|
265
|
+
config.vaultMind.embedding.model = model;
|
|
205
266
|
if (useTransformers)
|
|
206
267
|
config.vaultMind.embedding.useTransformers = true;
|
|
207
268
|
if (dim)
|
|
@@ -210,9 +271,22 @@ export const setupWizard = async (ctx, cliArgs) => {
|
|
|
210
271
|
config.vaultMind.embedding.modal = config.vaultMind.embedding.modal || {};
|
|
211
272
|
config.vaultMind.embedding.modal.workspace = workspace;
|
|
212
273
|
}
|
|
213
|
-
config.vaultMind.vaults.default = {
|
|
274
|
+
config.vaultMind.vaults.default = {
|
|
275
|
+
path: shrinkHome(vaultPath),
|
|
276
|
+
autoSync: true,
|
|
277
|
+
autoStart: enableAutoStart,
|
|
278
|
+
};
|
|
214
279
|
config.vaultMind.graph = config.vaultMind.graph || { enabled: true, canvasSync: true };
|
|
215
280
|
config.vaultMind.ftsEnabled = config.vaultMind.ftsEnabled !== false;
|
|
281
|
+
config.extensionCompatibility = config.extensionCompatibility || {};
|
|
282
|
+
config.extensionCompatibility["pi-context"] = {
|
|
283
|
+
tagPatterns: [],
|
|
284
|
+
enhanceInjectors: false,
|
|
285
|
+
autoEnableAcm: true,
|
|
286
|
+
indexContextEvents: true,
|
|
287
|
+
...(config.extensionCompatibility["pi-context"] || {}),
|
|
288
|
+
enabled: enableContextAutomation,
|
|
289
|
+
};
|
|
216
290
|
const dir = path.dirname(getGlobalConfigPath());
|
|
217
291
|
if (!fs.existsSync(dir))
|
|
218
292
|
fs.mkdirSync(dir, { recursive: true });
|
|
@@ -224,10 +298,19 @@ export const setupWizard = async (ctx, cliArgs) => {
|
|
|
224
298
|
` Vault: ${vaultPath}`,
|
|
225
299
|
` Remote: ${remoteUrl || "none"}`,
|
|
226
300
|
` Local: ${localUrl || "none"}`,
|
|
227
|
-
` Model: ${model}`,
|
|
301
|
+
` Model: ${model || "(auto/server default)"}`,
|
|
302
|
+
` Context automation: ${enableContextAutomation ? "enabled" : "disabled"}`,
|
|
303
|
+
` Vault auto-start: ${enableAutoStart ? "enabled" : "disabled"}`,
|
|
228
304
|
"",
|
|
229
305
|
"Next: /vm watcher start (or restart pi for auto-start)",
|
|
230
306
|
].join("\n"), "info");
|
|
307
|
+
if (remoteUrl?.includes("modal.run")) {
|
|
308
|
+
ctx.ui.notify([
|
|
309
|
+
"Modal token reminder:",
|
|
310
|
+
"- /vm remote token",
|
|
311
|
+
"- ./scripts/fetch-modal-token.sh --write",
|
|
312
|
+
].join("\n"), "info");
|
|
313
|
+
}
|
|
231
314
|
};
|
|
232
315
|
/**
|
|
233
316
|
* Interactive Modal embedding configuration + "Test connection" action.
|
|
@@ -347,30 +430,68 @@ export const openSettingsDashboard = async (ctx) => {
|
|
|
347
430
|
let running = true;
|
|
348
431
|
while (running) {
|
|
349
432
|
const choice = await ctx.ui.select("pi-vault-mind Settings", [
|
|
350
|
-
"
|
|
351
|
-
"
|
|
352
|
-
"
|
|
353
|
-
"
|
|
433
|
+
"Collections (storage + search targets)",
|
|
434
|
+
"Injectors (regex capture rules)",
|
|
435
|
+
"Embedding + remote provider",
|
|
436
|
+
"Context automation (pi-context)",
|
|
437
|
+
"Config (view active values)",
|
|
438
|
+
"Obsidian plugin settings (what to configure)",
|
|
354
439
|
"Exit",
|
|
355
440
|
]);
|
|
356
441
|
if (!choice || choice === "Exit") {
|
|
357
442
|
running = false;
|
|
358
443
|
break;
|
|
359
444
|
}
|
|
360
|
-
if (choice === "
|
|
445
|
+
if (choice === "Collections (storage + search targets)") {
|
|
361
446
|
await manageCollections(ctx, cfgPath);
|
|
362
447
|
}
|
|
363
|
-
else if (choice === "
|
|
448
|
+
else if (choice === "Injectors (regex capture rules)") {
|
|
364
449
|
await manageInjectors(ctx, cfgPath);
|
|
365
450
|
}
|
|
366
|
-
else if (choice === "
|
|
451
|
+
else if (choice === "Embedding + remote provider") {
|
|
367
452
|
await configureModalWizard(ctx);
|
|
368
453
|
}
|
|
369
|
-
else if (choice === "
|
|
454
|
+
else if (choice === "Context automation (pi-context)") {
|
|
370
455
|
await manageExtensions(ctx, cfgPath);
|
|
371
456
|
}
|
|
457
|
+
else if (choice === "Config (view active values)") {
|
|
458
|
+
await showConfigSettings(ctx, cfgPath);
|
|
459
|
+
}
|
|
460
|
+
else if (choice === "Obsidian plugin settings (what to configure)") {
|
|
461
|
+
showObsidianSettingsHelp(ctx);
|
|
462
|
+
}
|
|
372
463
|
}
|
|
373
464
|
};
|
|
465
|
+
const showConfigSettings = async (ctx, cfgPath) => {
|
|
466
|
+
const merged = loadConfig(ctx.cwd);
|
|
467
|
+
const paths = findConfig(ctx.cwd);
|
|
468
|
+
const lines = [
|
|
469
|
+
"Config sources:",
|
|
470
|
+
`- Global: ${paths.global || "(none)"}`,
|
|
471
|
+
`- Project: ${paths.project || "(none)"}`,
|
|
472
|
+
"",
|
|
473
|
+
"Merged runtime config:",
|
|
474
|
+
JSON.stringify(merged.vaultMind, null, 2),
|
|
475
|
+
];
|
|
476
|
+
ctx.ui.notify(lines.join("\n"), "info");
|
|
477
|
+
const projectRaw = fs.readFileSync(cfgPath, "utf-8");
|
|
478
|
+
ctx.ui.notify(["Project config JSON:", projectRaw].join("\n"), "info");
|
|
479
|
+
};
|
|
480
|
+
const showObsidianSettingsHelp = (ctx) => {
|
|
481
|
+
ctx.ui.notify([
|
|
482
|
+
"Obsidian plugin settings (Vault Mind):",
|
|
483
|
+
"- Host / Port",
|
|
484
|
+
"- Pi binary path",
|
|
485
|
+
"- Check extension on startup",
|
|
486
|
+
"- Include editor context",
|
|
487
|
+
"- Include file picker",
|
|
488
|
+
"- Include slash commands",
|
|
489
|
+
"- Folder layout: Inbox / Library / Presentations / Journal",
|
|
490
|
+
"- API token (Import from dotenv)",
|
|
491
|
+
"",
|
|
492
|
+
"Location: Obsidian → Settings → Community plugins → Vault Mind",
|
|
493
|
+
].join("\n"), "info");
|
|
494
|
+
};
|
|
374
495
|
const manageCollections = async (ctx, cfgPath) => {
|
|
375
496
|
const existing = JSON.parse(fs.readFileSync(cfgPath, "utf-8"));
|
|
376
497
|
const names = Object.keys(existing.collections || {});
|
|
@@ -422,22 +543,27 @@ const manageInjectors = async (ctx, cfgPath) => {
|
|
|
422
543
|
const manageExtensions = async (ctx, cfgPath) => {
|
|
423
544
|
const existing = JSON.parse(fs.readFileSync(cfgPath, "utf-8"));
|
|
424
545
|
existing.extensionCompatibility = existing.extensionCompatibility || {};
|
|
425
|
-
const piCtx =
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
546
|
+
const piCtx = {
|
|
547
|
+
enabled: true,
|
|
548
|
+
autoEnableAcm: true,
|
|
549
|
+
indexContextEvents: true,
|
|
550
|
+
...(existing.extensionCompatibility["pi-context"] || {}),
|
|
551
|
+
};
|
|
552
|
+
const choice = await ctx.ui.select("Context automation (pi-context)", [
|
|
553
|
+
`Toggle integration (currently ${piCtx.enabled ? "ON" : "OFF"})`,
|
|
554
|
+
`Toggle auto-/acm trigger (currently ${piCtx.autoEnableAcm !== false ? "ON" : "OFF"})`,
|
|
555
|
+
`Toggle context event indexing (currently ${piCtx.indexContextEvents !== false ? "ON" : "OFF"})`,
|
|
430
556
|
"Back",
|
|
431
557
|
]);
|
|
432
558
|
if (!choice || choice === "Back")
|
|
433
559
|
return;
|
|
434
|
-
if (choice.startsWith("Toggle
|
|
560
|
+
if (choice.startsWith("Toggle integration")) {
|
|
435
561
|
piCtx.enabled = !piCtx.enabled;
|
|
436
562
|
}
|
|
437
|
-
else if (choice.startsWith("Toggle
|
|
563
|
+
else if (choice.startsWith("Toggle auto-/acm trigger")) {
|
|
438
564
|
piCtx.autoEnableAcm = piCtx.autoEnableAcm === false;
|
|
439
565
|
}
|
|
440
|
-
else if (choice.startsWith("Toggle
|
|
566
|
+
else if (choice.startsWith("Toggle context event indexing")) {
|
|
441
567
|
piCtx.indexContextEvents = piCtx.indexContextEvents === false;
|
|
442
568
|
}
|
|
443
569
|
existing.extensionCompatibility["pi-context"] = piCtx;
|
package/dist/src/types.d.ts
CHANGED
|
@@ -153,6 +153,8 @@ export interface GraphConfig {
|
|
|
153
153
|
}
|
|
154
154
|
export interface VaultConfig {
|
|
155
155
|
path: string;
|
|
156
|
+
/** When true, auto-start the watcher and HTTP server on session_start. Default false. */
|
|
157
|
+
autoStart?: boolean;
|
|
156
158
|
autoSync?: boolean;
|
|
157
159
|
autoSyncTags?: string[];
|
|
158
160
|
autoSyncMinLength?: number;
|
package/dist/src/types.js
CHANGED
|
@@ -32,7 +32,6 @@ export const DEFAULT_CONFIG = {
|
|
|
32
32
|
},
|
|
33
33
|
embedding: {
|
|
34
34
|
useTransformers: true,
|
|
35
|
-
model: "embeddinggemma",
|
|
36
35
|
localUrl: "http://127.0.0.1:11434",
|
|
37
36
|
},
|
|
38
37
|
graph: {
|
|
@@ -45,7 +44,7 @@ export const DEFAULT_CONFIG = {
|
|
|
45
44
|
},
|
|
46
45
|
extensionCompatibility: {
|
|
47
46
|
"pi-context": {
|
|
48
|
-
enabled:
|
|
47
|
+
enabled: true,
|
|
49
48
|
tagPatterns: [],
|
|
50
49
|
enhanceInjectors: false,
|
|
51
50
|
autoEnableAcm: true,
|
package/dist/src/utils.js
CHANGED
|
@@ -7,7 +7,7 @@ export const CONFIG_FILES = ["pi-vault-mind.config.json", ".pi/vault-mind.config
|
|
|
7
7
|
export const getGlobalConfigDir = () => path.join(process.env.HOME || process.env.USERPROFILE || "", ".pi", "agent");
|
|
8
8
|
/** Lazy — recomputes HOME each call so tests can override it. */
|
|
9
9
|
export const getGlobalConfigPath = () => path.join(getGlobalConfigDir(), "vault-mind.config.json");
|
|
10
|
-
export const EXT_ROOT = path.join(import.meta.dirname, "..");
|
|
10
|
+
export const EXT_ROOT = path.join(import.meta.dirname, "..", "..");
|
|
11
11
|
/** Expand a leading `~` to the user's home directory. */
|
|
12
12
|
export const expandHome = (p) => {
|
|
13
13
|
if (p.startsWith("~/") || p === "~") {
|
|
@@ -123,7 +123,7 @@ export const hasPiContextTools = (pi) => {
|
|
|
123
123
|
};
|
|
124
124
|
export const isPiContextEnabled = (cfg) => cfg.extensionCompatibility?.["pi-context"]?.enabled === true;
|
|
125
125
|
export const getPiContextConfig = (cfg) => ({
|
|
126
|
-
enabled:
|
|
126
|
+
enabled: true,
|
|
127
127
|
tagPatterns: [],
|
|
128
128
|
enhanceInjectors: false,
|
|
129
129
|
autoEnableAcm: true,
|
package/dist/test/index.test.js
CHANGED
|
@@ -99,7 +99,7 @@ describe("pi-vault-mind", () => {
|
|
|
99
99
|
assert.equal(types.DEFAULT_CONFIG.version, 2);
|
|
100
100
|
assert.ok(types.DEFAULT_CONFIG.collections, "collections should exist");
|
|
101
101
|
assert.ok(types.DEFAULT_CONFIG.collections.main, "should have main collection");
|
|
102
|
-
assert.equal(types.DEFAULT_CONFIG.vaultMind.embedding.model,
|
|
102
|
+
assert.equal(types.DEFAULT_CONFIG.vaultMind.embedding.model, undefined);
|
|
103
103
|
}
|
|
104
104
|
finally {
|
|
105
105
|
process.chdir(cwd);
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { strict as assert } from "node:assert";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as os from "node:os";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import { afterEach, describe, it } from "node:test";
|
|
6
|
+
import { detectVaultFromCwd, setupWizard } from "../src/settings-ui.js";
|
|
7
|
+
import { modalUrl } from "../src/modal-config.js";
|
|
8
|
+
import { getGlobalConfigPath } from "../src/utils.js";
|
|
9
|
+
const savedHome = process.env.HOME;
|
|
10
|
+
const mkTmpDir = (prefix) => fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
11
|
+
const makeCtx = (cwd) => ({
|
|
12
|
+
cwd,
|
|
13
|
+
hasUI: false,
|
|
14
|
+
ui: {
|
|
15
|
+
notify: () => undefined,
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
const expandMaybeHome = (p, home) => p.startsWith("~/") ? path.join(home, p.slice(2)) : p;
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
if (savedHome === undefined) {
|
|
21
|
+
Reflect.deleteProperty(process.env, "HOME");
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
process.env.HOME = savedHome;
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
describe("settings-ui vault detection", () => {
|
|
28
|
+
it("detectVaultFromCwd returns cwd when .obsidian exists", () => {
|
|
29
|
+
const vault = mkTmpDir("pvm-vault-detect-");
|
|
30
|
+
fs.mkdirSync(path.join(vault, ".obsidian"), { recursive: true });
|
|
31
|
+
assert.equal(detectVaultFromCwd(vault), vault);
|
|
32
|
+
fs.rmSync(vault, { recursive: true, force: true });
|
|
33
|
+
});
|
|
34
|
+
it("detectVaultFromCwd returns null when .obsidian missing", () => {
|
|
35
|
+
const dir = mkTmpDir("pvm-nonvault-detect-");
|
|
36
|
+
assert.equal(detectVaultFromCwd(dir), null);
|
|
37
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
describe("setupWizard CLI defaults", () => {
|
|
41
|
+
it("auto-uses cwd as vault when cwd is an Obsidian vault", async () => {
|
|
42
|
+
const home = mkTmpDir("pvm-home-");
|
|
43
|
+
const vault = path.join(home, "Vault");
|
|
44
|
+
fs.mkdirSync(path.join(vault, ".obsidian"), { recursive: true });
|
|
45
|
+
process.env.HOME = home;
|
|
46
|
+
await setupWizard(makeCtx(vault), {
|
|
47
|
+
remoteUrl: "https://example.com/v1",
|
|
48
|
+
model: "embeddinggemma",
|
|
49
|
+
});
|
|
50
|
+
const cfg = JSON.parse(fs.readFileSync(getGlobalConfigPath(), "utf-8"));
|
|
51
|
+
const stored = cfg.vaultMind?.vaults?.default?.path;
|
|
52
|
+
assert.ok(stored, "default vault path should be written");
|
|
53
|
+
assert.equal(expandMaybeHome(stored, home), vault);
|
|
54
|
+
assert.equal(cfg.vaultMind?.embedding?.remoteUrl, "https://example.com/v1");
|
|
55
|
+
fs.rmSync(home, { recursive: true, force: true });
|
|
56
|
+
});
|
|
57
|
+
it("derives remoteUrl from workspace in CLI mode", async () => {
|
|
58
|
+
const home = mkTmpDir("pvm-home-");
|
|
59
|
+
const vault = path.join(home, "Vault");
|
|
60
|
+
fs.mkdirSync(path.join(vault, ".obsidian"), { recursive: true });
|
|
61
|
+
process.env.HOME = home;
|
|
62
|
+
await setupWizard(makeCtx(vault), {
|
|
63
|
+
workspace: "acme-workspace",
|
|
64
|
+
model: "embeddinggemma",
|
|
65
|
+
});
|
|
66
|
+
const cfg = JSON.parse(fs.readFileSync(getGlobalConfigPath(), "utf-8"));
|
|
67
|
+
assert.equal(cfg.vaultMind?.embedding?.modal?.workspace, "acme-workspace");
|
|
68
|
+
assert.equal(cfg.vaultMind?.embedding?.remoteUrl, modalUrl("acme-workspace"));
|
|
69
|
+
fs.rmSync(home, { recursive: true, force: true });
|
|
70
|
+
});
|
|
71
|
+
});
|
package/package.json
CHANGED
|
@@ -1,87 +1,89 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
2
|
+
"name": "pi-vault-mind",
|
|
3
|
+
"version": "0.8.7",
|
|
4
|
+
"description": "Passive Obsidian vault extension for pi. Watches @agent markers, dispatches forked subagents (Miner, Broadcaster, Heavy-Lifter), stores in LanceDB with vector + FTS + graph. Multi-agent 'Drop & Forget' workflow for the pi agent ecosystem.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"agents",
|
|
11
|
+
"skills",
|
|
12
|
+
"scripts",
|
|
13
|
+
"CHANGELOG.md",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE"
|
|
16
|
+
],
|
|
17
|
+
"keywords": [
|
|
18
|
+
"pi-package",
|
|
19
|
+
"pi-extension",
|
|
20
|
+
"pi-skills",
|
|
21
|
+
"vault-mind",
|
|
22
|
+
"jsonl",
|
|
23
|
+
"lance",
|
|
24
|
+
"lancedb",
|
|
25
|
+
"search",
|
|
26
|
+
"knowledge-base",
|
|
27
|
+
"agent-tools",
|
|
28
|
+
"hitl",
|
|
29
|
+
"semantic-search",
|
|
30
|
+
"obsidian",
|
|
31
|
+
"obsidian-vault",
|
|
32
|
+
"subagent",
|
|
33
|
+
"subagents",
|
|
34
|
+
"passive",
|
|
35
|
+
"file-watcher",
|
|
36
|
+
"graph",
|
|
37
|
+
"knowledge-graph",
|
|
38
|
+
"notebooklm",
|
|
39
|
+
"pkm"
|
|
40
|
+
],
|
|
41
|
+
"author": "Kyle Brodeur",
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "git+https://github.com/kylebrodeur/pi-vault-mind.git"
|
|
46
|
+
},
|
|
47
|
+
"bugs": {
|
|
48
|
+
"url": "https://github.com/kylebrodeur/pi-vault-mind/issues"
|
|
49
|
+
},
|
|
50
|
+
"homepage": "https://github.com/kylebrodeur/pi-vault-mind#readme",
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=20.6.0"
|
|
53
|
+
},
|
|
54
|
+
"pi": {
|
|
55
|
+
"extensions": [
|
|
56
|
+
"./dist/index.js"
|
|
57
|
+
],
|
|
58
|
+
"skills": [
|
|
59
|
+
"./skills"
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
"dependencies": {
|
|
63
|
+
"@lancedb/lancedb": "latest",
|
|
64
|
+
"@xenova/transformers": "latest",
|
|
65
|
+
"apache-arrow": "^21.1.0",
|
|
66
|
+
"ws": "^8.21.0"
|
|
67
|
+
},
|
|
68
|
+
"peerDependencies": {
|
|
69
|
+
"@earendil-works/pi-coding-agent": ">=0.75.0 <1.0.0"
|
|
70
|
+
},
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@biomejs/biome": "^1.9.0",
|
|
73
|
+
"@earendil-works/pi-coding-agent": "^0.79.0",
|
|
74
|
+
"@earendil-works/pi-tui": "^0.79.0",
|
|
75
|
+
"@types/node": "^20.0.0",
|
|
76
|
+
"@types/ws": "^8.18.1",
|
|
77
|
+
"husky": "^9.1.6",
|
|
78
|
+
"typebox": "^1.1.0",
|
|
79
|
+
"typescript": "^5.0.0"
|
|
80
|
+
},
|
|
81
|
+
"scripts": {
|
|
82
|
+
"check": "biome check --write . && tsc --noEmit",
|
|
83
|
+
"build": "tsc && tsc -p tsconfig.test.json",
|
|
84
|
+
"dev": "tsc --watch",
|
|
85
|
+
"test": "node --test dist/test/*.test.js",
|
|
86
|
+
"e2e:ws": "node scripts/e2e-ws.mjs",
|
|
87
|
+
"e2e:modal": "node scripts/modal-e2e-smoke.mjs"
|
|
88
|
+
}
|
|
89
|
+
}
|
package/scripts/check-deps.sh
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# setup-vault-pi.sh — Bootstrap a vault-local pi agent config.
|
|
3
|
+
#
|
|
4
|
+
# Run from the vault root. Creates .pi/agent/ with pi-vault-mind and pi-context
|
|
5
|
+
# installed locally, isolated from your global ~/.pi/agent/.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# cd /path/to/vault
|
|
9
|
+
# bash scripts/setup-vault-pi.sh
|
|
10
|
+
#
|
|
11
|
+
# After setup, start pi with:
|
|
12
|
+
# PI_CODING_AGENT_DIR="$(pwd)/.pi/agent" pi
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
VAULT_ROOT="${1:-$(pwd)}"
|
|
17
|
+
PI_DIR="${VAULT_ROOT}/.pi/agent"
|
|
18
|
+
|
|
19
|
+
echo "==> Setting up vault-local pi config at ${PI_DIR}"
|
|
20
|
+
|
|
21
|
+
mkdir -p "${PI_DIR}"
|
|
22
|
+
|
|
23
|
+
# Install pi-vault-mind and pi-context into the vault-local agent dir
|
|
24
|
+
PI_CODING_AGENT_DIR="${PI_DIR}" pi install npm:pi-vault-mind
|
|
25
|
+
PI_CODING_AGENT_DIR="${PI_DIR}" pi install npm:pi-context
|
|
26
|
+
|
|
27
|
+
echo ""
|
|
28
|
+
echo "==> Done. Start pi for this vault with:"
|
|
29
|
+
echo " PI_CODING_AGENT_DIR=\"${PI_DIR}\" pi"
|
|
30
|
+
echo ""
|
|
31
|
+
echo " Or from Obsidian Shell Commands:"
|
|
32
|
+
echo " PI_CODING_AGENT_DIR=${PI_DIR} pi"
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# vault-pi.sh — Start pi for a vault, ensuring only one instance runs.
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# bash scripts/vault-pi.sh /path/to/vault
|
|
6
|
+
#
|
|
7
|
+
# Obsidian Shell Commands integration:
|
|
8
|
+
# Command: bash /path/to/pi-vault-mind/scripts/vault-pi.sh {{vault_path}}
|
|
9
|
+
#
|
|
10
|
+
# The script:
|
|
11
|
+
# 1. Checks for an existing PID file at <vault>/.pi/agent/daemon.pid
|
|
12
|
+
# 2. If the PID is alive → exits silently (already running)
|
|
13
|
+
# 3. If stale/dead → cleans up and starts fresh
|
|
14
|
+
# 4. Opens a new terminal window with pi using the vault-local config dir
|
|
15
|
+
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
|
|
18
|
+
VAULT="${1:?Usage: vault-pi.sh <vault-path>}"
|
|
19
|
+
PI_DIR="${VAULT}/.pi/agent"
|
|
20
|
+
PID_FILE="${PI_DIR}/daemon.pid"
|
|
21
|
+
|
|
22
|
+
# Ensure the vault-local agent dir exists
|
|
23
|
+
mkdir -p "${PI_DIR}"
|
|
24
|
+
|
|
25
|
+
# Check for existing process
|
|
26
|
+
if [ -f "${PID_FILE}" ]; then
|
|
27
|
+
PID=$(cat "${PID_FILE}")
|
|
28
|
+
if kill -0 "${PID}" 2>/dev/null; then
|
|
29
|
+
echo "[vault-pi] pi already running for ${VAULT} (PID ${PID})"
|
|
30
|
+
exit 0
|
|
31
|
+
fi
|
|
32
|
+
echo "[vault-pi] Stale PID file (${PID} is dead), cleaning up"
|
|
33
|
+
rm -f "${PID_FILE}"
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# Start pi in a new terminal window.
|
|
37
|
+
# macOS: open -a Terminal or open -a iTerm
|
|
38
|
+
# The trap writes the PID and cleans up on exit.
|
|
39
|
+
PI_CMD="PI_CODING_AGENT_DIR='${PI_DIR}' pi; rm -f '${PID_FILE}'"
|
|
40
|
+
|
|
41
|
+
if [ -d "/Applications/iTerm.app" ]; then
|
|
42
|
+
open -a iTerm --args --detach bash -c "echo \$\$ > '${PID_FILE}'; cd '${VAULT}'; ${PI_CMD}"
|
|
43
|
+
elif [ -d "/Applications/Utilities/Terminal.app" ]; then
|
|
44
|
+
osascript -e "tell application \"Terminal\" to do script \"echo \$\$ > '${PID_FILE}'; cd '${VAULT}'; ${PI_CMD}\""
|
|
45
|
+
else
|
|
46
|
+
echo "[vault-pi] No supported terminal found (iTerm or Terminal.app)"
|
|
47
|
+
exit 1
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
echo "[vault-pi] Started pi for ${VAULT}"
|
|
@@ -83,15 +83,15 @@ Uses `all-MiniLM-L6-v2` (384 dimensions). Downloads ONNX model on first use.
|
|
|
83
83
|
## Obsidian Bridge
|
|
84
84
|
|
|
85
85
|
pi-vault-mind runs an HTTP server on `http://127.0.0.1:11435` (configurable
|
|
86
|
-
via `vaultMind.httpPort`). The
|
|
87
|
-
events to `/vault-mind/scan` for explicit, reliable
|
|
88
|
-
relying solely on `fs.watch`.
|
|
86
|
+
via `vaultMind.httpPort`). The Vault Mind plugin (or any local automation)
|
|
87
|
+
can POST file-save events to `/vault-mind/scan` for explicit, reliable
|
|
88
|
+
file-watching instead of relying solely on `fs.watch`.
|
|
89
89
|
|
|
90
90
|
```bash
|
|
91
|
-
#
|
|
91
|
+
# Manual local-bridge trigger:
|
|
92
92
|
curl -s -X POST http://127.0.0.1:11435/vault-mind/scan \
|
|
93
93
|
-H "Content-Type: application/json" \
|
|
94
|
-
-d '{"file":"
|
|
94
|
+
-d '{"file":"/absolute/path/to/note.md"}'
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
Endpoints:
|
|
@@ -73,7 +73,7 @@ obsidian plugin:install id=obsidian-git enable
|
|
|
73
73
|
obsidian plugin:install id=obsidian-breadcrumbs enable
|
|
74
74
|
obsidian plugin:install id=graph-analysis enable
|
|
75
75
|
obsidian plugin:install id=actions-uri enable
|
|
76
|
-
obsidian plugin:install id=
|
|
76
|
+
obsidian plugin:install id=obsidian-pi-vault-mind enable
|
|
77
77
|
obsidian plugin:install id=obsidian42-brat enable # for beta plugins
|
|
78
78
|
|
|
79
79
|
# Verify
|
|
@@ -103,4 +103,4 @@ Community plugins to install:
|
|
|
103
103
|
- `Breadcrumbs` — typed-edge graph
|
|
104
104
|
- `Graph Analysis` — co-citation discovery
|
|
105
105
|
- `Actions URI` — cross-app automation
|
|
106
|
-
- `
|
|
106
|
+
- `Vault Mind plugin` — native setup/status/chat + local bridge trigger
|