pi-vault-mind 0.16.34 → 0.16.36
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 +21 -0
- package/README.md +32 -11
- package/dist/src/agent-docs.js +55 -0
- package/dist/src/agent-queue.js +3 -2
- package/dist/src/agents/BroadcasterAgent.js +3 -2
- package/dist/src/agents/HeavyLifterAgent.js +3 -2
- package/dist/src/agents/ManagerAgent.js +3 -2
- package/dist/src/agents/MinerAgent.js +3 -2
- package/dist/src/commands.js +101 -5
- package/dist/src/dispatch-writeback.js +20 -0
- package/dist/src/docling.js +43 -0
- package/dist/src/engine.js +2 -0
- package/dist/src/personalize.js +8 -2
- package/dist/src/tool-catalog.js +1 -0
- package/dist/src/tools.js +57 -27
- package/dist/src/vm-handlers.js +10 -20
- package/dist/src/vm-script.js +201 -0
- package/dist/src/watcher.js +164 -62
- package/dist/test/agent-docs.test.js +28 -0
- package/dist/test/agent-queue.test.js +9 -8
- package/dist/test/agents/Agents.test.js +6 -5
- package/dist/test/check-deps-script.test.js +8 -1
- package/dist/test/commands.test.js +43 -0
- package/dist/test/index.test.js +24 -0
- package/dist/test/personalize.test.js +63 -0
- package/dist/test/pvm-setup.test.js +19 -0
- package/dist/test/tool-catalog.test.js +10 -4
- package/dist/test/vm-script-tool.test.js +21 -0
- package/package.json +3 -2
- package/scripts/check-deps.sh +5 -5
- package/scripts/pvm-setup.mjs +39 -8
- package/skills/vault-mind/SKILL.md +29 -3
- package/skills/vault-mind-miner/SKILL.md +5 -5
- package/skills/vault-mind-setup/references/obsidian-vault-structure.md +1 -1
- package/skills/vault-mind-setup/references/pi-extension-wiring.md +12 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.16.36 — 2026-08-30
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **Node 24 runtime floor.** The supported Node.js floor is now `24.19.0+`, matching the current local/runtime toolchain and simplifying support expectations.
|
|
8
|
+
- **GitHub workflows disabled.** The repository no longer runs the generic `ci.yml` or `release-obsidian.yml` automation. Verification and publishing are currently a local operator workflow; the former workflow files are preserved under `.github/workflows-disabled/` for future reuse.
|
|
9
|
+
|
|
10
|
+
## 0.16.35 — 2026-08-30
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Watcher request-block preservation.** The vault watcher no longer destructively replaces `@agent` markers with subagent output. The original request block is preserved in the source note; only the running-status line is updated with a brief completion summary. Requests are reviewable and the vault note remains a faithful record of what was asked.
|
|
15
|
+
- **Reserved `@agent:main` identity.** The `@agent:main` marker is recognized by the watcher but never auto-dispatched to a background subagent. It is reserved for the interactive main session. Use `/vm main sweep [file-or-folder]` to collect pending `@agent:main` markers and address them in the current session.
|
|
16
|
+
- **Stock `system.md` and `AGENTS.md` baseline provisioning.** `pvm setup` provisions vault-local `system.md` (agent system prompt) and vault-root `AGENTS.md` (agent conventions) from curated stock baselines when those files do not already exist, giving every fresh vault a working agent identity without manual authoring. Baselines are demarcated with `<!-- vm:baseline:start/end -->` tags so `/vm personalize` can update only the personalization section without touching the baseline.
|
|
17
|
+
- **`vm_script` tool and run-sdk scaffold.** A sandboxed JavaScript/TypeScript execution tool (`vm_script`) is scaffolded using the `run` QuickJS package. Scripts execute with no ambient filesystem, network, or timer access beyond declared host functions (`vm.*`, `vault.read`, `log`). Enabled by default for manager and main agents. Follow-up: HITL interruption path, `vault.write`, and `pvm run <script>` CLI subcommand.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- **docling replaces any2md.** The Miner's document-conversion path (`vm_ingest`) now uses `docling` (via `uvx` or a globally installed `pip install docling`) for PDF, DOCX, HTML, and URL normalization. The `any2md` npm package is no longer a dependency or runtime requirement.
|
|
22
|
+
|
|
23
|
+
|
|
3
24
|
## 0.16.34 — 2026-08-29
|
|
4
25
|
|
|
5
26
|
### Added
|
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ Passive Obsidian vault extension for the [pi](https://github.com/mariozechner/pi
|
|
|
12
12
|
|
|
13
13
|
## Features
|
|
14
14
|
|
|
15
|
-
- **Passive File Watcher** — drop `@agent
|
|
15
|
+
- **Passive File Watcher** — drop `@agent:<role>` (or `@agent-<role>`) markers in any Obsidian note. Save. The watcher detects the marker, groups by role, and dispatches isolated subagent forks. The original request block is preserved in the note; only the running-status line is updated with the result summary. `@agent:main` is reserved for the current interactive session and is never auto-dispatched — sweep it manually with `/vm main sweep`. No chat pollution, no manual triggers.
|
|
16
16
|
- **Multi-Agent Architecture** — five specialist agents under the `vault-mind-{role}` skill naming: **Manager** (interactive orchestrator), **Miner** (research + entity extraction), **Broadcaster** (NotebookLM artifacts), **Heavy-Lifter** (external delegation), **Watcher** (passive file observer).
|
|
17
17
|
- **LanceDB Vector + FTS + Graph** — hybrid semantic + keyword search with automatic entity extraction and BFS graph traversal. All local, no external binaries.
|
|
18
18
|
- **JSONL Source-of-Truth + LanceDB Index** — every fact lives in a durable, human-readable, version-control-friendly `collections/*.jsonl` file. The LanceDB index is derived and rebuildable via `/vm reindex --all --reembed`.
|
|
@@ -171,7 +171,7 @@ The Broadcaster agent can generate podcasts, study guides, and slide decks from
|
|
|
171
171
|
|
|
172
172
|
### Prerequisites
|
|
173
173
|
|
|
174
|
-
- **Node.js**
|
|
174
|
+
- **Node.js** 24.19.0+ (matches `engines` in `package.json`)
|
|
175
175
|
- **Embedding Provider** — choose one:
|
|
176
176
|
- `@xenova/transformers` — built-in, no external deps (uses all-MiniLM-L6-v2, offline-capable)
|
|
177
177
|
- `ollama` — requires Ollama running locally with `embeddinggemma` (higher quality)
|
|
@@ -200,8 +200,10 @@ context, model-routing, task, and subagent extensions inside
|
|
|
200
200
|
`<vault>/.vault-mind/.pi/agent/`; installs the curated `obsidian-cli`,
|
|
201
201
|
`obsidian-markdown`, `obsidian-bases`, and `json-canvas` skills; and
|
|
202
202
|
merge-safely scaffolds Vault Mind, model-router, and main-agent configuration.
|
|
203
|
-
|
|
204
|
-
preserved
|
|
203
|
+
On first run it also creates stock `.pi/agent/system.md` and vault-root
|
|
204
|
+
`AGENTS.md` baseline regions. All of these are preserved on repeat runs —
|
|
205
|
+
existing user configuration, collections, model choices, and personalizations
|
|
206
|
+
are never overwritten.
|
|
205
207
|
|
|
206
208
|
Refresh only extensions, tools, and skills without touching vault
|
|
207
209
|
configuration:
|
|
@@ -360,16 +362,33 @@ applies no boundary while the persona is `main`.
|
|
|
360
362
|
### Use specialist agents without changing the main session
|
|
361
363
|
|
|
362
364
|
Normally, leave the interactive session on `main` and dispatch specialist work
|
|
363
|
-
through the passive watcher:
|
|
365
|
+
through the passive watcher. Both marker forms are equivalent:
|
|
364
366
|
|
|
365
367
|
```text
|
|
366
|
-
@agent
|
|
368
|
+
@agent:miner
|
|
367
369
|
Research and extract the durable facts from this note.
|
|
368
370
|
```
|
|
369
371
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
372
|
+
or
|
|
373
|
+
|
|
374
|
+
```text
|
|
375
|
+
@agent-miner
|
|
376
|
+
Research and extract the durable facts from this note.
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
The watcher runs that work as an isolated specialist job, preserving the
|
|
380
|
+
request block in the source note and replacing only the running-status line
|
|
381
|
+
with a result summary. It does not turn the interactive terminal into the
|
|
382
|
+
Miner persona. The same applies to `@agent:broadcaster`, `@agent:manager`,
|
|
383
|
+
and `@agent:heavy-lifter`.
|
|
384
|
+
|
|
385
|
+
`@agent:main` is reserved for the current interactive session and is never
|
|
386
|
+
auto-dispatched. To sweep reserved markers into the active session:
|
|
387
|
+
|
|
388
|
+
```text
|
|
389
|
+
/vm main sweep
|
|
390
|
+
/vm main sweep path/to/note.md
|
|
391
|
+
```
|
|
373
392
|
|
|
374
393
|
### Explicitly change the interactive persona
|
|
375
394
|
|
|
@@ -535,7 +554,8 @@ belongs in `vaultMind.identities` or an explicit environment override.
|
|
|
535
554
|
| `vm_describe` | Introspect schema, count, and sample entries |
|
|
536
555
|
| `vm_stats` | Dashboard: counts, sizes, LanceDB status |
|
|
537
556
|
| `vm_export` | Export to JSON, CSV, or Markdown |
|
|
538
|
-
| `
|
|
557
|
+
| `vm_ingest` | Convert URLs and documents to markdown via docling |
|
|
558
|
+
| `vm_script` | Execute sandboxed JS/TS with vm.*, vault.read, log.* |
|
|
539
559
|
|
|
540
560
|
## Commands
|
|
541
561
|
|
|
@@ -555,7 +575,8 @@ belongs in `vaultMind.identities` or an explicit environment override.
|
|
|
555
575
|
| `/vm embedding status \| use \| model \| models \| pull` | Manage embedding provider |
|
|
556
576
|
| `/vm identity status \| set <role> \| on \| off` | Inspect or explicitly control the interactive agent persona boundary |
|
|
557
577
|
| `/vm remote status \| config \| sync \| jobs \| migrate` | Manage remote embedding + vector sync |
|
|
558
|
-
| `/vm watcher start \| stop \| status` | Manage the passive file watcher
|
|
578
|
+
| `/vm watcher start \| stop \| status` | Manage the passive file watcher |
|
|
579
|
+
| `/vm main sweep [file]` | Sweep `@agent:main` markers into the current interactive session |
|
|
559
580
|
| `/vm server status` | Show HTTP server status, port, and uptime |
|
|
560
581
|
|
|
561
582
|
## Documentation
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
export const BASELINE_START = "<!-- vm:baseline:start -->";
|
|
3
|
+
export const BASELINE_END = "<!-- vm:baseline:end -->";
|
|
4
|
+
export const PERSONALIZATION_START = "<!-- vm:personalization:start -->";
|
|
5
|
+
export const PERSONALIZATION_END = "<!-- vm:personalization:end -->";
|
|
6
|
+
export const buildDefaultSystemMd = (vaultPath) => {
|
|
7
|
+
const vaultName = path.basename(vaultPath);
|
|
8
|
+
return `# Pi main agent: ${vaultName}
|
|
9
|
+
|
|
10
|
+
${BASELINE_START}
|
|
11
|
+
You are the unrestricted main interactive agent for this vault. Bundled specialist skills are available resources; their presence does not change your active identity. Adopt a specialist identity only when explicitly launched as that subagent.
|
|
12
|
+
|
|
13
|
+
## Operating baseline
|
|
14
|
+
- Read \`00-system/SYSTEM.md\` first when it exists. If it does not exist, fall back to a vault-root \`SYSTEM.md\`. Then read the vault-root \`AGENTS.md\`.
|
|
15
|
+
- Prefer the vault's own operating manual and schemas over generic assumptions.
|
|
16
|
+
- Use the \`vm_*\` tools, collections, and queue as the durable system of record for agent work.
|
|
17
|
+
- Prefer \`uv\` for Python tools and CLIs. Use \`pnpm\`/\`npm\` for JavaScript and TypeScript packages such as \`run\`.
|
|
18
|
+
- Use \`docling\` for document conversion in ingest flows.
|
|
19
|
+
- Use \`vm_script\` for reusable sandboxed JS/TS workflows when the task fits the approved host-function surface.
|
|
20
|
+
- Never auto-publish, never fabricate facts, and keep human approval at external side effects.
|
|
21
|
+
- The watcher recognizes \`@agent:<role>\` and \`@agent-<role>\` anywhere in watchable files. \`@agent:main\` is reserved for the main interactive flow and is not auto-dispatched.
|
|
22
|
+
${BASELINE_END}
|
|
23
|
+
|
|
24
|
+
${PERSONALIZATION_START}
|
|
25
|
+
Add vault-specific priorities, domain language, and working preferences here. /vm personalize may update only this section.
|
|
26
|
+
${PERSONALIZATION_END}
|
|
27
|
+
`;
|
|
28
|
+
};
|
|
29
|
+
export const buildDefaultAgentsMd = () => `# AGENTS: pi-vault-mind operating baseline
|
|
30
|
+
|
|
31
|
+
${BASELINE_START}
|
|
32
|
+
## Agent roles
|
|
33
|
+
- \`main\` — the interactive agent you work with directly.
|
|
34
|
+
- \`manager\` — synthesis, orchestration, and roadmap work.
|
|
35
|
+
- \`miner\` — extraction, ingestion, and structured knowledge capture.
|
|
36
|
+
- \`broadcaster\` — human-facing artifact generation.
|
|
37
|
+
- \`heavy-lifter\` — large-context implementation and deeper delegated work.
|
|
38
|
+
- \`personalization\` — resolves annotated feedback on generated system/agent docs.
|
|
39
|
+
|
|
40
|
+
## Watcher + inline requests
|
|
41
|
+
- Use \`@agent:<role>\` or \`@agent-<role>\` in watchable files to request agent help.
|
|
42
|
+
- \`@agent:main\` is recognized but never auto-dispatched; it is for main-agent or human-led follow-up.
|
|
43
|
+
- Auto-dispatched watcher jobs preserve the request block in the source note and replace only the running-status line with the result summary.
|
|
44
|
+
- Watcher dispatches should be logged to collections so they can be queried later.
|
|
45
|
+
|
|
46
|
+
## Tooling baseline
|
|
47
|
+
- Prefer \`uv\` for Python tools.
|
|
48
|
+
- Prefer reusable \`vm_script\` workflows over ad-hoc one-off scripts when the workflow should be repeatable.
|
|
49
|
+
- Keep reusable content, test, and update workflows in skills/tools rather than scattered shell snippets.
|
|
50
|
+
${BASELINE_END}
|
|
51
|
+
|
|
52
|
+
${PERSONALIZATION_START}
|
|
53
|
+
Add vault-specific agent roster details, domain-specific prompts, and delegation rules here. /vm personalize may update only this section.
|
|
54
|
+
${PERSONALIZATION_END}
|
|
55
|
+
`;
|
package/dist/src/agent-queue.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import * as fs from "node:fs";
|
|
14
14
|
import * as path from "node:path";
|
|
15
|
+
import { dispatchStatusLine } from "./dispatch-writeback.js";
|
|
15
16
|
import { loadConfig, resolveVaultFile } from "./utils.js";
|
|
16
17
|
const queueSubscribers = new Set();
|
|
17
18
|
/** Subscribe to queue events. Returns an unsubscribe function. */
|
|
@@ -138,11 +139,11 @@ export const cancelJob = (vaultPath, id) => {
|
|
|
138
139
|
return undefined;
|
|
139
140
|
return updateJob(vaultPath, id, "cancelled");
|
|
140
141
|
};
|
|
141
|
-
/** True if the file still contains the dispatch
|
|
142
|
+
/** True if the file still contains the pending dispatch status placeholder. */
|
|
142
143
|
export const markerStillPresent = (filePath, dispatchId) => {
|
|
143
144
|
try {
|
|
144
145
|
const content = fs.readFileSync(filePath, "utf-8");
|
|
145
|
-
return content.includes(
|
|
146
|
+
return content.includes(dispatchStatusLine(dispatchId));
|
|
146
147
|
}
|
|
147
148
|
catch {
|
|
148
149
|
return false;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* Writes output only under Agent/Presentations/.
|
|
7
7
|
*/
|
|
8
8
|
import { Agent } from "../agent-bus/Agent.js";
|
|
9
|
+
import { buildDispatchResultLine, dispatchStatusLine } from "../dispatch-writeback.js";
|
|
9
10
|
export class BroadcasterAgent extends Agent {
|
|
10
11
|
taskQueue = null;
|
|
11
12
|
constructor(config, bus, llm) {
|
|
@@ -58,8 +59,8 @@ export class BroadcasterAgent extends Agent {
|
|
|
58
59
|
path: job.filePath,
|
|
59
60
|
edits: [
|
|
60
61
|
{
|
|
61
|
-
oldText:
|
|
62
|
-
newText:
|
|
62
|
+
oldText: dispatchStatusLine(job.dispatchId),
|
|
63
|
+
newText: buildDispatchResultLine(job.dispatchId, `Broadcaster completed. See dispatch ${job.dispatchId}.`),
|
|
63
64
|
},
|
|
64
65
|
],
|
|
65
66
|
});
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* Runs bash in isolated worktree.
|
|
7
7
|
*/
|
|
8
8
|
import { Agent } from "../agent-bus/Agent.js";
|
|
9
|
+
import { buildDispatchResultLine, dispatchStatusLine } from "../dispatch-writeback.js";
|
|
9
10
|
export class HeavyLifterAgent extends Agent {
|
|
10
11
|
taskQueue = null;
|
|
11
12
|
constructor(config, bus, llm) {
|
|
@@ -61,8 +62,8 @@ export class HeavyLifterAgent extends Agent {
|
|
|
61
62
|
path: job.filePath,
|
|
62
63
|
edits: [
|
|
63
64
|
{
|
|
64
|
-
oldText:
|
|
65
|
-
newText:
|
|
65
|
+
oldText: dispatchStatusLine(job.dispatchId),
|
|
66
|
+
newText: buildDispatchResultLine(job.dispatchId, `Heavy-Lifter completed. See dispatch ${job.dispatchId}.`),
|
|
66
67
|
},
|
|
67
68
|
],
|
|
68
69
|
});
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* Only the Manager can dispatch subagents and publish to the human-facing vault.
|
|
8
8
|
*/
|
|
9
9
|
import { Agent } from "../agent-bus/Agent.js";
|
|
10
|
+
import { buildDispatchResultLine, dispatchStatusLine } from "../dispatch-writeback.js";
|
|
10
11
|
export class ManagerAgent extends Agent {
|
|
11
12
|
taskQueue = null;
|
|
12
13
|
constructor(config, bus, llm) {
|
|
@@ -59,8 +60,8 @@ export class ManagerAgent extends Agent {
|
|
|
59
60
|
path: job.filePath,
|
|
60
61
|
edits: [
|
|
61
62
|
{
|
|
62
|
-
oldText:
|
|
63
|
-
newText:
|
|
63
|
+
oldText: dispatchStatusLine(job.dispatchId),
|
|
64
|
+
newText: buildDispatchResultLine(job.dispatchId, `Manager completed. See dispatch ${job.dispatchId}.`),
|
|
64
65
|
},
|
|
65
66
|
],
|
|
66
67
|
});
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* Cannot publish (vm_sync disabled).
|
|
7
7
|
*/
|
|
8
8
|
import { Agent } from "../agent-bus/Agent.js";
|
|
9
|
+
import { buildDispatchResultLine, dispatchStatusLine } from "../dispatch-writeback.js";
|
|
9
10
|
export class MinerAgent extends Agent {
|
|
10
11
|
taskQueue = null;
|
|
11
12
|
constructor(config, bus, llm) {
|
|
@@ -58,8 +59,8 @@ export class MinerAgent extends Agent {
|
|
|
58
59
|
path: job.filePath,
|
|
59
60
|
edits: [
|
|
60
61
|
{
|
|
61
|
-
oldText:
|
|
62
|
-
newText:
|
|
62
|
+
oldText: dispatchStatusLine(job.dispatchId),
|
|
63
|
+
newText: buildDispatchResultLine(job.dispatchId, `Miner extracted findings. See dispatch ${job.dispatchId}.`),
|
|
63
64
|
},
|
|
64
65
|
],
|
|
65
66
|
});
|
package/dist/src/commands.js
CHANGED
|
@@ -19,11 +19,11 @@ import { scaffoldVaultConfig } from "./scaffold.js";
|
|
|
19
19
|
import { createServerState, startServer } from "./server.js";
|
|
20
20
|
import { createCollectionWizard, createInjectorWizard, openSettingsDashboard, setupWizard, } from "./settings-ui.js";
|
|
21
21
|
import { getActiveCollection, setActiveCollection } from "./state.js";
|
|
22
|
+
import { createWatcherState, getWatcherStatus, isWatchableFile, scanFile, startWatcher, stopWatcher } from "./watcher.js";
|
|
22
23
|
import { reindexRemote, syncAll, syncCollection } from "./sync.js";
|
|
23
24
|
import { tombstoneEntryById } from "./tombstone.js";
|
|
24
25
|
import { DEFAULT_CONFIG } from "./types.js";
|
|
25
26
|
import { collectionNames, ensureDir, expandHome, findConfig, getConfigPath, getPiContextConfig, hasPiContextTools, isVaultMindReady, loadConfig, resolveVaultMindPaths, } from "./utils.js";
|
|
26
|
-
import { createWatcherState, getWatcherStatus, startWatcher, stopWatcher } from "./watcher.js";
|
|
27
27
|
import { updateActiveCollectionWidget } from "./widget.js";
|
|
28
28
|
// ── Shared helpers ───────────────────────────────────────────────────────────
|
|
29
29
|
const VM_USAGE = [
|
|
@@ -51,15 +51,15 @@ const VM_USAGE = [
|
|
|
51
51
|
" /vm remote auto Discover models from the configured Remote URL",
|
|
52
52
|
" /vm remote sync Pull server vectors into local LanceDB [--full]",
|
|
53
53
|
" /vm remote jobs <id> Poll a remote bulk embed job",
|
|
54
|
-
" /vm watcher start
|
|
54
|
+
" /vm watcher start [roles] Start passive file watcher (optional role allowlist)",
|
|
55
55
|
" /vm watcher stop Stop passive file watcher",
|
|
56
56
|
" /vm watcher status Show watcher status",
|
|
57
57
|
" /vm watcher queue [status] Show agent dispatch queue (pending|running|done|failed|cancelled|all)",
|
|
58
58
|
" /vm watcher retry <id> Retry a failed or cancelled agent job",
|
|
59
59
|
" /vm watcher cancel <id> Cancel a pending agent job",
|
|
60
|
+
" /vm main sweep [file] Sweep @agent:main markers into the current session",
|
|
60
61
|
" /vm server status Show HTTP server status + port",
|
|
61
62
|
" /vm server start Start the in-process HTTP server",
|
|
62
|
-
" /vm personalize Analyze vault and suggest personality tuning for system.md + AGENTS.md",
|
|
63
63
|
" /vm setup Onboarding flow: config + personalize (auto-uses cwd when it's a vault)",
|
|
64
64
|
" /vm setup --vault <path> CLI: set vault path",
|
|
65
65
|
" /vm setup --remoteUrl <url> CLI: set remote embedding URL",
|
|
@@ -1297,6 +1297,90 @@ const handleServer = (args, ctx, pi) => {
|
|
|
1297
1297
|
}
|
|
1298
1298
|
ctx.ui.notify(lines.join("\n"), "info");
|
|
1299
1299
|
};
|
|
1300
|
+
const MAIN_SWEEP_IGNORE_DIRS = {
|
|
1301
|
+
".git": true,
|
|
1302
|
+
".obsidian": true,
|
|
1303
|
+
node_modules: true,
|
|
1304
|
+
".trash": true,
|
|
1305
|
+
__pycache__: true,
|
|
1306
|
+
".DS_Store": true,
|
|
1307
|
+
".pi": true,
|
|
1308
|
+
".lancedb": true,
|
|
1309
|
+
};
|
|
1310
|
+
const collectSweepFiles = (targetPath) => {
|
|
1311
|
+
const stat = fs.statSync(targetPath);
|
|
1312
|
+
if (stat.isFile())
|
|
1313
|
+
return isWatchableFile(targetPath) ? [targetPath] : [];
|
|
1314
|
+
if (!stat.isDirectory())
|
|
1315
|
+
return [];
|
|
1316
|
+
const files = [];
|
|
1317
|
+
for (const entry of fs.readdirSync(targetPath, { withFileTypes: true })) {
|
|
1318
|
+
if (MAIN_SWEEP_IGNORE_DIRS[entry.name])
|
|
1319
|
+
continue;
|
|
1320
|
+
const child = path.join(targetPath, entry.name);
|
|
1321
|
+
if (entry.isDirectory()) {
|
|
1322
|
+
files.push(...collectSweepFiles(child));
|
|
1323
|
+
}
|
|
1324
|
+
else if (entry.isFile() && isWatchableFile(child)) {
|
|
1325
|
+
files.push(child);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
return files;
|
|
1329
|
+
};
|
|
1330
|
+
const handleMain = async (args, ctx, pi) => {
|
|
1331
|
+
const parts = args.trim().split(/\s+/g).filter(Boolean);
|
|
1332
|
+
const subcommand = parts[0]?.toLowerCase() || "sweep";
|
|
1333
|
+
if (subcommand !== "sweep") {
|
|
1334
|
+
ctx.ui.notify("Usage: /vm main sweep [file-or-folder]", "error");
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
const configuredVault = loadConfig(ctx.cwd).vaultMind.vaults?.default?.path;
|
|
1338
|
+
const vaultRoot = configuredVault ? expandHome(configuredVault) : ctx.cwd;
|
|
1339
|
+
const targetArg = parts.slice(1).join(" ").trim();
|
|
1340
|
+
const targetPath = targetArg
|
|
1341
|
+
? path.isAbsolute(targetArg)
|
|
1342
|
+
? targetArg
|
|
1343
|
+
: path.join(ctx.cwd, targetArg)
|
|
1344
|
+
: vaultRoot;
|
|
1345
|
+
if (!fs.existsSync(targetPath)) {
|
|
1346
|
+
ctx.ui.notify(`Main sweep target not found: ${targetPath}`, "error");
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
const items = collectSweepFiles(targetPath)
|
|
1350
|
+
.flatMap((filePath) => scanFile(filePath, vaultRoot).flatMap((group) => group.role !== "main"
|
|
1351
|
+
? []
|
|
1352
|
+
: group.markers.map((marker) => ({
|
|
1353
|
+
filePath,
|
|
1354
|
+
lineNumber: marker.lineNumber,
|
|
1355
|
+
instruction: marker.instruction || marker.rawLine.trim(),
|
|
1356
|
+
context: marker.context,
|
|
1357
|
+
}))));
|
|
1358
|
+
if (items.length === 0) {
|
|
1359
|
+
ctx.ui.notify("No unresolved @agent:main markers found.", "info");
|
|
1360
|
+
return;
|
|
1361
|
+
}
|
|
1362
|
+
const prompt = [
|
|
1363
|
+
"# Main Agent Sweep",
|
|
1364
|
+
"",
|
|
1365
|
+
"Review the following inline @agent:main requests. These are reserved for the current interactive main session and are not auto-dispatched.",
|
|
1366
|
+
"Address the requests directly in this session. Keep the source notes intact unless an edit is explicitly needed.",
|
|
1367
|
+
"",
|
|
1368
|
+
...items.flatMap((item, index) => [
|
|
1369
|
+
`## Item ${index + 1}`,
|
|
1370
|
+
`- File: ${item.filePath}`,
|
|
1371
|
+
`- Line: ${item.lineNumber}`,
|
|
1372
|
+
`- Request: ${item.instruction}`,
|
|
1373
|
+
"",
|
|
1374
|
+
"Context:",
|
|
1375
|
+
"```",
|
|
1376
|
+
item.context,
|
|
1377
|
+
"```",
|
|
1378
|
+
"",
|
|
1379
|
+
]),
|
|
1380
|
+
].join("\n");
|
|
1381
|
+
pi.sendMessage({ customType: "vm-main-sweep", content: prompt, display: false }, { triggerTurn: true });
|
|
1382
|
+
ctx.ui.notify(`Sweeping ${items.length} @agent:main item(s) from ${new Set(items.map((item) => item.filePath)).size} file(s).`, "info");
|
|
1383
|
+
};
|
|
1300
1384
|
const handleWatcher = async (args, ctx, pi) => {
|
|
1301
1385
|
const parts = args.trim().split(/\s+/g);
|
|
1302
1386
|
const subcommand = parts[0]?.toLowerCase() || "status";
|
|
@@ -1316,7 +1400,12 @@ const handleWatcher = async (args, ctx, pi) => {
|
|
|
1316
1400
|
ctx.ui.notify("Watcher is already running.", "info");
|
|
1317
1401
|
return;
|
|
1318
1402
|
}
|
|
1319
|
-
|
|
1403
|
+
const requestedRoles = parts
|
|
1404
|
+
.slice(1)
|
|
1405
|
+
.flatMap((part) => part.split(","))
|
|
1406
|
+
.map((role) => role.trim().toLowerCase())
|
|
1407
|
+
.filter(Boolean);
|
|
1408
|
+
startWatcher(pi, cfg.vaultMind.vaults, watcherState, requestedRoles.length > 0 ? requestedRoles : undefined);
|
|
1320
1409
|
ctx.ui.notify(getWatcherStatus(watcherState), "info");
|
|
1321
1410
|
return;
|
|
1322
1411
|
}
|
|
@@ -1711,8 +1800,8 @@ export const registerCommands = (pi) => {
|
|
|
1711
1800
|
"embedding",
|
|
1712
1801
|
"remote",
|
|
1713
1802
|
"watcher",
|
|
1803
|
+
"main",
|
|
1714
1804
|
"personalize",
|
|
1715
|
-
"setup",
|
|
1716
1805
|
"token",
|
|
1717
1806
|
"index",
|
|
1718
1807
|
"reindex",
|
|
@@ -1741,6 +1830,11 @@ export const registerCommands = (pi) => {
|
|
|
1741
1830
|
.filter((c) => c.startsWith(prefix))
|
|
1742
1831
|
.map((c) => ({ label: c, value: c, description: `identity ${c}` }));
|
|
1743
1832
|
}
|
|
1833
|
+
if (subcommand === "main") {
|
|
1834
|
+
return ["sweep"]
|
|
1835
|
+
.filter((c) => c.startsWith(prefix))
|
|
1836
|
+
.map((c) => ({ label: c, value: c, description: `main ${c}` }));
|
|
1837
|
+
}
|
|
1744
1838
|
if (subcommand === "discover-schema") {
|
|
1745
1839
|
return ["--name", "--sample"]
|
|
1746
1840
|
.filter((c) => c.startsWith(prefix))
|
|
@@ -1825,6 +1919,8 @@ export const registerCommands = (pi) => {
|
|
|
1825
1919
|
return handleServer(rest, ctx, pi);
|
|
1826
1920
|
case "watcher":
|
|
1827
1921
|
return handleWatcher(rest, ctx, pi);
|
|
1922
|
+
case "main":
|
|
1923
|
+
return handleMain(rest, ctx, pi);
|
|
1828
1924
|
case "remote":
|
|
1829
1925
|
return handleModal(rest, ctx, pi);
|
|
1830
1926
|
case "personalize":
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const dispatchMarker = (dispatchId) => `<!-- pi-dispatch:${dispatchId} -->`;
|
|
2
|
+
export const dispatchStatusLine = (dispatchId) => `> Status [${dispatchId}]: running`;
|
|
3
|
+
const summarizeInstruction = (instruction) => {
|
|
4
|
+
const trimmed = instruction.trim();
|
|
5
|
+
return trimmed.length > 0 ? trimmed : "(no instruction provided)";
|
|
6
|
+
};
|
|
7
|
+
export const buildPrimaryDispatchBlock = (dispatchId, role, instructions, markerCount) => {
|
|
8
|
+
const lines = [dispatchMarker(dispatchId), `> Agent: ${role}`];
|
|
9
|
+
if (markerCount > 1) {
|
|
10
|
+
lines.push(`> Grouped markers: ${markerCount}`);
|
|
11
|
+
}
|
|
12
|
+
lines.push("> Requests:");
|
|
13
|
+
for (const instruction of instructions) {
|
|
14
|
+
lines.push(`> - ${summarizeInstruction(instruction)}`);
|
|
15
|
+
}
|
|
16
|
+
lines.push(dispatchStatusLine(dispatchId));
|
|
17
|
+
return lines.join("\n");
|
|
18
|
+
};
|
|
19
|
+
export const buildJoinedDispatchNote = (dispatchId, role, instruction, lineNumber) => `> Joined dispatch ${dispatchId} (${role}, line ${lineNumber}): ${summarizeInstruction(instruction)}`;
|
|
20
|
+
export const buildDispatchResultLine = (dispatchId, result) => `> Result [${dispatchId}]: ${result}`;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as os from "node:os";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
export const DOCLING_DOC_EXTS = {
|
|
8
|
+
".pdf": true,
|
|
9
|
+
".docx": true,
|
|
10
|
+
".pptx": true,
|
|
11
|
+
".xlsx": true,
|
|
12
|
+
".epub": true,
|
|
13
|
+
".html": true,
|
|
14
|
+
".htm": true,
|
|
15
|
+
".xml": true,
|
|
16
|
+
};
|
|
17
|
+
const doclingErrorMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
18
|
+
export const sourceNeedsDocling = (source, sourceType) => {
|
|
19
|
+
if (sourceType === "url")
|
|
20
|
+
return true;
|
|
21
|
+
if (sourceType !== "file")
|
|
22
|
+
return false;
|
|
23
|
+
return DOCLING_DOC_EXTS[path.extname(source).toLowerCase()] === true;
|
|
24
|
+
};
|
|
25
|
+
export const convertWithDocling = async (source, cwd) => {
|
|
26
|
+
const outputDir = fs.mkdtempSync(path.join(os.tmpdir(), "pvm-docling-"));
|
|
27
|
+
try {
|
|
28
|
+
await execFileAsync("docling", ["convert", source, "--to", "md", "--output", outputDir, "--quiet"], { cwd, timeout: 120000 });
|
|
29
|
+
const markdownFile = fs
|
|
30
|
+
.readdirSync(outputDir)
|
|
31
|
+
.find((file) => path.extname(file).toLowerCase() === ".md");
|
|
32
|
+
if (!markdownFile) {
|
|
33
|
+
throw new Error(`docling did not produce a markdown file for ${source}`);
|
|
34
|
+
}
|
|
35
|
+
return fs.readFileSync(path.join(outputDir, markdownFile), "utf-8");
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
throw new Error(`docling failed: ${doclingErrorMessage(error)}`);
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
fs.rmSync(outputDir, { recursive: true, force: true });
|
|
42
|
+
}
|
|
43
|
+
};
|
package/dist/src/engine.js
CHANGED
|
@@ -217,6 +217,7 @@ export function buildIdentityFromRole(role, id) {
|
|
|
217
217
|
"vm_codegraph_impact",
|
|
218
218
|
"vault_write",
|
|
219
219
|
"vault_append",
|
|
220
|
+
"vm_script",
|
|
220
221
|
],
|
|
221
222
|
writeCollections: ["main", "research", "presentations"],
|
|
222
223
|
canPublish: true,
|
|
@@ -248,6 +249,7 @@ export function buildIdentityFromRole(role, id) {
|
|
|
248
249
|
"vm_related",
|
|
249
250
|
"vm_broken_links",
|
|
250
251
|
"vm_codegraph_impact",
|
|
252
|
+
"vm_script",
|
|
251
253
|
],
|
|
252
254
|
readCollections: ["main"],
|
|
253
255
|
writeCollections: [],
|
package/dist/src/personalize.js
CHANGED
|
@@ -4,6 +4,7 @@ import { parseFrontmatter } from "@earendil-works/pi-coding-agent";
|
|
|
4
4
|
import { notifyAcmReminder } from "./context-capture.js";
|
|
5
5
|
import { getStatus } from "./lance.js";
|
|
6
6
|
import { scaffoldVaultConfig } from "./scaffold.js";
|
|
7
|
+
import { BASELINE_END, BASELINE_START, PERSONALIZATION_END, PERSONALIZATION_START, buildDefaultAgentsMd, buildDefaultSystemMd, } from "./agent-docs.js";
|
|
7
8
|
import { expandHome, getPersonalizedMarkerPath, hasPiContextTools, isPersonalized, loadConfig, resolveAgentDir, } from "./utils.js";
|
|
8
9
|
class PersonalizationCancelledError extends Error {
|
|
9
10
|
constructor() {
|
|
@@ -197,6 +198,8 @@ const buildPrompt = (profile, currentConfig) => {
|
|
|
197
198
|
},
|
|
198
199
|
],
|
|
199
200
|
};
|
|
201
|
+
const seededSystemMd = currentConfig.systemMd ?? buildDefaultSystemMd(profile.vaultPath);
|
|
202
|
+
const seededAgentsMd = currentConfig.agentsMd ?? buildDefaultAgentsMd();
|
|
200
203
|
const lines = [
|
|
201
204
|
"# Vault Personality Tuning",
|
|
202
205
|
"",
|
|
@@ -222,12 +225,12 @@ const buildPrompt = (profile, currentConfig) => {
|
|
|
222
225
|
"",
|
|
223
226
|
"### .pi/agent/system.md",
|
|
224
227
|
"```markdown",
|
|
225
|
-
|
|
228
|
+
seededSystemMd,
|
|
226
229
|
"```",
|
|
227
230
|
"",
|
|
228
231
|
"### AGENTS.md",
|
|
229
232
|
"```markdown",
|
|
230
|
-
|
|
233
|
+
seededAgentsMd,
|
|
231
234
|
"```",
|
|
232
235
|
"",
|
|
233
236
|
"## Instructions",
|
|
@@ -242,6 +245,9 @@ const buildPrompt = (profile, currentConfig) => {
|
|
|
242
245
|
"- If a file does not currently exist, use action: create.",
|
|
243
246
|
"- If a file exists but needs changes, use action: update and provide the full new content.",
|
|
244
247
|
"- If a file is fine as-is, use action: skip and omit content.",
|
|
248
|
+
"- Keep all content between the baseline markers unchanged.",
|
|
249
|
+
`- The baseline markers are: ${BASELINE_START}, ${BASELINE_END}, ${PERSONALIZATION_START}, ${PERSONALIZATION_END}.`,
|
|
250
|
+
"- Put vault-specific tuning only inside the personalization section.",
|
|
245
251
|
"- Tailor the voice, priorities, and conventions to the vault's domains and tags.",
|
|
246
252
|
"- Keep files concise and actionable.",
|
|
247
253
|
];
|
package/dist/src/tool-catalog.js
CHANGED
|
@@ -46,6 +46,7 @@ const EXTENSION_TOOLS = [
|
|
|
46
46
|
description: "Promote a pending entry to the main collection",
|
|
47
47
|
},
|
|
48
48
|
{ name: "vm_ingest", kind: "vm_data", description: "Ingest content into a vault collection" },
|
|
49
|
+
{ name: "vm_script", kind: "vm_data", description: "Execute sandboxed JS/TS against curated host functions" },
|
|
49
50
|
{ name: "vm_export", kind: "vm_data", description: "Export collection data" },
|
|
50
51
|
{
|
|
51
52
|
name: "vm_stats",
|