@khanhicetea/pi-better-tool 0.2.4 → 0.2.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 +7 -6
- package/package.json +1 -1
- package/src/diagnostics.ts +2 -2
- package/src/read-symbol.ts +19 -12
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Context-aware tools for the [Pi coding agent](https://github.com/earendil-works/pi-mono):
|
|
4
4
|
|
|
5
5
|
- **`edit`** replaces the built-in edit tool. Failures explain what failed, what was not written, and what to do next.
|
|
6
|
-
- **`read_symbol`** reads a whole function, method, class, or type by containing line or exact name.
|
|
6
|
+
- **`read_symbol`** reads a whole function, method, class, or type by containing line or exact name. Its required `mode` makes symbol reads and outline listings explicit.
|
|
7
7
|
- **`read_code_imports`** reads the first dependency import zone as exact raw source lines, including comments and blank lines between adjacent imports.
|
|
8
8
|
- **`read` stays unchanged** for ordinary text, images, and explicit line ranges.
|
|
9
9
|
|
|
@@ -26,25 +26,25 @@ The root `package.json` also registers the extension. After local changes, use `
|
|
|
26
26
|
After grep identifies a location:
|
|
27
27
|
|
|
28
28
|
```json
|
|
29
|
-
{"path":"src/server.ts","line":142}
|
|
29
|
+
{"path":"src/server.ts","mode":"symbol","line":142}
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
Call `read_symbol` with an exact name instead:
|
|
33
33
|
|
|
34
34
|
```json
|
|
35
|
-
{"path":"src/server.ts","symbol":"Server.handleRequest"}
|
|
35
|
+
{"path":"src/server.ts","mode":"symbol","symbol":"Server.handleRequest"}
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
Get the enclosing class or function:
|
|
39
39
|
|
|
40
40
|
```json
|
|
41
|
-
{"path":"src/server.ts","line":142,"parent":1}
|
|
41
|
+
{"path":"src/server.ts","mode":"symbol","line":142,"parent":1}
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
Discover names and ranges without reading every body:
|
|
45
45
|
|
|
46
46
|
```json
|
|
47
|
-
{"path":"src/server.ts"}
|
|
47
|
+
{"path":"src/server.ts","mode":"outline"}
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
### Arguments
|
|
@@ -52,6 +52,7 @@ Discover names and ranges without reading every body:
|
|
|
52
52
|
| Argument | Meaning |
|
|
53
53
|
| --- | --- |
|
|
54
54
|
| `path` | Local relative/absolute path; supports `@path`, `~/path`, and file URLs. |
|
|
55
|
+
| `mode` | Required: `symbol` reads one declaration and requires `symbol` or `line`; `outline` only lists names and ranges. |
|
|
55
56
|
| `line` | 1-based file line. Select the innermost declaration containing it. |
|
|
56
57
|
| `column` | Optional 1-based UTF-16 column with `line`, to distinguish same-line symbols. |
|
|
57
58
|
| `symbol` | Exact, case-sensitive name or qualified name such as `Server.run`. Combine with `line` for duplicate names. |
|
|
@@ -60,7 +61,7 @@ Discover names and ranges without reading every body:
|
|
|
60
61
|
| `offset` | 1-based position **within the selection**, not a file line. For an outline, the entry position. |
|
|
61
62
|
| `limit` | Maximum source lines (default 1000) or outline entries (default 50); maximum 1800. |
|
|
62
63
|
|
|
63
|
-
|
|
64
|
+
Set `mode` to `outline` for an outline. Named selection never silently chooses the first duplicate. Line selection never silently chooses between same-line siblings. Candidate lists include concrete calls with names and positions.
|
|
64
65
|
|
|
65
66
|
### Languages and boundaries
|
|
66
67
|
|
package/package.json
CHANGED
package/src/diagnostics.ts
CHANGED
|
@@ -147,7 +147,7 @@ export function formatEditFailure(opts: FormatFailureOptions): string {
|
|
|
147
147
|
function nextContextCall(path: string, start: number, end = start): string {
|
|
148
148
|
const read = `read ${JSON.stringify({ path, offset: Math.max(1, start - 3), limit: Math.min(1800, end - start + 7) })}`;
|
|
149
149
|
return languageForPath(path)
|
|
150
|
-
? `Next context call: read_symbol ${JSON.stringify({ path, line: start })} for the enclosing symbol; or ${read} for exact line context.`
|
|
150
|
+
? `Next context call: read_symbol ${JSON.stringify({ path, mode: "symbol", line: start })} for the enclosing symbol; or ${read} for exact line context.`
|
|
151
151
|
: `Next context call: ${read}.`;
|
|
152
152
|
}
|
|
153
153
|
|
|
@@ -456,7 +456,7 @@ function formatNotFound(opts: FormatFailureOptions, oldText: string): string {
|
|
|
456
456
|
} else {
|
|
457
457
|
lines.push("No reliable similar region was found within the bounded diagnostic search.");
|
|
458
458
|
lines.push("If you expected this text to exist, read the file around the expected location and retry.");
|
|
459
|
-
lines.push(languageForPath(opts.path) ? `Next context call: read_symbol ${JSON.stringify({ path: opts.path })} to locate the intended symbol without guessing line ranges.` : nextContextCall(opts.path, 1));
|
|
459
|
+
lines.push(languageForPath(opts.path) ? `Next context call: read_symbol ${JSON.stringify({ path: opts.path, mode: "outline" })} to locate the intended symbol without guessing line ranges.` : nextContextCall(opts.path, 1));
|
|
460
460
|
}
|
|
461
461
|
|
|
462
462
|
if (causes.length > 0) {
|
package/src/read-symbol.ts
CHANGED
|
@@ -9,15 +9,18 @@ import { getLineSpans } from "./text.ts";
|
|
|
9
9
|
|
|
10
10
|
export const readSymbolSchema = Type.Object({
|
|
11
11
|
path: Type.String({ minLength: 1, maxLength: 4096, description: "Local source file path (relative, absolute, @path, ~/path, or file URL)." }),
|
|
12
|
-
|
|
12
|
+
mode: Type.Union([Type.Literal("symbol"), Type.Literal("outline")], { description: 'Required operation. Use "symbol" to read one declaration (and provide symbol or line); use "outline" only to list declaration names and ranges.' }),
|
|
13
|
+
line: Type.Optional(Type.Integer({ minimum: 1, description: 'With mode="symbol", read the innermost symbol containing this 1-based line. Combine with symbol to disambiguate duplicate names.' })),
|
|
13
14
|
column: Type.Optional(Type.Integer({ minimum: 1, description: "Optional 1-based UTF-16 column with line, to distinguish symbols on the same line." })),
|
|
14
|
-
symbol: Type.Optional(Type.String({ minLength: 1, maxLength: 256, description: "
|
|
15
|
+
symbol: Type.Optional(Type.String({ minLength: 1, maxLength: 256, description: 'With mode="symbol", the exact symbol name or qualified name, for example Server.run. A symbol or line selector is required in symbol mode.' })),
|
|
15
16
|
parent: Type.Optional(Type.Integer({ minimum: 0, maximum: 20, description: "Move outward this many enclosing symbols after selection (default 0)." })),
|
|
16
|
-
context: Type.Optional(Type.Integer({ minimum: 0, maximum: 20, description: "Extra whole lines before and after the selected symbol (default 0)." })),
|
|
17
|
+
context: Type.Optional(Type.Integer({ minimum: 0, maximum: 20, description: "Extra whole lines before and after the selected symbol (default 0, max 20)." })),
|
|
17
18
|
offset: Type.Optional(Type.Integer({ minimum: 1, description: "1-based position within the selected symbol/context or outline, not a file line. Use the exact continuation call from a partial result." })),
|
|
18
19
|
limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 1800, description: "Maximum source lines (default 1000) or outline entries (default 50). Output also has a 48 KiB hard limit." })),
|
|
19
20
|
});
|
|
20
|
-
|
|
21
|
+
type RegisteredReadSymbolInput = Static<typeof readSymbolSchema>;
|
|
22
|
+
/** mode stays optional here so stored calls from releases before explicit modes remain verifiable. */
|
|
23
|
+
export type ReadSymbolInput = Omit<RegisteredReadSymbolInput, "mode"> & { mode?: RegisteredReadSymbolInput["mode"] };
|
|
21
24
|
|
|
22
25
|
export interface VisibleSource {
|
|
23
26
|
startLine: number;
|
|
@@ -48,12 +51,15 @@ export function validateReadSymbolInput(input: ReadSymbolInput): void {
|
|
|
48
51
|
const value = input[key];
|
|
49
52
|
if (value !== undefined && (!Number.isSafeInteger(value) || value < min || value > max)) throw new Error(`read_symbol ${key} must be an integer from ${min} to ${max}.`);
|
|
50
53
|
}
|
|
54
|
+
if (input.mode !== undefined && input.mode !== "symbol" && input.mode !== "outline") throw new Error('read_symbol mode must be "symbol" or "outline".');
|
|
51
55
|
if (input.column !== undefined && input.line === undefined) throw new Error("read_symbol column requires line.");
|
|
52
|
-
if (
|
|
56
|
+
if (input.mode === "symbol" && input.line === undefined && input.symbol === undefined) throw new Error('read_symbol mode="symbol" requires a symbol name or containing line.');
|
|
57
|
+
if (input.mode === "outline" && (input.line !== undefined || input.symbol !== undefined || input.parent !== undefined || input.context !== undefined)) throw new Error('read_symbol mode="outline" does not accept symbol, line, parent, or context.');
|
|
58
|
+
if ((input.parent || input.context) && input.line === undefined && input.symbol === undefined) throw new Error("read_symbol parent/context requires a line or symbol selector.");
|
|
53
59
|
}
|
|
54
60
|
|
|
55
61
|
function candidateLine(path: string, symbol: SourceSymbol): string {
|
|
56
|
-
return `- ${label(symbol.qualifiedName)} (${symbol.kind}), lines ${symbol.startLine}-${symbol.endLine}. ${callText({ path, line: symbol.startLine, column: symbol.startColumn, symbol: symbol.qualifiedName.length <= 256 ? symbol.qualifiedName : undefined })}`;
|
|
62
|
+
return `- ${label(symbol.qualifiedName)} (${symbol.kind}), lines ${symbol.startLine}-${symbol.endLine}. ${callText({ path, mode: "symbol", line: symbol.startLine, column: symbol.startColumn, symbol: symbol.qualifiedName.length <= 256 ? symbol.qualifiedName : undefined })}`;
|
|
57
63
|
}
|
|
58
64
|
|
|
59
65
|
/** Non-copyable preview for failed selection; never presented as a complete symbol. */
|
|
@@ -99,7 +105,7 @@ function selectSymbol(input: ReadSymbolInput, symbols: SourceSymbol[]): SourceSy
|
|
|
99
105
|
matches.length ? `Ambiguous symbol selection: ${matches.length} candidates. No symbol was selected.` : "No matching symbol. Names are exact and case-sensitive; no nearby symbol was selected automatically.",
|
|
100
106
|
...candidates.slice(0, 8).map((symbol) => candidateLine(input.path, symbol)),
|
|
101
107
|
...(candidates.length > 8 ? [`${candidates.length - 8} more candidates omitted.`] : []),
|
|
102
|
-
`Next: choose a candidate call above, or list the outline with ${callText({ path: input.path })}.`,
|
|
108
|
+
`Next: choose a candidate call above, or list the outline with ${callText({ path: input.path, mode: "outline" })}.`,
|
|
103
109
|
].join("\n"));
|
|
104
110
|
}
|
|
105
111
|
let selected = matches[0];
|
|
@@ -133,7 +139,7 @@ export async function buildSymbolRead(input: ReadSymbolInput, content: string, s
|
|
|
133
139
|
const snapshot = createHash("sha256").update(content).digest("hex");
|
|
134
140
|
const header = `Source ${label(input.path)} (${lines.length} lines). Snapshot sha256:${snapshot}`;
|
|
135
141
|
const offset = input.offset ?? 1;
|
|
136
|
-
if (input.line === undefined && input.symbol === undefined) {
|
|
142
|
+
if (input.mode === "outline" || (input.mode === undefined && input.line === undefined && input.symbol === undefined)) {
|
|
137
143
|
const total = index.symbols.length;
|
|
138
144
|
if (offset > Math.max(1, total)) throw new Error(`Outline offset=${offset} is beyond ${total} entries. Next: ${callText({ ...input, offset: Math.max(1, total - 49) })}`);
|
|
139
145
|
const entries: string[] = [];
|
|
@@ -201,11 +207,12 @@ export async function executeReadSymbol(input: ReadSymbolInput, signal: AbortSig
|
|
|
201
207
|
export function registerReadSymbolTool(pi: ExtensionAPI): void {
|
|
202
208
|
pi.registerTool({
|
|
203
209
|
name: "read_symbol", label: "read_symbol",
|
|
204
|
-
description: `Read a complete function, method, class, or type from a local source file
|
|
205
|
-
promptSnippet: "Read whole source
|
|
210
|
+
description: `Read a complete function, method, class, or type from a local source file. Always set mode: use mode="symbol" with an exact symbol name or containing line; use mode="outline" only to list names and ranges. Supports ${SUPPORTED_LANGUAGES}. Includes enclosing names, exact source ranges, and actionable failure context. Output is bounded to 48 KiB/1950 lines; partial results include an exact continuation call. Does not replace read for ordinary text or images.`,
|
|
211
|
+
promptSnippet: "Read one whole source symbol by line/name; set mode=outline only to list declarations",
|
|
206
212
|
promptGuidelines: [
|
|
207
|
-
"
|
|
208
|
-
"Use read_symbol with symbol
|
|
213
|
+
"Always set read_symbol mode. Use mode=\"symbol\" with a symbol name or containing line to read source; mode=\"outline\" only lists declarations and does not read their bodies.",
|
|
214
|
+
"Use read_symbol with mode=\"symbol\", path, and line after grep/edit identifies a code location, instead of guessing successive read offsets to find the function boundary.",
|
|
215
|
+
"Use read_symbol with mode=\"symbol\" and symbol for an exact name or qualified name. Use parent to include an enclosing function or class.",
|
|
209
216
|
"For read_symbol partial output, use the supplied continuation arguments. offset is relative to the selection, not a file line. Only displayed source is evidence for edit.",
|
|
210
217
|
],
|
|
211
218
|
parameters: readSymbolSchema,
|