@bacnh85/pi-serena 0.1.0 → 0.1.2
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 +8 -0
- package/index.ts +27 -13
- package/package.json +2 -2
- package/worker.ts +2 -2
package/README.md
CHANGED
|
@@ -6,6 +6,12 @@ Note: Serena itself is a Python package. The TypeScript worker owns lifecycle, r
|
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
|
+
Install the published package from npm:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pi install npm:@bacnh85/pi-serena
|
|
13
|
+
```
|
|
14
|
+
|
|
9
15
|
From this repository checkout, install only this extension package:
|
|
10
16
|
|
|
11
17
|
```bash
|
|
@@ -18,6 +24,8 @@ For local development from a checkout:
|
|
|
18
24
|
pi -e ./extensions/pi-serena
|
|
19
25
|
```
|
|
20
26
|
|
|
27
|
+
The package manifest points Pi directly at `./index.ts`, so published npm installs and local installs load the same extension entrypoint.
|
|
28
|
+
|
|
21
29
|
There is intentionally no repository-level Pi package. Install each extension from its own subdirectory, matching `extensions/pi-rtk` and future extensions.
|
|
22
30
|
|
|
23
31
|
After install or update, restart Pi or run `/reload` in an existing Pi session.
|
package/index.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { Type } from "typebox";
|
|
3
|
+
import { withFileMutationQueue } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import path from "node:path";
|
|
3
5
|
import { SerenaWorkerClient, type SerenaWorkerResponse } from "./worker";
|
|
4
6
|
|
|
5
7
|
const DEFAULT_CONTEXT = "ide";
|
|
@@ -214,15 +216,27 @@ export default function serenaToolsExtension(pi: ExtensionAPI) {
|
|
|
214
216
|
return worker;
|
|
215
217
|
};
|
|
216
218
|
|
|
217
|
-
const callSerena = async (ctx: any, tool: string, rawParams: Record<string, unknown
|
|
219
|
+
const callSerena = async (ctx: any, tool: string, rawParams: Record<string, unknown>, lockPath?: string) => {
|
|
218
220
|
const { project, context, timeoutMs, params } = stripControlParams(rawParams);
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
221
|
+
const run = async () => {
|
|
222
|
+
const response = await getWorker(ctx).request({ action: "call", project, context, tool, params }, timeoutMs);
|
|
223
|
+
return {
|
|
224
|
+
content: [{ type: "text" as const, text: resultText(response) }],
|
|
225
|
+
details: response,
|
|
226
|
+
};
|
|
223
227
|
};
|
|
228
|
+
return lockPath ? withFileMutationQueue(lockPath, run) : run();
|
|
224
229
|
};
|
|
225
230
|
|
|
231
|
+
const lockPathForRelativeFile = (rawParams: Record<string, unknown>): string | undefined => {
|
|
232
|
+
const project = normalizeProject(rawParams.project);
|
|
233
|
+
return typeof rawParams.relative_path === "string" && rawParams.relative_path.trim()
|
|
234
|
+
? path.resolve(project, rawParams.relative_path)
|
|
235
|
+
: undefined;
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const lockPathForProject = (rawParams: Record<string, unknown>): string => path.resolve(normalizeProject(rawParams.project));
|
|
239
|
+
|
|
226
240
|
pi.registerTool({
|
|
227
241
|
name: "serena_status",
|
|
228
242
|
label: "Serena Status",
|
|
@@ -299,7 +313,7 @@ export default function serenaToolsExtension(pi: ExtensionAPI) {
|
|
|
299
313
|
promptGuidelines: ["Use serena_replace_symbol_body only after serena_find_symbol identifies the exact symbol to replace."],
|
|
300
314
|
parameters: replaceBodySchema,
|
|
301
315
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
302
|
-
return callSerena(ctx, "replace_symbol_body", params);
|
|
316
|
+
return callSerena(ctx, "replace_symbol_body", params, lockPathForRelativeFile(params));
|
|
303
317
|
},
|
|
304
318
|
});
|
|
305
319
|
|
|
@@ -311,7 +325,7 @@ export default function serenaToolsExtension(pi: ExtensionAPI) {
|
|
|
311
325
|
promptGuidelines: ["Use serena_insert_before_symbol for symbol-adjacent code insertion after locating the target symbol."],
|
|
312
326
|
parameters: insertSchema,
|
|
313
327
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
314
|
-
return callSerena(ctx, "insert_before_symbol", params);
|
|
328
|
+
return callSerena(ctx, "insert_before_symbol", params, lockPathForRelativeFile(params));
|
|
315
329
|
},
|
|
316
330
|
});
|
|
317
331
|
|
|
@@ -323,7 +337,7 @@ export default function serenaToolsExtension(pi: ExtensionAPI) {
|
|
|
323
337
|
promptGuidelines: ["Use serena_insert_after_symbol for adding sibling/helper symbols after a located symbol."],
|
|
324
338
|
parameters: insertSchema,
|
|
325
339
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
326
|
-
return callSerena(ctx, "insert_after_symbol", params);
|
|
340
|
+
return callSerena(ctx, "insert_after_symbol", params, lockPathForRelativeFile(params));
|
|
327
341
|
},
|
|
328
342
|
});
|
|
329
343
|
|
|
@@ -335,7 +349,7 @@ export default function serenaToolsExtension(pi: ExtensionAPI) {
|
|
|
335
349
|
promptGuidelines: ["Use serena_rename_symbol for cross-file code renames after finding the exact symbol and references."],
|
|
336
350
|
parameters: renameSchema,
|
|
337
351
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
338
|
-
return callSerena(ctx, "rename_symbol", params);
|
|
352
|
+
return callSerena(ctx, "rename_symbol", params, lockPathForProject(params));
|
|
339
353
|
},
|
|
340
354
|
});
|
|
341
355
|
|
|
@@ -347,7 +361,7 @@ export default function serenaToolsExtension(pi: ExtensionAPI) {
|
|
|
347
361
|
promptGuidelines: ["Use serena_safe_delete_symbol instead of text deletion when remaining references matter."],
|
|
348
362
|
parameters: safeDeleteSchema,
|
|
349
363
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
350
|
-
return callSerena(ctx, "safe_delete_symbol", params);
|
|
364
|
+
return callSerena(ctx, "safe_delete_symbol", params, lockPathForRelativeFile(params));
|
|
351
365
|
},
|
|
352
366
|
});
|
|
353
367
|
|
|
@@ -371,7 +385,7 @@ export default function serenaToolsExtension(pi: ExtensionAPI) {
|
|
|
371
385
|
promptGuidelines: ["Prefer symbol-aware Serena edit tools for whole symbols; use serena_replace_content for non-symbol scoped replacements supported by Serena."],
|
|
372
386
|
parameters: replaceContentSchema,
|
|
373
387
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
374
|
-
return callSerena(ctx, "replace_content", params);
|
|
388
|
+
return callSerena(ctx, "replace_content", params, lockPathForRelativeFile(params));
|
|
375
389
|
},
|
|
376
390
|
});
|
|
377
391
|
|
|
@@ -455,7 +469,7 @@ export default function serenaToolsExtension(pi: ExtensionAPI) {
|
|
|
455
469
|
promptGuidelines: ["Use serena_write_memory after onboarding or when durable verified project knowledge should be available to Serena."],
|
|
456
470
|
parameters: writeMemorySchema,
|
|
457
471
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
458
|
-
return callSerena(ctx, "write_memory", params);
|
|
472
|
+
return callSerena(ctx, "write_memory", params, lockPathForProject(params));
|
|
459
473
|
},
|
|
460
474
|
});
|
|
461
475
|
|
|
@@ -467,7 +481,7 @@ export default function serenaToolsExtension(pi: ExtensionAPI) {
|
|
|
467
481
|
promptGuidelines: ["Use serena_delete_memory only when the user explicitly asks to remove a Serena memory."],
|
|
468
482
|
parameters: memoryNameSchema,
|
|
469
483
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
470
|
-
return callSerena(ctx, "delete_memory", params);
|
|
484
|
+
return callSerena(ctx, "delete_memory", params, lockPathForProject(params));
|
|
471
485
|
},
|
|
472
486
|
});
|
|
473
487
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bacnh85/pi-serena",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Pi extension that provides Serena semantic code tools through a persistent worker.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
],
|
|
29
29
|
"pi": {
|
|
30
30
|
"extensions": [
|
|
31
|
-
"."
|
|
31
|
+
"./index.ts"
|
|
32
32
|
]
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
package/worker.ts
CHANGED
|
@@ -21,8 +21,8 @@ function piConfigDirs(): string[] {
|
|
|
21
21
|
return process.env.PI_CODING_AGENT_DIR ? [process.env.PI_CODING_AGENT_DIR] : [path.join(os.homedir(), ".pi", "agent"), path.join(os.homedir(), ".pi", "agents")];
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
function loadEnv(
|
|
25
|
-
for (const file of
|
|
24
|
+
function loadEnv(): void {
|
|
25
|
+
for (const file of piConfigDirs().flatMap((dir) => [path.join(dir, ".env.local"), path.join(dir, ".env")])) {
|
|
26
26
|
let text: string;
|
|
27
27
|
try { text = fs.readFileSync(file, "utf8"); } catch (error: any) { if (error?.code === "ENOENT") continue; throw error; }
|
|
28
28
|
for (const rawLine of text.split(/\r?\n/)) {
|