pi-vault-mind 0.16.32 → 0.16.34

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 CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.16.34 — 2026-08-29
4
+
5
+ ### Added
6
+
7
+ - **Ollama web tools.** `pvm setup` now installs the official `@ollama/pi-web-search` extension as a required extension, giving the vault agent Ollama-backed web search and fetch tools.
8
+
9
+ ## 0.16.33 — 2026-08-29
10
+
11
+ ### Added
12
+
13
+ - **Content-writing-system runtime.** `pvm setup` now installs the private `content-writing-system` Pi package (`git:git@github.com:kylebrodeur/content-writing-system`) as a required extension, registering both its skill and the `/check-writing` command in the vault-local agent. Requires git/SSH access to the repository at setup time.
14
+
15
+ ### Changed
16
+
17
+ - **Idempotent extension install.** `pvm setup` now reads the vault-local `settings.json` and skips extensions already present at the recorded spec (including pinned versions), reinstalling only missing specs or changed version pins. Repeated `pvm setup` / `--resources-only` runs no longer re-clone or re-fetch unchanged extensions.
18
+
3
19
  ## 0.16.32 — 2026-08-28
4
20
 
5
21
  ### Changed
@@ -138,6 +138,7 @@ const suggestionJson = (systemContent, agentsContent = "") => JSON.stringify({
138
138
  describe("personalize", () => {
139
139
  let testDir;
140
140
  let savedHome;
141
+ let savedAgentDir;
141
142
  beforeEach(() => {
142
143
  testDir = path.join(tmpdir(), `pi-vault-mind-personalize-test-${randomUUID()}`);
143
144
  fs.mkdirSync(testDir, { recursive: true });
@@ -145,12 +146,21 @@ describe("personalize", () => {
145
146
  savedHome = process.env.HOME;
146
147
  process.env.HOME = testDir;
147
148
  process.env.USERPROFILE = testDir;
149
+ // Isolate the resolved agent dir under the temp vault so these tests never
150
+ // touch a real or inherited PI_CODING_AGENT_DIR (getAgentDir() would
151
+ // otherwise return that absolute path, ignoring the per-test cwd).
152
+ savedAgentDir = process.env.PI_CODING_AGENT_DIR;
153
+ process.env.PI_CODING_AGENT_DIR = path.join(testDir, ".pi", "agent");
148
154
  });
149
155
  afterEach(() => {
150
156
  if (savedHome !== undefined)
151
157
  process.env.HOME = savedHome;
152
158
  else
153
159
  process.env.HOME = undefined;
160
+ if (savedAgentDir !== undefined)
161
+ process.env.PI_CODING_AGENT_DIR = savedAgentDir;
162
+ else
163
+ delete process.env.PI_CODING_AGENT_DIR;
154
164
  if (fs.existsSync(testDir))
155
165
  fs.rmSync(testDir, { recursive: true });
156
166
  });
@@ -14,6 +14,8 @@ const requiredExtensions = [
14
14
  "npm:@kylebrodeur/pi-model-router",
15
15
  "npm:@juicesharp/rpiv-todo@2.7.1",
16
16
  "npm:pi-subagents@0.58.0",
17
+ "npm:@ollama/pi-web-search",
18
+ "git:git@github.com:kylebrodeur/content-writing-system",
17
19
  ];
18
20
  const runtimeSkills = [
19
21
  "obsidian-cli",
@@ -124,7 +126,7 @@ fs.appendFileSync(process.env.PVM_TEST_UV_LOG, JSON.stringify(process.argv.slice
124
126
  assert.ok(fs.existsSync(path.join(nlmSkillDir, "references", ref)), `nlm-skill reference missing: ${ref}`);
125
127
  }
126
128
  assert.doesNotMatch(result.stdout + result.stderr, /NOISY INSTALL/);
127
- assert.match(result.stdout, /\[6\/6\] npm:pi-subagents@0\.58\.0 \.\.\. done/);
129
+ assert.match(result.stdout, /\[8\/8\] git:git@github\.com:kylebrodeur\/content-writing-system \.\.\. done/);
128
130
  assert.match(result.stdout, /Curated skills \(5\)/);
129
131
  });
130
132
  it("removes package paths that escape the vault-local agent directory", () => {
@@ -191,6 +193,29 @@ if (args[0] === "install" && args[1] === "npm:pi-context") {
191
193
  assert.ok(fs.existsSync(path.join(vaultPath, ".vault-mind", ".pi", "agent", "skills", skill, "SKILL.md")));
192
194
  }
193
195
  });
196
+ it("skips extensions already present in the vault settings", () => {
197
+ const agentDir = path.join(vaultPath, ".vault-mind", ".pi", "agent");
198
+ fs.mkdirSync(agentDir, { recursive: true });
199
+ fs.writeFileSync(path.join(agentDir, "settings.json"), `${JSON.stringify({
200
+ packages: ["npm:pi-vault-mind", "npm:pi-context", "npm:pi-subagents@0.58.0"],
201
+ })}\n`);
202
+ const result = runSetup(["--resources-only", vaultPath]);
203
+ assert.equal(result.status, 0, result.stderr || result.stdout);
204
+ const installed = installedExtensions();
205
+ for (const present of ["npm:pi-vault-mind", "npm:pi-context", "npm:pi-subagents@0.58.0"]) {
206
+ assert.ok(!installed.includes(present), `expected ${present} to be skipped`);
207
+ }
208
+ for (const missing of [
209
+ "npm:@kylebrodeur/pi-model-discovery",
210
+ "npm:@kylebrodeur/pi-model-router",
211
+ "npm:@juicesharp/rpiv-todo@2.7.1",
212
+ "npm:@ollama/pi-web-search",
213
+ "git:git@github.com:kylebrodeur/content-writing-system",
214
+ ]) {
215
+ assert.ok(installed.includes(missing), `expected ${missing} to be installed`);
216
+ }
217
+ assert.match(result.stdout, /npm:pi-subagents@0\.58\.0 \.\.\. already installed/);
218
+ });
194
219
  it("preserves customized configuration across repeated full setup", () => {
195
220
  const first = runSetup([vaultPath]);
196
221
  assert.equal(first.status, 0, first.stderr || first.stdout);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-vault-mind",
3
- "version": "0.16.32",
3
+ "version": "0.16.34",
4
4
  "packageManager": "pnpm@10.34.5",
5
5
  "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.",
6
6
  "main": "dist/index.js",
@@ -5,7 +5,9 @@
5
5
  "npm:@kylebrodeur/pi-model-discovery",
6
6
  "npm:@kylebrodeur/pi-model-router",
7
7
  "npm:@juicesharp/rpiv-todo@2.7.1",
8
- "npm:pi-subagents@0.58.0"
8
+ "npm:pi-subagents@0.58.0",
9
+ "npm:@ollama/pi-web-search",
10
+ "git:git@github.com:kylebrodeur/content-writing-system"
9
11
  ],
10
12
  "optional": ["npm:pi-1password"]
11
13
  }
@@ -158,6 +158,21 @@ function removeStaleExtensions(agentDir) {
158
158
  for (const extension of STALE_EXTENSIONS) removeStaleExtension(extension, agentDir);
159
159
  }
160
160
 
161
+ function readInstalledPackages(agentDir) {
162
+ const settingsPath = path.join(agentDir, "settings.json");
163
+ try {
164
+ const settings = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
165
+ if (!Array.isArray(settings.packages)) return new Set();
166
+ return new Set(
167
+ settings.packages
168
+ .map((entry) => (typeof entry === "string" ? entry : entry?.source))
169
+ .filter((source) => typeof source === "string")
170
+ );
171
+ } catch {
172
+ return new Set();
173
+ }
174
+ }
175
+
161
176
  function installExtensions(agentDir) {
162
177
  const manifest = readExtensionManifest();
163
178
  const extensions = [...manifest.required];
@@ -168,8 +183,13 @@ function installExtensions(agentDir) {
168
183
  }
169
184
  }
170
185
 
186
+ const installed = readInstalledPackages(agentDir);
171
187
  console.log(`==> Pi extensions (${extensions.length})`);
172
188
  for (const [index, extension] of extensions.entries()) {
189
+ if (installed.has(extension)) {
190
+ console.log(` [${index + 1}/${extensions.length}] ${extension} ... already installed`);
191
+ continue;
192
+ }
173
193
  installExtension(extension, agentDir, index + 1, extensions.length);
174
194
  }
175
195
  }