@bacnh85/pi-serena 0.1.1 → 0.1.3
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 +14 -0
- package/index.ts +41 -14
- package/package.json +1 -1
- package/worker.ts +2 -2
package/README.md
CHANGED
|
@@ -55,6 +55,20 @@ After install or update, restart Pi or run `/reload` in an existing Pi session.
|
|
|
55
55
|
|
|
56
56
|
All tool outputs are truncated to 50KB / 2000 lines to match Pi-friendly output limits. Most tools accept optional `timeout_ms`.
|
|
57
57
|
|
|
58
|
+
### Pattern search
|
|
59
|
+
|
|
60
|
+
Use the Pi-facing `pattern` field with `serena_search_for_pattern`:
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"pattern": "USB_HOST_DEVICE_OBJ|USB_HOST_DEVICE_STATE_ERROR_HOLDING",
|
|
65
|
+
"relative_path": "AmazonFreeRTOS",
|
|
66
|
+
"paths_include_glob": "**/*.h"
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The extension maps `pattern` to Serena's backend `substring_pattern` parameter internally, so users do not need to call the Serena implementation detail directly.
|
|
71
|
+
|
|
58
72
|
## Commands
|
|
59
73
|
|
|
60
74
|
- `/serena-status [project]`
|
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";
|
|
@@ -102,6 +104,10 @@ const searchPatternSchema = Type.Object({
|
|
|
102
104
|
relative_path: Type.Optional(Type.String({ description: "Optional file or directory restriction relative to the Serena project root." })),
|
|
103
105
|
paths_include_glob: Type.Optional(Type.String({ description: "Optional glob for included paths." })),
|
|
104
106
|
paths_exclude_glob: Type.Optional(Type.String({ description: "Optional glob for excluded paths." })),
|
|
107
|
+
context_lines_before: Type.Optional(Type.Number({ description: "Number of context lines before each match." })),
|
|
108
|
+
context_lines_after: Type.Optional(Type.Number({ description: "Number of context lines after each match." })),
|
|
109
|
+
restrict_search_to_code_files: Type.Optional(Type.Boolean({ description: "Restrict search to source/code files when supported by Serena." })),
|
|
110
|
+
multiline: Type.Optional(Type.Boolean({ description: "Treat the pattern as a multiline regular expression when supported by Serena." })),
|
|
105
111
|
max_answer_chars: MAX_CHARS_PARAM,
|
|
106
112
|
});
|
|
107
113
|
|
|
@@ -133,6 +139,15 @@ function stripControlParams(params: Record<string, unknown>): { project: string;
|
|
|
133
139
|
return { project: normalizeProject(project), context: normalizeContext(context), timeoutMs: normalizeTimeoutMs(timeout_ms), params: toolParams };
|
|
134
140
|
}
|
|
135
141
|
|
|
142
|
+
export function normalizeSearchPatternParams(params: Record<string, unknown>): Record<string, unknown> {
|
|
143
|
+
const normalized = { ...params };
|
|
144
|
+
if (normalized.substring_pattern === undefined && normalized.pattern !== undefined) {
|
|
145
|
+
normalized.substring_pattern = normalized.pattern;
|
|
146
|
+
}
|
|
147
|
+
delete normalized.pattern;
|
|
148
|
+
return normalized;
|
|
149
|
+
}
|
|
150
|
+
|
|
136
151
|
function truncateText(text: string): string {
|
|
137
152
|
const lines = text.split("\n");
|
|
138
153
|
let output = lines.slice(0, OUTPUT_MAX_LINES).join("\n");
|
|
@@ -214,15 +229,27 @@ export default function serenaToolsExtension(pi: ExtensionAPI) {
|
|
|
214
229
|
return worker;
|
|
215
230
|
};
|
|
216
231
|
|
|
217
|
-
const callSerena = async (ctx: any, tool: string, rawParams: Record<string, unknown
|
|
232
|
+
const callSerena = async (ctx: any, tool: string, rawParams: Record<string, unknown>, lockPath?: string) => {
|
|
218
233
|
const { project, context, timeoutMs, params } = stripControlParams(rawParams);
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
234
|
+
const run = async () => {
|
|
235
|
+
const response = await getWorker(ctx).request({ action: "call", project, context, tool, params }, timeoutMs);
|
|
236
|
+
return {
|
|
237
|
+
content: [{ type: "text" as const, text: resultText(response) }],
|
|
238
|
+
details: response,
|
|
239
|
+
};
|
|
223
240
|
};
|
|
241
|
+
return lockPath ? withFileMutationQueue(lockPath, run) : run();
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const lockPathForRelativeFile = (rawParams: Record<string, unknown>): string | undefined => {
|
|
245
|
+
const project = normalizeProject(rawParams.project);
|
|
246
|
+
return typeof rawParams.relative_path === "string" && rawParams.relative_path.trim()
|
|
247
|
+
? path.resolve(project, rawParams.relative_path)
|
|
248
|
+
: undefined;
|
|
224
249
|
};
|
|
225
250
|
|
|
251
|
+
const lockPathForProject = (rawParams: Record<string, unknown>): string => path.resolve(normalizeProject(rawParams.project));
|
|
252
|
+
|
|
226
253
|
pi.registerTool({
|
|
227
254
|
name: "serena_status",
|
|
228
255
|
label: "Serena Status",
|
|
@@ -299,7 +326,7 @@ export default function serenaToolsExtension(pi: ExtensionAPI) {
|
|
|
299
326
|
promptGuidelines: ["Use serena_replace_symbol_body only after serena_find_symbol identifies the exact symbol to replace."],
|
|
300
327
|
parameters: replaceBodySchema,
|
|
301
328
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
302
|
-
return callSerena(ctx, "replace_symbol_body", params);
|
|
329
|
+
return callSerena(ctx, "replace_symbol_body", params, lockPathForRelativeFile(params));
|
|
303
330
|
},
|
|
304
331
|
});
|
|
305
332
|
|
|
@@ -311,7 +338,7 @@ export default function serenaToolsExtension(pi: ExtensionAPI) {
|
|
|
311
338
|
promptGuidelines: ["Use serena_insert_before_symbol for symbol-adjacent code insertion after locating the target symbol."],
|
|
312
339
|
parameters: insertSchema,
|
|
313
340
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
314
|
-
return callSerena(ctx, "insert_before_symbol", params);
|
|
341
|
+
return callSerena(ctx, "insert_before_symbol", params, lockPathForRelativeFile(params));
|
|
315
342
|
},
|
|
316
343
|
});
|
|
317
344
|
|
|
@@ -323,7 +350,7 @@ export default function serenaToolsExtension(pi: ExtensionAPI) {
|
|
|
323
350
|
promptGuidelines: ["Use serena_insert_after_symbol for adding sibling/helper symbols after a located symbol."],
|
|
324
351
|
parameters: insertSchema,
|
|
325
352
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
326
|
-
return callSerena(ctx, "insert_after_symbol", params);
|
|
353
|
+
return callSerena(ctx, "insert_after_symbol", params, lockPathForRelativeFile(params));
|
|
327
354
|
},
|
|
328
355
|
});
|
|
329
356
|
|
|
@@ -335,7 +362,7 @@ export default function serenaToolsExtension(pi: ExtensionAPI) {
|
|
|
335
362
|
promptGuidelines: ["Use serena_rename_symbol for cross-file code renames after finding the exact symbol and references."],
|
|
336
363
|
parameters: renameSchema,
|
|
337
364
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
338
|
-
return callSerena(ctx, "rename_symbol", params);
|
|
365
|
+
return callSerena(ctx, "rename_symbol", params, lockPathForProject(params));
|
|
339
366
|
},
|
|
340
367
|
});
|
|
341
368
|
|
|
@@ -347,7 +374,7 @@ export default function serenaToolsExtension(pi: ExtensionAPI) {
|
|
|
347
374
|
promptGuidelines: ["Use serena_safe_delete_symbol instead of text deletion when remaining references matter."],
|
|
348
375
|
parameters: safeDeleteSchema,
|
|
349
376
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
350
|
-
return callSerena(ctx, "safe_delete_symbol", params);
|
|
377
|
+
return callSerena(ctx, "safe_delete_symbol", params, lockPathForRelativeFile(params));
|
|
351
378
|
},
|
|
352
379
|
});
|
|
353
380
|
|
|
@@ -359,7 +386,7 @@ export default function serenaToolsExtension(pi: ExtensionAPI) {
|
|
|
359
386
|
promptGuidelines: ["Use serena_search_for_pattern for project-scoped code searches when symbol lookup is not enough."],
|
|
360
387
|
parameters: searchPatternSchema,
|
|
361
388
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
362
|
-
return callSerena(ctx, "search_for_pattern", params);
|
|
389
|
+
return callSerena(ctx, "search_for_pattern", normalizeSearchPatternParams(params));
|
|
363
390
|
},
|
|
364
391
|
});
|
|
365
392
|
|
|
@@ -371,7 +398,7 @@ export default function serenaToolsExtension(pi: ExtensionAPI) {
|
|
|
371
398
|
promptGuidelines: ["Prefer symbol-aware Serena edit tools for whole symbols; use serena_replace_content for non-symbol scoped replacements supported by Serena."],
|
|
372
399
|
parameters: replaceContentSchema,
|
|
373
400
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
374
|
-
return callSerena(ctx, "replace_content", params);
|
|
401
|
+
return callSerena(ctx, "replace_content", params, lockPathForRelativeFile(params));
|
|
375
402
|
},
|
|
376
403
|
});
|
|
377
404
|
|
|
@@ -455,7 +482,7 @@ export default function serenaToolsExtension(pi: ExtensionAPI) {
|
|
|
455
482
|
promptGuidelines: ["Use serena_write_memory after onboarding or when durable verified project knowledge should be available to Serena."],
|
|
456
483
|
parameters: writeMemorySchema,
|
|
457
484
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
458
|
-
return callSerena(ctx, "write_memory", params);
|
|
485
|
+
return callSerena(ctx, "write_memory", params, lockPathForProject(params));
|
|
459
486
|
},
|
|
460
487
|
});
|
|
461
488
|
|
|
@@ -467,7 +494,7 @@ export default function serenaToolsExtension(pi: ExtensionAPI) {
|
|
|
467
494
|
promptGuidelines: ["Use serena_delete_memory only when the user explicitly asks to remove a Serena memory."],
|
|
468
495
|
parameters: memoryNameSchema,
|
|
469
496
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
470
|
-
return callSerena(ctx, "delete_memory", params);
|
|
497
|
+
return callSerena(ctx, "delete_memory", params, lockPathForProject(params));
|
|
471
498
|
},
|
|
472
499
|
});
|
|
473
500
|
|
package/package.json
CHANGED
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/)) {
|