pi-vault-mind 0.16.30 → 0.16.31

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.
Files changed (53) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +40 -21
  3. package/dist/src/commands.js +7 -7
  4. package/dist/src/context-capture.d.ts +11 -6
  5. package/dist/src/context-capture.js +12 -7
  6. package/dist/src/events.d.ts +2 -2
  7. package/dist/src/events.js +109 -132
  8. package/dist/src/index.js +9 -8
  9. package/dist/src/personalize.js +2 -2
  10. package/dist/src/settings-ui.js +1 -1
  11. package/dist/src/types.d.ts +1 -1
  12. package/dist/src/types.js +1 -1
  13. package/dist/src/utils.js +3 -3
  14. package/dist/test/check-deps-script.test.js +102 -0
  15. package/dist/test/context-capture.test.js +34 -14
  16. package/dist/test/events.test.js +210 -132
  17. package/dist/test/pvm-cli.test.js +30 -0
  18. package/dist/test/pvm-setup.test.js +218 -0
  19. package/dist/test/utils.test.js +26 -4
  20. package/dist/test/vault-pi-script.test.js +18 -0
  21. package/package.json +19 -7
  22. package/pi-vault-mind.config.example.json +1 -1
  23. package/runtime/pvm-extensions.json +11 -0
  24. package/runtime/skills/LICENSE +21 -0
  25. package/runtime/skills/json-canvas/SKILL.md +244 -0
  26. package/runtime/skills/json-canvas/references/EXAMPLES.md +329 -0
  27. package/runtime/skills/nlm-skill/LICENSE +21 -0
  28. package/runtime/skills/nlm-skill/SKILL.md +936 -0
  29. package/runtime/skills/nlm-skill/references/command_reference.md +1072 -0
  30. package/runtime/skills/nlm-skill/references/remote-mcp.md +83 -0
  31. package/runtime/skills/nlm-skill/references/studio-prompt-examples.md +420 -0
  32. package/runtime/skills/nlm-skill/references/studio-prompting-guide.md +399 -0
  33. package/runtime/skills/nlm-skill/references/troubleshooting.md +427 -0
  34. package/runtime/skills/nlm-skill/references/workflows.md +572 -0
  35. package/runtime/skills/obsidian-bases/SKILL.md +499 -0
  36. package/runtime/skills/obsidian-bases/references/FUNCTIONS_REFERENCE.md +173 -0
  37. package/runtime/skills/obsidian-cli/SKILL.md +106 -0
  38. package/runtime/skills/obsidian-markdown/SKILL.md +196 -0
  39. package/runtime/skills/obsidian-markdown/references/CALLOUTS.md +58 -0
  40. package/runtime/skills/obsidian-markdown/references/EMBEDS.md +70 -0
  41. package/runtime/skills/obsidian-markdown/references/PROPERTIES.md +61 -0
  42. package/scripts/check-deps.sh +182 -118
  43. package/scripts/generate-extension-packages-json.mjs +5 -5
  44. package/scripts/pvm-setup.mjs +218 -0
  45. package/scripts/pvm.mjs +96 -3
  46. package/scripts/setup-vault-pi.sh +2 -66
  47. package/scripts/vault-pi.sh +34 -3
  48. package/skills/github-release-notes-file/SKILL.md +0 -42
  49. package/skills/obsidian-guided-test-session/SKILL.md +0 -110
  50. package/skills/obsidian-interactive-test/SKILL.md +0 -88
  51. package/skills/obsidian-plugin-publish/SKILL.md +0 -58
  52. package/skills/pi-vault-mind-release/SKILL.md +0 -141
  53. package/skills/pre-test-release/SKILL.md +0 -116
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.16.31 — 2026-08-28
4
+
5
+ ### Added
6
+
7
+ - **One-command vault setup.** `pvm setup [vault-path]` installs the complete vault-local extension, task/subagent, NotebookLM, and curated skill profile before merge-safely scaffolding Vault Mind, model-router, and main-agent configuration.
8
+ - **Resource-only repair.** `pvm setup --resources-only [vault-path]` refreshes extensions, tools, and skills without changing vault configuration.
9
+ - **Dependency audit and repair.** `pvm check-deps [--fix] [--strict] [--obsidian] [vault-path]` verifies the host runtime, vault-local extensions, curated skills, and optional content or Obsidian tooling.
10
+
11
+ ### Changed
12
+
13
+ - **Pi 0.84.3 runtime.** Development and compatibility checks now target `@earendil-works/pi-coding-agent` and `@earendil-works/pi-tui` 0.84.3.
14
+ - **Node.js 22.19 runtime floor.** Package metadata, dependency audits, and setup documentation now match the minimum Node version required by Pi 0.84.3.
15
+ - **Current task and subagent extensions.** New vault setup installs `@juicesharp/rpiv-todo` 2.7.1 and `pi-subagents` 0.58.0, while removing the obsolete `@tintinweb/pi-tasks` package.
16
+ - **Pi Context 2.1.2 integration.** Session-start and turn-end capture now use the current checkpoint and context-event APIs.
17
+ - **Curated package surface.** Published Pi discovery exposes only Vault Mind usage, setup, and specialist content skills; internal release, plugin-development, and test workflows are no longer shipped as runtime skills.
18
+ - **Reproducible package tooling.** The repository now pins pnpm 10.34.5 so Corepack does not select an incompatible pnpm major during clean release builds.
19
+
20
+ ### Fixed
21
+
22
+ - **Quiet main-agent startup.** Automatic context management initializes without injecting a synthetic `/acm` user message into a new session.
23
+ - **Stable Pi launcher selection.** Setup avoids transient `npx` binaries and consistently provisions the vault-local agent directory with the installed Pi runtime.
24
+
25
+ ### Verification
26
+
27
+ - Node 24.19.0 with pnpm 10.34.5: dependency install, core build, and package-scoped Biome check completed successfully.
28
+ - Full core test suite: 937/937 passing.
29
+ - Packed `pi-vault-mind-0.16.31.tgz` successfully; the packaged `pvm --help` executable ran and exposed the `run`, `setup`, and `check-deps` commands.
30
+
3
31
  ## 0.16.30 — 2026-08-27
4
32
 
5
33
  ### Added
package/README.md CHANGED
@@ -176,7 +176,7 @@ The Broadcaster agent can generate podcasts, study guides, and slide decks from
176
176
 
177
177
  ### Prerequisites
178
178
 
179
- - **Node.js** 20+ (matches `engines` in `package.json`)
179
+ - **Node.js** 22.19.0+ (matches `engines` in `package.json`)
180
180
  - **Embedding Provider** — choose one:
181
181
  - `@xenova/transformers` — built-in, no external deps (uses all-MiniLM-L6-v2, offline-capable)
182
182
  - `ollama` — requires Ollama running locally with `embeddinggemma` (higher quality)
@@ -190,47 +190,66 @@ The Broadcaster agent can generate podcasts, study guides, and slide decks from
190
190
  > bearer token are private to whoever owns it. (A standalone PyPI package for the
191
191
  > server may come later.)
192
192
 
193
- ### 1. Install with pi
193
+ ### 1. Install and set up the vault-local runtime
194
194
 
195
- ```bash
196
- pi install npm:pi-vault-mind # latest
197
- pi install npm:pi-vault-mind@0.16.30 # pinned release
198
- pi -e npm:pi-vault-mind # try without installing
199
- ```
200
-
201
- `pi install` loads the extension but does not expose its npm executable on your
202
- shell `PATH`. Install the first-class launcher once when you want the bare
203
- `pvm` command:
195
+ Install the first-class `pvm` CLI once, then run setup against a vault:
204
196
 
205
197
  ```bash
206
198
  npm install --global pi-vault-mind
199
+ pvm setup /path/to/your/vault
207
200
  pvm run /path/to/your/vault
208
201
  ```
209
202
 
210
- Or run it without a global installation:
203
+ `pvm setup` is safe to repeat. It installs or updates the required Vault Mind,
204
+ context, model-routing, task, and subagent extensions inside
205
+ `<vault>/.vault-mind/.pi/agent/`; installs the curated `obsidian-cli`,
206
+ `obsidian-markdown`, `obsidian-bases`, and `json-canvas` skills; and
207
+ merge-safely scaffolds Vault Mind, model-router, and main-agent configuration.
208
+ Existing user configuration, collections, model choices, and `system.md` are
209
+ preserved.
210
+
211
+ Refresh only extensions, tools, and skills without touching vault
212
+ configuration:
211
213
 
212
214
  ```bash
213
- npx --yes pi-vault-mind run /path/to/your/vault
215
+ pvm setup --resources-only /path/to/your/vault
214
216
  ```
215
217
 
216
- Or from git:
218
+ Audit the isolated runtime without changing it, or repair it in place:
217
219
 
218
220
  ```bash
219
- pi install git:git@github.com:kylebrodeur/pi-vault-mind
221
+ pvm check-deps /path/to/your/vault
222
+ pvm check-deps --fix /path/to/your/vault
220
223
  ```
221
224
 
222
- ### 2. Configure (interactive wizard)
225
+ Add `--strict` to fail when optional tools are missing and `--obsidian` to
226
+ include recommended Obsidian plugin checks.
223
227
 
228
+ Run either mode without a global installation:
229
+
230
+ ```bash
231
+ npx --yes pi-vault-mind setup /path/to/your/vault
232
+ npx --yes pi-vault-mind run /path/to/your/vault
224
233
  ```
225
- /vm setup
234
+
235
+ Installing only the Pi extension remains supported:
236
+
237
+ ```bash
238
+ pi install npm:pi-vault-mind
239
+ pi -e npm:pi-vault-mind
226
240
  ```
227
241
 
228
- This configures the current vault's setup surface: vault path, embedding
229
- provider details, folder layout, and scaffolding.
242
+ ### 2. Configure interactively when needed
230
243
 
231
- Or via CLI for scripting/repair:
244
+ Inside Pi, use the wizard to choose or change embedding providers, folder
245
+ layout, and other user-facing settings:
232
246
 
233
- ```bash
247
+ ```text
248
+ /vm setup
249
+ ```
250
+ For a non-interactive repair inside Pi:
251
+
252
+ ```text
234
253
  /vm setup --vault /home/you/Obsidian/MyVault --remoteUrl https://your-embedding-service-url.example.com --model embeddinggemma
235
254
  ```
236
255
 
@@ -259,7 +259,7 @@ const handleInit = async (_args, ctx, pi) => {
259
259
  const existing = JSON.parse(fs.readFileSync(cfgDest, "utf-8"));
260
260
  existing.extensionCompatibility = existing.extensionCompatibility || {};
261
261
  existing.extensionCompatibility["pi-context"] = {
262
- tagPatterns: [],
262
+ checkpointPatterns: [],
263
263
  enhanceInjectors: false,
264
264
  autoEnableAcm: true,
265
265
  indexContextEvents: true,
@@ -314,7 +314,7 @@ const handleValidate = async (_args, ctx, pi) => {
314
314
  const contextCfg = getPiContextConfig(cfg);
315
315
  const willCapture = hasContext && contextCfg.enabled;
316
316
  lines.push("", "🧩 Extension Integrations:", " • pi-context:");
317
- lines.push(` - Tools installed: ${hasContext ? "✅ Yes (context_tag, context_log, context_checkout)" : "❌ No"}`);
317
+ lines.push(` - Tools installed: ${hasContext ? "✅ Yes (context_checkpoint, context_timeline, context_compact)" : "❌ No"}`);
318
318
  lines.push(` - Config enabled: ${contextCfg.enabled ? "✅ Yes" : "❌ No"}`);
319
319
  lines.push(` - Will capture events: ${willCapture ? "✅ Yes — on next turn" : "❌ No"}`);
320
320
  if (hasContext && !contextCfg.enabled)
@@ -1122,10 +1122,10 @@ const handleContext = async (args, ctx, pi) => {
1122
1122
  ` - Config enabled: ${piContextCfg.enabled ? "✅" : "❌"}`,
1123
1123
  ` - Will capture on next turn: ${willCapture ? "✅ Yes" : "❌ No"}`,
1124
1124
  "",
1125
- `Tag Patterns: ${piContextCfg.tagPatterns?.length || 0}`,
1126
- ...(piContextCfg.tagPatterns?.length
1127
- ? piContextCfg.tagPatterns.map((p) => ` • "${p}"`)
1128
- : [" (none — no tag-based lookups)"]),
1125
+ `Checkpoint Patterns: ${piContextCfg.checkpointPatterns?.length || 0}`,
1126
+ ...(piContextCfg.checkpointPatterns?.length
1127
+ ? piContextCfg.checkpointPatterns.map((p) => ` • "${p}"`)
1128
+ : [" (none — no checkpoint-based lookups)"]),
1129
1129
  "",
1130
1130
  `Enhance Injectors: ${piContextCfg.enhanceInjectors ? "Yes" : "No"}`,
1131
1131
  `autoEnableAcm: ${piContextCfg.autoEnableAcm ? "Yes" : "No"}`,
@@ -1163,7 +1163,7 @@ const _toggleContext = (cfgPath, enabled) => {
1163
1163
  const existing = JSON.parse(fs.readFileSync(cfgPath, "utf-8"));
1164
1164
  existing.extensionCompatibility = existing.extensionCompatibility || {};
1165
1165
  existing.extensionCompatibility["pi-context"] = {
1166
- tagPatterns: [],
1166
+ checkpointPatterns: [],
1167
1167
  enhanceInjectors: false,
1168
1168
  autoEnableAcm: true,
1169
1169
  indexContextEvents: true,
@@ -1,18 +1,23 @@
1
1
  /**
2
- * Context Capture — Layer 4 auto-trigger pi-context /acm.
2
+ * Context Capture — Layer 4 pi-context /acm reminder.
3
+ *
4
+ * On extension load, reminds the human to run /acm to enable agentic context
5
+ * management. Does NOT inject /acm as a user or model message; the reminder
6
+ * is an unobtrusive UI notification only.
3
7
  *
4
- * On extension load, auto-sends /acm to enable agentic context management.
5
8
  * Injects context_checkpoint instructions into agent task prompts.
6
9
  *
7
10
  * Note: context_compact requires CommandCtx (only in main session, not
8
11
  * subagent forks). Subagent forks use context_checkpoint and context_timeline.
9
12
  */
10
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
13
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
11
14
  /**
12
- * Auto-send /acm on extension load to enable agentic context management
13
- * for the Manager session.
15
+ * Notify the human to run /acm to enable agentic context management.
16
+ *
17
+ * Uses ctx.ui.notify — a transient TUI notification that never enters the
18
+ * session history and is never delivered to the model.
14
19
  */
15
- export declare function enableACM(pi: ExtensionAPI): void;
20
+ export declare function notifyAcmReminder(ctx: ExtensionContext): void;
16
21
  /**
17
22
  * Build context_checkpoint instructions to inject into a dispatch task prompt.
18
23
  *
@@ -1,19 +1,24 @@
1
1
  /**
2
- * Context Capture — Layer 4 auto-trigger pi-context /acm.
2
+ * Context Capture — Layer 4 pi-context /acm reminder.
3
+ *
4
+ * On extension load, reminds the human to run /acm to enable agentic context
5
+ * management. Does NOT inject /acm as a user or model message; the reminder
6
+ * is an unobtrusive UI notification only.
3
7
  *
4
- * On extension load, auto-sends /acm to enable agentic context management.
5
8
  * Injects context_checkpoint instructions into agent task prompts.
6
9
  *
7
10
  * Note: context_compact requires CommandCtx (only in main session, not
8
11
  * subagent forks). Subagent forks use context_checkpoint and context_timeline.
9
12
  */
10
- // ── ACM auto-trigger ──────────────────────────────────────────────────────────
13
+ // ── ACM reminder notification ─────────────────────────────────────────────────
11
14
  /**
12
- * Auto-send /acm on extension load to enable agentic context management
13
- * for the Manager session.
15
+ * Notify the human to run /acm to enable agentic context management.
16
+ *
17
+ * Uses ctx.ui.notify — a transient TUI notification that never enters the
18
+ * session history and is never delivered to the model.
14
19
  */
15
- export function enableACM(pi) {
16
- pi.sendUserMessage("/acm", { deliverAs: "followUp" });
20
+ export function notifyAcmReminder(ctx) {
21
+ ctx.ui.notify("Run /acm to enable agentic context management for this session.", "info");
17
22
  }
18
23
  // ── Checkpoint instructions for agent task prompts ────────────────────────────
19
24
  /**
@@ -1,13 +1,13 @@
1
1
  import type { BeforeAgentStartEvent, ExtensionAPI, ExtensionContext, TurnEndEvent } from "@earendil-works/pi-coding-agent";
2
2
  /**
3
- * Handles before_agent_start to inject collection entries and pi-context tag matches
3
+ * Handles before_agent_start to inject collection entries and pi-context checkpoint matches
4
4
  * into the system prompt.
5
5
  */
6
6
  export declare const handleBeforeAgentStart: (pi: ExtensionAPI, event: BeforeAgentStartEvent, ctx: ExtensionContext) => Promise<{
7
7
  systemPrompt?: string;
8
8
  } | undefined>;
9
9
  /**
10
- * Handles turn_end to capture pi-context events and append them to the
10
+ * Handles turn_end to capture pi-context checkpoints and summaries in the
11
11
  * context_events collection.
12
12
  */
13
13
  export declare const handleTurnEnd: (pi: ExtensionAPI, _event: TurnEndEvent, ctx: ExtensionContext) => Promise<void>;
@@ -2,7 +2,7 @@ import * as fs from "node:fs";
2
2
  import { getActiveCollection } from "./state.js";
3
3
  import { ensureDir, getPiContextConfig, hasPiContextTools, loadConfig } from "./utils.js";
4
4
  /**
5
- * Handles before_agent_start to inject collection entries and pi-context tag matches
5
+ * Handles before_agent_start to inject collection entries and pi-context checkpoint matches
6
6
  * into the system prompt.
7
7
  */
8
8
  export const handleBeforeAgentStart = async (pi, event, ctx) => {
@@ -46,81 +46,71 @@ export const handleBeforeAgentStart = async (pi, event, ctx) => {
46
46
  .replace(/\{\{entries\}\}/g, entriesText || "(none)")
47
47
  .replace(/\{\{artifact\}\}/g, artifactText || "(none)");
48
48
  }
49
- if (additions) {
50
- return { systemPrompt: event.systemPrompt + additions };
51
- }
52
- // 2. pi-context integration: auto-ACM and tag-based triggers
49
+ // 2. pi-context integration: checkpoint-based triggers via sessionManager labels.
50
+ // context_checkpoint calls pi.setLabel(targetId, name); we read those labels directly
51
+ // from ctx.sessionManager — no cross-extension tool execution required.
53
52
  const piContextCfg = getPiContextConfig(cfg);
54
- if (!piContextCfg.enabled || !hasPiContextTools(pi)) {
55
- // pi-context is disabled or unavailable — skip integration silently.
56
- }
57
- else {
58
- // ACM auto-enable is handled exactly once at session_start (see index.ts)
59
- // via pi.sendUserMessage("/acm"), gated on isVaultMindReady. A hidden
60
- // sendMessage({ customType: "pi-context", content: "/acm" }) here does NOT
61
- // enable compaction pi-context only enables ACM from its own `/acm`
62
- // command handler (which captures the command context), so this dead path
63
- // was removed.
64
- if (piContextCfg.tagPatterns && piContextCfg.tagPatterns.length > 0) {
65
- try {
66
- const allTools = pi.getAllTools();
67
- const contextLogTool = allTools.find((t) => t.name === "context_log");
68
- if (contextLogTool) {
69
- const logs = await contextLogTool.execute?.(null, { limit: 100, verbose: true }, null, null, ctx);
70
- if (logs?.content?.[0]?.text) {
71
- const logText = logs.content[0].text;
72
- let tagInjections = 0;
73
- for (const pattern of piContextCfg.tagPatterns) {
74
- const tagRegex = new RegExp(pattern, "i");
75
- const tagMatches = logText.match(/tag:\s*([\w-]+)/gi);
76
- if (tagMatches) {
77
- for (const match of tagMatches) {
78
- const tagValue = match.replace(/tag:\s*/i, "");
79
- if (tagRegex.test(tagValue)) {
80
- for (const [collectionName, collectionDef] of Object.entries(cfg.collections)) {
81
- if (collectionDef.schema.includes("tag") &&
82
- fs.existsSync(collectionDef.path)) {
83
- const lines = fs
84
- .readFileSync(collectionDef.path, "utf-8")
85
- .split("\n")
86
- .filter(Boolean);
87
- const hits = lines
88
- .map((l) => {
89
- try {
90
- return JSON.parse(l);
91
- }
92
- catch {
93
- return null;
94
- }
95
- })
96
- .filter((e) => e && e.tag === tagValue);
97
- if (hits.length > 0) {
98
- additions += `\n\n=== pi-context tag [${tagValue}] matched ${collectionName} ===\n${JSON.stringify(hits, null, 2)}\n`;
99
- tagInjections += hits.length;
100
- }
101
- }
102
- }
103
- }
104
- }
105
- }
53
+ if (piContextCfg.enabled &&
54
+ hasPiContextTools(pi) &&
55
+ piContextCfg.checkpointPatterns &&
56
+ piContextCfg.checkpointPatterns.length > 0) {
57
+ try {
58
+ const sm = ctx.sessionManager;
59
+ // Inspect only the current branch (root-to-leaf path) for labels.
60
+ // getBranch() excludes entries on abandoned forks; getEntries() would span
61
+ // all forks and wrongly trigger injection for off-branch checkpoints.
62
+ const branchEntries = sm.getBranch();
63
+ // Collect all distinct labels present on the active branch
64
+ const checkpointNames = new Set();
65
+ for (const entry of branchEntries) {
66
+ const label = sm.getLabel(entry.id);
67
+ if (label)
68
+ checkpointNames.add(label);
69
+ }
70
+ let checkpointInjections = 0;
71
+ for (const pattern of piContextCfg.checkpointPatterns) {
72
+ const checkpointRegex = new RegExp(pattern, "i");
73
+ for (const checkpointName of checkpointNames) {
74
+ if (!checkpointRegex.test(checkpointName))
75
+ continue;
76
+ for (const [collectionName, collectionDef] of Object.entries(cfg.collections)) {
77
+ if (!collectionDef.schema.includes("tag") || !fs.existsSync(collectionDef.path)) {
78
+ continue;
106
79
  }
107
- if (tagInjections > 0) {
108
- ctx.ui?.notify?.(`pi-context: injected ${tagInjections} entries from matching tags`, "info");
80
+ const hits = fs
81
+ .readFileSync(collectionDef.path, "utf-8")
82
+ .split("\n")
83
+ .filter(Boolean)
84
+ .map((line) => {
85
+ try {
86
+ return JSON.parse(line);
87
+ }
88
+ catch {
89
+ return null;
90
+ }
91
+ })
92
+ .filter((e) => e?.tag === checkpointName);
93
+ if (hits.length > 0) {
94
+ additions += `\n\n=== pi-context checkpoint [${checkpointName}] matched ${collectionName} ===\n${JSON.stringify(hits, null, 2)}\n`;
95
+ checkpointInjections += hits.length;
109
96
  }
110
97
  }
111
98
  }
112
99
  }
113
- catch (e) {
114
- console.warn("[pi-vault-mind] pi-context tag integration error:", e);
100
+ if (checkpointInjections > 0) {
101
+ ctx.ui?.notify?.(`pi-context: injected ${checkpointInjections} entries from matching checkpoints`, "info");
115
102
  }
116
103
  }
104
+ catch (error) {
105
+ console.warn("[pi-vault-mind] pi-context checkpoint integration error:", error);
106
+ }
117
107
  }
118
108
  if (additions) {
119
109
  return { systemPrompt: event.systemPrompt + additions };
120
110
  }
121
111
  };
122
112
  /**
123
- * Handles turn_end to capture pi-context events and append them to the
113
+ * Handles turn_end to capture pi-context checkpoints and summaries in the
124
114
  * context_events collection.
125
115
  */
126
116
  export const handleTurnEnd = async (pi, _event, ctx) => {
@@ -131,96 +121,83 @@ export const handleTurnEnd = async (pi, _event, ctx) => {
131
121
  return;
132
122
  }
133
123
  try {
134
- if (!hasPiContextTools(pi)) {
135
- return;
136
- }
137
- const allTools = pi.getAllTools();
138
- const contextLogTool = allTools.find((t) => t.name === "context_log");
139
- const contextCheckoutTool = allTools.find((t) => t.name === "context_checkout");
140
- if (!contextLogTool || !contextCheckoutTool) {
141
- return;
142
- }
143
124
  const collectionDef = cfg.collections.context_events;
144
125
  if (!collectionDef) {
145
126
  console.warn("[pi-vault-mind] 'context_events' collection not defined in config. Cannot index pi-context events.");
146
127
  return;
147
128
  }
148
- // Get current session history
149
- const logs = await contextLogTool.execute?.(null, { limit: 100, verbose: true }, null, null, ctx);
150
- if (!logs?.content?.[0]?.text) {
151
- return;
152
- }
153
- const logText = logs.content[0].text;
129
+ const sm = ctx.sessionManager;
130
+ // Inspect the most recent 100 entries (100-entry bound matches prior behavior)
131
+ const recentEntries = sm.getEntries().slice(-100);
154
132
  const capturedEvents = [];
155
- const lines = logText.split("\n");
156
- for (const line of lines) {
157
- const timestamp = new Date().toISOString();
158
- let event = null;
159
- const tagMatch = line.match(/^\*?\s*([0-9a-f]+)\s+\(tag:\s*([\w-]+).*\)\s*\[(AI|USER|BASH|TOOL|SUMMARY)\]\s*(.*)/i);
160
- if (tagMatch) {
161
- event = {
162
- id: `tag-${tagMatch[1]}-${tagMatch[2]}`,
163
- type: "tag_created",
164
- session_entry_id: tagMatch[1],
165
- content: `Tag '${tagMatch[2]}' created at ${tagMatch[1]}`,
133
+ const timestamp = new Date().toISOString();
134
+ for (const entry of recentEntries) {
135
+ // Checkpoint: pi-context stores checkpoints as Pi labels on session entries.
136
+ // context_checkpoint calls pi.setLabel(entryId, name); we read via sm.getLabel.
137
+ const label = sm.getLabel(entry.id);
138
+ if (label) {
139
+ capturedEvents.push({
140
+ id: `checkpoint-${entry.id}-${label}`,
141
+ type: "checkpoint_created",
142
+ session_entry_id: entry.id,
143
+ content: `Checkpoint '${label}' created at ${entry.id}`,
166
144
  timestamp,
167
- tags: [tagMatch[2], "pi-context"],
145
+ tags: [label, "pi-context", "checkpoint"],
168
146
  details: {
169
- tag_name: tagMatch[2],
170
- entry_id: tagMatch[1],
171
- entry_type: tagMatch[3],
172
- entry_summary: tagMatch[4].trim(),
147
+ checkpoint_name: label,
148
+ entry_id: entry.id,
149
+ entry_type: entry.type.toUpperCase(),
173
150
  },
174
- };
151
+ });
175
152
  }
176
- const summaryMatch = line.match(/^\*?\s*([0-9a-f]+)\s+\((ROOT|HEAD)?.*summary from (.*)\)\s*\[SUMMARY\]\s*(.*)/i);
177
- if (summaryMatch) {
178
- event = {
179
- id: `checkout-summary-${summaryMatch[1]}`,
180
- type: "checkout_summary",
181
- session_entry_id: summaryMatch[1],
182
- content: `Checkout summary from ${summaryMatch[3]}: ${summaryMatch[4].trim()}`,
153
+ // Summary: compaction and branch_summary entries carry a real `.summary` string.
154
+ if ((entry.type === "compaction" || entry.type === "branch_summary") &&
155
+ "summary" in entry &&
156
+ typeof entry.summary === "string") {
157
+ capturedEvents.push({
158
+ id: `context-summary-${entry.id}`,
159
+ type: "context_summary",
160
+ session_entry_id: entry.id,
161
+ content: entry.summary,
183
162
  timestamp,
184
- tags: ["pi-context", "checkout", "summary"],
163
+ tags: ["pi-context", "summary"],
185
164
  details: {
186
- origin: summaryMatch[3],
187
- summary_message: summaryMatch[4].trim(),
188
- entry_id: summaryMatch[1],
165
+ entry_id: entry.id,
166
+ summary_message: entry.summary,
189
167
  },
190
- };
191
- }
192
- if (event) {
193
- capturedEvents.push(event);
168
+ });
194
169
  }
195
170
  }
196
- // Append events directly to the context_events collection
197
- if (collectionDef) {
198
- ensureDir(collectionDef.path);
199
- const existingIds = new Set();
200
- if (collectionDef.dedupField && fs.existsSync(collectionDef.path)) {
201
- const data = fs.readFileSync(collectionDef.path, "utf-8");
202
- for (const line of data.split("\n").filter(Boolean)) {
203
- try {
204
- existingIds.add(JSON.parse(line)[collectionDef.dedupField]);
205
- }
206
- catch {
207
- /* ignore malformed */
208
- }
171
+ // Append events to the context_events collection with dedup
172
+ ensureDir(collectionDef.path);
173
+ const existingIds = new Set();
174
+ if (collectionDef.dedupField && fs.existsSync(collectionDef.path)) {
175
+ const data = fs.readFileSync(collectionDef.path, "utf-8");
176
+ for (const line of data.split("\n").filter(Boolean)) {
177
+ try {
178
+ existingIds.add(JSON.parse(line)[collectionDef.dedupField]);
179
+ }
180
+ catch {
181
+ /* ignore malformed */
209
182
  }
210
183
  }
211
- let appended = 0;
212
- for (const event of capturedEvents) {
213
- if (collectionDef.dedupField && existingIds.has(event[collectionDef.dedupField])) {
184
+ }
185
+ let appended = 0;
186
+ for (const capturedEvent of capturedEvents) {
187
+ if (collectionDef.dedupField) {
188
+ const dedupVal = capturedEvent[collectionDef.dedupField];
189
+ if (existingIds.has(dedupVal))
214
190
  continue;
215
- }
216
- fs.appendFileSync(collectionDef.path, `${JSON.stringify(event)}\n`);
217
- if (collectionDef.dedupField)
218
- existingIds.add(event[collectionDef.dedupField]);
219
- appended++;
191
+ fs.appendFileSync(collectionDef.path, `${JSON.stringify(capturedEvent)}\n`);
192
+ existingIds.add(dedupVal);
220
193
  }
221
- if (appended > 0 && ctx.ui?.notify) {
222
- ctx.ui.notify(`pi-context: captured ${appended} event(s) → ${collectionDef.path}`, "info");
194
+ else {
195
+ fs.appendFileSync(collectionDef.path, `${JSON.stringify(capturedEvent)}\n`);
223
196
  }
197
+ appended++;
198
+ }
199
+ if (appended > 0 && ctx.ui?.notify) {
200
+ ctx.ui.notify(`pi-context: captured ${appended} event(s) → ${collectionDef.path}`, "info");
224
201
  }
225
202
  }
226
203
  catch (e) {
package/dist/src/index.js CHANGED
@@ -5,7 +5,7 @@ import { TaskQueue } from "./agent-bus/TaskQueue.js";
5
5
  import { startAutoSync } from "./autosync.js";
6
6
  import { createBridge } from "./bridge.js";
7
7
  import { registerCommands, selectActiveCollection, serverState, watcherState } from "./commands.js";
8
- import { enableACM } from "./context-capture.js";
8
+ import { notifyAcmReminder } from "./context-capture.js";
9
9
  import { startEngine, stopEngine } from "./engine.js";
10
10
  import { handleBeforeAgentStart, handleTurnEnd } from "./events.js";
11
11
  import { registerIdentityInjector } from "./identity-injector.js";
@@ -13,7 +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, isVaultMindReady, loadConfig } from "./utils.js";
16
+ import { EXT_ROOT, hasPiContextTools, isVaultMindReady, loadConfig } from "./utils.js";
17
17
  import { registerVaultTools } from "./vault-tools.js";
18
18
  import { registerAllExtensionTools } from "./vm-handlers.js";
19
19
  import { startWatcher, stopWatcher, subscribeJobCompleted } from "./watcher.js";
@@ -37,14 +37,15 @@ export default function (pi) {
37
37
  * during extension loading — the runtime must be initialized first. */
38
38
  pi.on("session_start", async (_event, ctx) => {
39
39
  updateActiveCollectionWidget(ctx);
40
- /* Auto-enable ACM only when this vault is completely configured and
41
- * ready. isVaultMindReady reads the RAW config file (not the merged
42
- * config, whose DEFAULT_CONFIG always populates embedding.localUrl and
43
- * would make a fresh vault look ready). */
40
+ /* Remind the human about ACM only when this vault is completely configured,
41
+ * pi-context is available, and the reminder has not been disabled. */
44
42
  const sessionCfg = loadConfig(ctx.cwd);
45
43
  const piCtxCfg = sessionCfg.extensionCompatibility?.["pi-context"];
46
- if (isVaultMindReady(ctx.cwd) && piCtxCfg?.enabled && piCtxCfg?.autoEnableAcm !== false) {
47
- enableACM(pi);
44
+ if (isVaultMindReady(ctx.cwd) &&
45
+ piCtxCfg?.enabled &&
46
+ piCtxCfg?.autoEnableAcm !== false &&
47
+ hasPiContextTools(pi)) {
48
+ notifyAcmReminder(ctx);
48
49
  }
49
50
  /* register shortcut */
50
51
  pi.registerShortcut("ctrl+alt+l", {
@@ -1,7 +1,7 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { parseFrontmatter } from "@earendil-works/pi-coding-agent";
4
- import { enableACM } from "./context-capture.js";
4
+ import { notifyAcmReminder } from "./context-capture.js";
5
5
  import { getStatus } from "./lance.js";
6
6
  import { scaffoldVaultConfig } from "./scaffold.js";
7
7
  import { expandHome, getPersonalizedMarkerPath, hasPiContextTools, isPersonalized, loadConfig, resolveAgentDir, } from "./utils.js";
@@ -633,7 +633,7 @@ export const runPersonalize = async (ctx, pi) => {
633
633
  const sessionCfg = loadConfig(ctx.cwd);
634
634
  const piCtxCfg = sessionCfg.extensionCompatibility?.["pi-context"];
635
635
  if (piCtxCfg?.enabled && piCtxCfg?.autoEnableAcm !== false && hasPiContextTools(pi)) {
636
- enableACM(pi);
636
+ notifyAcmReminder(ctx);
637
637
  }
638
638
  if (activePersonalizations.get(vaultKey) === controller) {
639
639
  activePersonalizations.delete(vaultKey);
@@ -631,7 +631,7 @@ export const setupWizard = async (ctx, cliArgs) => {
631
631
  // config alongside everything else (no separate project file).
632
632
  config.extensionCompatibility = config.extensionCompatibility || {};
633
633
  config.extensionCompatibility["pi-context"] = {
634
- tagPatterns: [],
634
+ checkpointPatterns: [],
635
635
  enhanceInjectors: false,
636
636
  autoEnableAcm: true,
637
637
  indexContextEvents: true,
@@ -246,7 +246,7 @@ export interface IdentitiesConfig {
246
246
  }
247
247
  export interface PiContextDef {
248
248
  enabled?: boolean;
249
- tagPatterns?: string[];
249
+ checkpointPatterns?: string[];
250
250
  enhanceInjectors?: boolean;
251
251
  autoEnableAcm?: boolean;
252
252
  indexContextEvents?: boolean;