pi-vault-mind 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/dist/src/index.js +5 -1
- package/dist/src/server.js +39 -0
- package/dist/src/tools/codegraph.d.ts +15 -0
- package/dist/src/tools/codegraph.js +118 -0
- package/dist/src/tools.js +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/dist/src/index.js
CHANGED
|
@@ -53,7 +53,11 @@ export default function (pi) {
|
|
|
53
53
|
})()
|
|
54
54
|
: {};
|
|
55
55
|
const projectEmbed = projectCfg.vaultMind?.embedding;
|
|
56
|
-
const
|
|
56
|
+
const projectAgentLLM = projectCfg.vaultMind?.agentLLM;
|
|
57
|
+
const isSetup = !!(projectEmbed?.remoteUrl ||
|
|
58
|
+
projectEmbed?.localUrl ||
|
|
59
|
+
projectEmbed?.model ||
|
|
60
|
+
projectAgentLLM?.model);
|
|
57
61
|
const sessionCfg = loadConfig(ctx.cwd);
|
|
58
62
|
const piCtxCfg = sessionCfg.extensionCompatibility?.["pi-context"];
|
|
59
63
|
if (isSetup && piCtxCfg?.enabled && piCtxCfg?.autoEnableAcm !== false) {
|
package/dist/src/server.js
CHANGED
|
@@ -224,6 +224,14 @@ export function startServer(pi, serverState, watcherState) {
|
|
|
224
224
|
}
|
|
225
225
|
withAuth(req, res, () => handleVmPending(res, serverState));
|
|
226
226
|
break;
|
|
227
|
+
case "/vm/stats":
|
|
228
|
+
if (req.method !== "GET") {
|
|
229
|
+
res.writeHead(405);
|
|
230
|
+
res.end(JSON.stringify({ error: "Method not allowed" }));
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
withAuth(req, res, () => handleVmStatsRoute(res, serverState));
|
|
234
|
+
break;
|
|
227
235
|
default:
|
|
228
236
|
if (url.pathname.startsWith("/agent/jobs/")) {
|
|
229
237
|
withAuth(req, res, () => handleAgentJob(req, res, url.pathname), {
|
|
@@ -1059,3 +1067,34 @@ function handleVmPending(res, serverState) {
|
|
|
1059
1067
|
res.writeHead(200);
|
|
1060
1068
|
res.end(JSON.stringify({ pending }));
|
|
1061
1069
|
}
|
|
1070
|
+
function handleVmStatsRoute(res, serverState) {
|
|
1071
|
+
const vaultPath = serverState.vaultPath || process.cwd();
|
|
1072
|
+
const cfg = loadConfig(vaultPath);
|
|
1073
|
+
const collections = [];
|
|
1074
|
+
for (const [name, def] of Object.entries(cfg.collections)) {
|
|
1075
|
+
let count = 0;
|
|
1076
|
+
let malformed = 0;
|
|
1077
|
+
let size = 0;
|
|
1078
|
+
if (fs.existsSync(def.path)) {
|
|
1079
|
+
try {
|
|
1080
|
+
size = fs.statSync(def.path).size;
|
|
1081
|
+
const lines = fs.readFileSync(def.path, "utf-8").split("\n").filter(Boolean);
|
|
1082
|
+
for (const line of lines) {
|
|
1083
|
+
try {
|
|
1084
|
+
JSON.parse(line);
|
|
1085
|
+
count++;
|
|
1086
|
+
}
|
|
1087
|
+
catch {
|
|
1088
|
+
malformed++;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
catch {
|
|
1093
|
+
// file unreadable — leave counts at 0
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
collections.push({ name, path: def.path, schema: def.schema, count, size, malformed });
|
|
1097
|
+
}
|
|
1098
|
+
res.writeHead(200);
|
|
1099
|
+
res.end(JSON.stringify({ collections }));
|
|
1100
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
/**
|
|
4
|
+
* Look up `symbol` in the `.codegraph/` index at `projectRoot` and return a
|
|
5
|
+
* concise, human-readable impact summary.
|
|
6
|
+
*
|
|
7
|
+
* Runs `codegraph impact` and `codegraph callers` in parallel, formats the
|
|
8
|
+
* results into a text block, and never throws — all errors surface as message
|
|
9
|
+
* strings suitable for direct tool output.
|
|
10
|
+
*/
|
|
11
|
+
export declare const getCodegraphImpact: (symbol: string, projectRoot: string) => Promise<string>;
|
|
12
|
+
export declare const vmCodegraphImpactTool: import("@earendil-works/pi-coding-agent").ToolDefinition<Type.TObject<{
|
|
13
|
+
symbol: Type.TString;
|
|
14
|
+
}>, {}, any> & import("@earendil-works/pi-coding-agent").ToolDefinition<any, any, any>;
|
|
15
|
+
export declare const registerCodegraphTool: (pi: ExtensionAPI) => void;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import * as cp from "node:child_process";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
import { defineTool, } from "@earendil-works/pi-coding-agent";
|
|
6
|
+
import { Type } from "typebox";
|
|
7
|
+
const execFile = promisify(cp.execFile);
|
|
8
|
+
// ── Core helper ──────────────────────────────────────────────────────────────
|
|
9
|
+
/**
|
|
10
|
+
* Look up `symbol` in the `.codegraph/` index at `projectRoot` and return a
|
|
11
|
+
* concise, human-readable impact summary.
|
|
12
|
+
*
|
|
13
|
+
* Runs `codegraph impact` and `codegraph callers` in parallel, formats the
|
|
14
|
+
* results into a text block, and never throws — all errors surface as message
|
|
15
|
+
* strings suitable for direct tool output.
|
|
16
|
+
*/
|
|
17
|
+
export const getCodegraphImpact = async (symbol, projectRoot) => {
|
|
18
|
+
if (!fs.existsSync(path.join(projectRoot, ".codegraph", "codegraph.db"))) {
|
|
19
|
+
return ("Codegraph index not found. A `.codegraph/` directory must exist at the " +
|
|
20
|
+
"project root. Run `codegraph init` then `codegraph index` to build it, " +
|
|
21
|
+
"and ensure the `codegraph` CLI is on PATH.");
|
|
22
|
+
}
|
|
23
|
+
const run = async (command) => {
|
|
24
|
+
try {
|
|
25
|
+
const { stdout } = await execFile("codegraph", [command, symbol, "--json"], {
|
|
26
|
+
cwd: projectRoot,
|
|
27
|
+
timeout: 15_000,
|
|
28
|
+
});
|
|
29
|
+
return stdout;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
// Run both queries in parallel for speed.
|
|
36
|
+
const [impactRaw, callersRaw] = await Promise.all([run("impact"), run("callers")]);
|
|
37
|
+
let impactData = null;
|
|
38
|
+
let callersData = null;
|
|
39
|
+
if (impactRaw) {
|
|
40
|
+
try {
|
|
41
|
+
impactData = JSON.parse(impactRaw);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// Malformed JSON — treat as unavailable.
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (callersRaw) {
|
|
48
|
+
try {
|
|
49
|
+
callersData = JSON.parse(callersRaw);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Malformed JSON — treat as unavailable.
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (!impactData && !callersData) {
|
|
56
|
+
return `Codegraph: no data returned for \`${symbol}\`. The symbol may not be in the index, or the \`codegraph\` CLI may be missing from PATH. Try running \`codegraph sync\` at the project root.`;
|
|
57
|
+
}
|
|
58
|
+
const lines = [`Codegraph impact for \`${symbol}\`:\n`];
|
|
59
|
+
// ── Direct callers ─────────────────────────────────────────────────────
|
|
60
|
+
if (callersData) {
|
|
61
|
+
const { callers } = callersData;
|
|
62
|
+
if (callers.length === 0) {
|
|
63
|
+
lines.push("Direct callers: none — this symbol may be an entry point or is not called directly.");
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
lines.push(`Direct callers (${callers.length}):`);
|
|
67
|
+
for (const c of callers) {
|
|
68
|
+
lines.push(` • ${c.name} [${c.kind}] ${c.filePath}:${c.startLine}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
lines.push("");
|
|
72
|
+
}
|
|
73
|
+
// ── Full impact set ────────────────────────────────────────────────────
|
|
74
|
+
if (impactData) {
|
|
75
|
+
// Exclude the queried symbol itself; report only downstream dependents.
|
|
76
|
+
const downstream = impactData.affected.filter((a) => a.name !== symbol);
|
|
77
|
+
if (downstream.length === 0) {
|
|
78
|
+
lines.push(`Full impact (depth ${impactData.depth}): no downstream dependents found.`);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
lines.push(`Full impact (depth ${impactData.depth}, ${impactData.nodeCount} nodes, ${impactData.edgeCount} edges):`);
|
|
82
|
+
for (const a of downstream) {
|
|
83
|
+
lines.push(` • ${a.name} [${a.kind}] ${a.filePath}:${a.startLine}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return lines.join("\n");
|
|
88
|
+
};
|
|
89
|
+
// ── Tool definition ──────────────────────────────────────────────────────────
|
|
90
|
+
export const vmCodegraphImpactTool = defineTool({
|
|
91
|
+
name: "vm_codegraph_impact",
|
|
92
|
+
label: "Codegraph Impact",
|
|
93
|
+
description: "Report which code depends on a given symbol using the workspace codegraph index.",
|
|
94
|
+
promptSnippet: 'vm_codegraph_impact(symbol="registerTools")',
|
|
95
|
+
promptGuidelines: [
|
|
96
|
+
"Requires a `.codegraph/` index at the project root (`codegraph init && codegraph index`).",
|
|
97
|
+
"Returns direct callers and the full downstream impact set for the named symbol.",
|
|
98
|
+
"Useful for assessing blast radius before modifying an exported function, class, or type.",
|
|
99
|
+
],
|
|
100
|
+
parameters: Type.Object({
|
|
101
|
+
symbol: Type.String({
|
|
102
|
+
description: "Symbol name to look up (function, class, variable, type, etc.)",
|
|
103
|
+
}),
|
|
104
|
+
}),
|
|
105
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
106
|
+
const summary = await getCodegraphImpact(params.symbol, ctx.cwd);
|
|
107
|
+
return {
|
|
108
|
+
content: [{ type: "text", text: summary }],
|
|
109
|
+
details: {},
|
|
110
|
+
};
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
// ── Registration ─────────────────────────────────────────────────────────────
|
|
114
|
+
export const registerCodegraphTool = (pi) => {
|
|
115
|
+
const existing = new Set(pi.getAllTools().map((t) => t.name));
|
|
116
|
+
if (!existing.has("vm_codegraph_impact"))
|
|
117
|
+
pi.registerTool(vmCodegraphImpactTool);
|
|
118
|
+
};
|
package/dist/src/tools.js
CHANGED
|
@@ -5,6 +5,7 @@ import { registerDiscoverSchemaTool } from "./discover-schema.js";
|
|
|
5
5
|
import { graphUpsert, queryGraph } from "./graph.js";
|
|
6
6
|
import { getStatus, queryCollection, searchFts, searchHybrid, upsertEntry, } from "./lance.js";
|
|
7
7
|
import { registerTombstoneTool } from "./tombstone.js";
|
|
8
|
+
import { registerCodegraphTool } from "./tools/codegraph.js";
|
|
8
9
|
import { registerMarksmanTools } from "./tools/marksman.js";
|
|
9
10
|
import { collectionNames, ensureDir, findConfig, loadConfig } from "./utils.js";
|
|
10
11
|
import { registerVaultTools } from "./vault-tools.js";
|
|
@@ -807,4 +808,5 @@ export const registerTools = (pi) => {
|
|
|
807
808
|
registerTombstoneTool(pi);
|
|
808
809
|
registerVaultTools(pi);
|
|
809
810
|
registerMarksmanTools(pi);
|
|
811
|
+
registerCodegraphTool(pi);
|
|
810
812
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-vault-mind",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
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
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|