@vndv/pi-codegraph 0.1.3 → 0.1.5
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 +4 -4
- package/extensions/codegraph.ts +116 -69
- package/package.json +10 -8
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# pi-codegraph
|
|
2
2
|
### CodeGraph tools for pi
|
|
3
3
|
|
|
4
|
-
Install · Usage · How it works
|
|
4
|
+
[Install](#install) · [Usage](#usage) · [How it works](#how-it-works)
|
|
5
5
|
|
|
6
6
|
Ask pi structural questions about your codebase without falling back to slow grep/read loops.
|
|
7
7
|
|
|
@@ -15,7 +15,7 @@ An extension for [pi](https://pi.dev) that gives the agent access to [CodeGraph]
|
|
|
15
15
|
npm install -g @colbymchenry/codegraph
|
|
16
16
|
cd /path/to/project
|
|
17
17
|
codegraph init -i
|
|
18
|
-
pi install npm:@vndv/pi-codegraph@0.1.
|
|
18
|
+
pi install npm:@vndv/pi-codegraph@0.1.5
|
|
19
19
|
pi
|
|
20
20
|
```
|
|
21
21
|
|
|
@@ -51,7 +51,7 @@ Extension tools only. There is no MCP setup for pi users to maintain.
|
|
|
51
51
|
From npm:
|
|
52
52
|
|
|
53
53
|
```bash
|
|
54
|
-
pi install npm:@vndv/pi-codegraph@0.1.
|
|
54
|
+
pi install npm:@vndv/pi-codegraph@0.1.5
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
From GitHub:
|
|
@@ -160,7 +160,7 @@ That means another developer only needs the npm package, the `codegraph` CLI, an
|
|
|
160
160
|
Remove the package using the same source shown by `pi list`:
|
|
161
161
|
|
|
162
162
|
```bash
|
|
163
|
-
pi remove npm:@vndv/pi-codegraph@0.1.
|
|
163
|
+
pi remove npm:@vndv/pi-codegraph@0.1.5
|
|
164
164
|
```
|
|
165
165
|
|
|
166
166
|
If you installed from GitHub or a local path, remove that exact entry instead:
|
package/extensions/codegraph.ts
CHANGED
|
@@ -135,6 +135,10 @@ const ToolDefinitions = [
|
|
|
135
135
|
type ToolName = (typeof ToolDefinitions)[number]["name"];
|
|
136
136
|
type ToolParams = Record<string, unknown> & { projectPath?: string };
|
|
137
137
|
type JsonRpcRequest = (method: string, params: Record<string, unknown>) => Promise<any>;
|
|
138
|
+
type PendingJsonRpcRequests = Map<number, {
|
|
139
|
+
resolve: (value: any) => void;
|
|
140
|
+
reject: (error: Error) => void;
|
|
141
|
+
}>;
|
|
138
142
|
|
|
139
143
|
const MaxDiagnosticLength = 1000;
|
|
140
144
|
|
|
@@ -194,67 +198,106 @@ async function runJsonRpcSession<T>(
|
|
|
194
198
|
signal: AbortSignal | undefined,
|
|
195
199
|
fn: (request: JsonRpcRequest) => Promise<T>,
|
|
196
200
|
): Promise<T> {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const pending = new Map<number, {
|
|
201
|
-
resolve: (value: any) => void;
|
|
202
|
-
reject: (error: Error) => void;
|
|
203
|
-
}>();
|
|
204
|
-
|
|
205
|
-
const cleanup = () => {
|
|
206
|
-
for (const entry of pending.values()) {
|
|
207
|
-
entry.reject(new Error("CodeGraph MCP process closed before responding."));
|
|
208
|
-
}
|
|
209
|
-
pending.clear();
|
|
210
|
-
if (!child.killed) child.kill();
|
|
211
|
-
};
|
|
212
|
-
|
|
201
|
+
const pending: PendingJsonRpcRequests = new Map();
|
|
202
|
+
const stderr = { value: "" };
|
|
203
|
+
const cleanup = () => cleanupJsonRpcChild(child, pending);
|
|
213
204
|
const onAbort = () => cleanup();
|
|
205
|
+
|
|
214
206
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
207
|
+
attachJsonRpcHandlers(child, pending, stderr);
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
const sendRequest = createJsonRpcRequestSender(child, pending);
|
|
211
|
+
await initializeJsonRpcSession(cwd, sendRequest, sendJsonRpcNotification.bind(undefined, child));
|
|
212
|
+
return await fn(sendRequest);
|
|
213
|
+
} finally {
|
|
214
|
+
signal?.removeEventListener("abort", onAbort);
|
|
215
|
+
cleanup();
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function cleanupJsonRpcChild(
|
|
220
|
+
child: ChildProcessWithoutNullStreams,
|
|
221
|
+
pending: PendingJsonRpcRequests,
|
|
222
|
+
): void {
|
|
223
|
+
rejectPendingJsonRpcRequests(
|
|
224
|
+
pending,
|
|
225
|
+
new Error("CodeGraph MCP process closed before responding."),
|
|
226
|
+
);
|
|
227
|
+
if (!child.killed) child.kill();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function rejectPendingJsonRpcRequests(
|
|
231
|
+
pending: PendingJsonRpcRequests,
|
|
232
|
+
error: Error,
|
|
233
|
+
): void {
|
|
234
|
+
for (const entry of pending.values()) entry.reject(error);
|
|
235
|
+
pending.clear();
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function attachJsonRpcHandlers(
|
|
239
|
+
child: ChildProcessWithoutNullStreams,
|
|
240
|
+
pending: PendingJsonRpcRequests,
|
|
241
|
+
stderr: { value: string },
|
|
242
|
+
): void {
|
|
243
|
+
const stdout = { value: "" };
|
|
215
244
|
|
|
216
245
|
child.stdout.on("data", (chunk) => {
|
|
217
|
-
stdout
|
|
218
|
-
let newline;
|
|
219
|
-
while ((newline = stdout.indexOf("\n")) !== -1) {
|
|
220
|
-
const line = stdout.slice(0, newline).trim();
|
|
221
|
-
stdout = stdout.slice(newline + 1);
|
|
222
|
-
if (!line) continue;
|
|
223
|
-
|
|
224
|
-
let msg: any;
|
|
225
|
-
try {
|
|
226
|
-
msg = JSON.parse(line);
|
|
227
|
-
} catch {
|
|
228
|
-
continue;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (msg.id !== undefined && pending.has(msg.id)) {
|
|
232
|
-
const { resolve, reject } = pending.get(msg.id)!;
|
|
233
|
-
pending.delete(msg.id);
|
|
234
|
-
if (msg.error) reject(new Error(msg.error.message || JSON.stringify(msg.error)));
|
|
235
|
-
else resolve(msg.result);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
246
|
+
handleJsonRpcStdout(chunk, stdout, pending);
|
|
238
247
|
});
|
|
239
|
-
|
|
240
248
|
child.stderr.on("data", (chunk) => {
|
|
241
|
-
stderr += chunk.toString("utf-8");
|
|
249
|
+
stderr.value += chunk.toString("utf-8");
|
|
242
250
|
});
|
|
251
|
+
child.on("error", (err) => rejectPendingJsonRpcRequests(pending, err));
|
|
252
|
+
child.on("exit", (code) => rejectPendingJsonRpcOnExit(pending, stderr.value, code));
|
|
253
|
+
}
|
|
243
254
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
255
|
+
function handleJsonRpcStdout(
|
|
256
|
+
chunk: Buffer,
|
|
257
|
+
stdout: { value: string },
|
|
258
|
+
pending: PendingJsonRpcRequests,
|
|
259
|
+
): void {
|
|
260
|
+
stdout.value += chunk.toString("utf-8");
|
|
261
|
+
let newline;
|
|
262
|
+
while ((newline = stdout.value.indexOf("\n")) !== -1) {
|
|
263
|
+
const line = stdout.value.slice(0, newline).trim();
|
|
264
|
+
stdout.value = stdout.value.slice(newline + 1);
|
|
265
|
+
if (line) resolveJsonRpcLine(line, pending);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
248
268
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
}
|
|
269
|
+
function resolveJsonRpcLine(line: string, pending: PendingJsonRpcRequests): void {
|
|
270
|
+
let msg: any;
|
|
271
|
+
try {
|
|
272
|
+
msg = JSON.parse(line);
|
|
273
|
+
} catch {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (msg.id === undefined || !pending.has(msg.id)) return;
|
|
278
|
+
const { resolve, reject } = pending.get(msg.id)!;
|
|
279
|
+
pending.delete(msg.id);
|
|
280
|
+
if (msg.error) reject(new Error(msg.error.message || JSON.stringify(msg.error)));
|
|
281
|
+
else resolve(msg.result);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function rejectPendingJsonRpcOnExit(
|
|
285
|
+
pending: PendingJsonRpcRequests,
|
|
286
|
+
stderr: string,
|
|
287
|
+
code: number | null,
|
|
288
|
+
): void {
|
|
289
|
+
if (pending.size === 0) return;
|
|
290
|
+
const diagnostic = sanitizeDiagnostic(stderr.trim());
|
|
291
|
+
const msg = diagnostic || `CodeGraph MCP process exited with code ${code}`;
|
|
292
|
+
rejectPendingJsonRpcRequests(pending, new Error(msg));
|
|
293
|
+
}
|
|
256
294
|
|
|
257
|
-
|
|
295
|
+
function createJsonRpcRequestSender(
|
|
296
|
+
child: ChildProcessWithoutNullStreams,
|
|
297
|
+
pending: PendingJsonRpcRequests,
|
|
298
|
+
): JsonRpcRequest {
|
|
299
|
+
let nextId = 1;
|
|
300
|
+
return (method, params) => {
|
|
258
301
|
const id = nextId++;
|
|
259
302
|
const payload = { jsonrpc: "2.0", id, method, params };
|
|
260
303
|
const promise = new Promise<any>((resolve, reject) => {
|
|
@@ -263,26 +306,30 @@ async function runJsonRpcSession<T>(
|
|
|
263
306
|
child.stdin.write(`${JSON.stringify(payload)}\n`);
|
|
264
307
|
return promise;
|
|
265
308
|
};
|
|
309
|
+
}
|
|
266
310
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
311
|
+
function sendJsonRpcNotification(
|
|
312
|
+
child: ChildProcessWithoutNullStreams,
|
|
313
|
+
method: string,
|
|
314
|
+
params: Record<string, unknown>,
|
|
315
|
+
): void {
|
|
316
|
+
child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", method, params })}\n`);
|
|
317
|
+
}
|
|
270
318
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
319
|
+
async function initializeJsonRpcSession(
|
|
320
|
+
cwd: string,
|
|
321
|
+
sendRequest: JsonRpcRequest,
|
|
322
|
+
sendNotification: (method: string, params: Record<string, unknown>) => void,
|
|
323
|
+
): Promise<void> {
|
|
324
|
+
const rootUri = pathToFileURL(cwd).href;
|
|
325
|
+
await sendRequest("initialize", {
|
|
326
|
+
protocolVersion: "2024-11-05",
|
|
327
|
+
rootUri,
|
|
328
|
+
workspaceFolders: [{ uri: rootUri, name: cwd.split(/[\\/]/).pop() || cwd }],
|
|
329
|
+
capabilities: {},
|
|
330
|
+
clientInfo: { name: "pi-codegraph", version: "0.1.0" },
|
|
331
|
+
});
|
|
332
|
+
sendNotification("initialized", {});
|
|
286
333
|
}
|
|
287
334
|
|
|
288
335
|
export async function callCodeGraphTool(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vndv/pi-codegraph",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "CodeGraph tools for Pi Agent.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -35,15 +35,17 @@
|
|
|
35
35
|
]
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
|
-
"
|
|
38
|
+
"check:readme-version": "node scripts/sync-readme-version.mjs --check",
|
|
39
|
+
"ci": "npm run typecheck && npm test && npm run compat:codegraph && npm run check:readme-version && npm pack --dry-run",
|
|
39
40
|
"compat:codegraph": "codegraph --version",
|
|
40
41
|
"local-release": "changeset version && changeset publish",
|
|
41
|
-
"publish-packages": "npm publish --access public --provenance",
|
|
42
|
+
"publish-packages": "node -e \"const {execSync}=require('node:child_process'); const p=require('./package.json'); const spec=p.name+'@'+p.version; try { execSync('npm view '+spec+' version', {stdio:'ignore'}); console.log(spec+' already published; skipping.'); } catch { execSync('npm publish --access public --provenance --ignore-scripts', {stdio:'inherit'}); }\"",
|
|
42
43
|
"prepack": "npm run typecheck && npm test",
|
|
43
44
|
"prepublishOnly": "npm run ci",
|
|
44
45
|
"typecheck": "tsc --noEmit",
|
|
45
46
|
"test": "vitest run",
|
|
46
|
-
"version
|
|
47
|
+
"sync:readme-version": "node scripts/sync-readme-version.mjs",
|
|
48
|
+
"version-packages": "changeset version && npm run sync:readme-version && npm install --package-lock-only --ignore-scripts"
|
|
47
49
|
},
|
|
48
50
|
"engines": {
|
|
49
51
|
"node": ">=22.19.0 <25"
|
|
@@ -58,9 +60,9 @@
|
|
|
58
60
|
"devDependencies": {
|
|
59
61
|
"@changesets/cli": "^2.31.0",
|
|
60
62
|
"@colbymchenry/codegraph": "^0.9.7",
|
|
61
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
62
|
-
"@types/node": "^
|
|
63
|
-
"typescript": "^
|
|
64
|
-
"vitest": "^
|
|
63
|
+
"@earendil-works/pi-coding-agent": "^0.78.0",
|
|
64
|
+
"@types/node": "^25.9.1",
|
|
65
|
+
"typescript": "^6.0.3",
|
|
66
|
+
"vitest": "^4.1.7"
|
|
65
67
|
}
|
|
66
68
|
}
|