pi-readseek 0.7.6 → 0.7.8

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.
Files changed (60) hide show
  1. package/README.md +28 -46
  2. package/dist/index.js +7333 -0
  3. package/package.json +8 -7
  4. package/index.ts +0 -147
  5. package/src/binary-detect.ts +0 -22
  6. package/src/check.ts +0 -92
  7. package/src/coerce-obvious-int.ts +0 -41
  8. package/src/confusable-hyphens.ts +0 -6
  9. package/src/def.ts +0 -143
  10. package/src/diff-data.ts +0 -302
  11. package/src/edit-classify.ts +0 -58
  12. package/src/edit-diff.ts +0 -359
  13. package/src/edit-output.ts +0 -110
  14. package/src/edit-render-helpers.ts +0 -141
  15. package/src/edit-syntax-validate.ts +0 -65
  16. package/src/edit.ts +0 -686
  17. package/src/file-map.ts +0 -17
  18. package/src/fs-error.ts +0 -33
  19. package/src/grep-budget.ts +0 -35
  20. package/src/grep-output.ts +0 -181
  21. package/src/grep-render-helpers.ts +0 -77
  22. package/src/grep-symbol-scope.ts +0 -184
  23. package/src/grep.ts +0 -608
  24. package/src/hashline.ts +0 -736
  25. package/src/hover.ts +0 -140
  26. package/src/path-utils.ts +0 -24
  27. package/src/pending-diff-preview.ts +0 -269
  28. package/src/read-local-bundle.ts +0 -75
  29. package/src/read-output.ts +0 -186
  30. package/src/read-render-helpers.ts +0 -104
  31. package/src/read.ts +0 -661
  32. package/src/readseek/constants.ts +0 -17
  33. package/src/readseek/enums.ts +0 -38
  34. package/src/readseek/formatter.ts +0 -350
  35. package/src/readseek/symbol-error-format.ts +0 -18
  36. package/src/readseek/symbol-lookup.ts +0 -240
  37. package/src/readseek/symbol-tree.ts +0 -21
  38. package/src/readseek/types.ts +0 -49
  39. package/src/readseek-client.ts +0 -1250
  40. package/src/readseek-params.ts +0 -39
  41. package/src/readseek-settings.ts +0 -263
  42. package/src/readseek-value.ts +0 -184
  43. package/src/refs-output.ts +0 -47
  44. package/src/refs.ts +0 -161
  45. package/src/register-tool.ts +0 -29
  46. package/src/rename.ts +0 -182
  47. package/src/replace-symbol.ts +0 -77
  48. package/src/runtime.ts +0 -3
  49. package/src/search-output.ts +0 -50
  50. package/src/search.ts +0 -214
  51. package/src/session-anchors.ts +0 -34
  52. package/src/stat-search-path.ts +0 -29
  53. package/src/syntax-validate-mode.ts +0 -15
  54. package/src/tool-prompt-metadata.ts +0 -107
  55. package/src/tool-types.ts +0 -12
  56. package/src/tui-diff-component.ts +0 -108
  57. package/src/tui-diff-renderer.ts +0 -92
  58. package/src/tui-render-utils.ts +0 -286
  59. package/src/view.ts +0 -158
  60. package/src/write.ts +0 -357
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "pi-readseek",
3
- "version": "0.7.6",
3
+ "version": "0.7.8",
4
4
  "description": "Pi extension for readseek-backed hash-anchored read/edit/grep, structural code maps, structural search, and file exploration",
5
5
  "type": "module",
6
6
  "exports": {
7
- ".": "./index.ts"
7
+ ".": "./dist/index.js"
8
8
  },
9
9
  "keywords": [
10
10
  "pi-package",
@@ -21,25 +21,26 @@
21
21
  "license": "Apache-2.0",
22
22
  "pi": {
23
23
  "extensions": [
24
- "./index.ts"
24
+ "./dist/index.js"
25
25
  ]
26
26
  },
27
27
  "files": [
28
- "index.ts",
29
- "src/",
28
+ "dist/",
30
29
  "prompts/",
31
30
  "LICENSE",
32
31
  "README.md"
33
32
  ],
34
33
  "scripts": {
35
- "test": "node scripts/vitest-run.mjs",
34
+ "build": "bun build index.ts --outfile dist/index.js --target=node --packages=external",
35
+ "prepack": "npm run build",
36
+ "test": "npm run build && node scripts/vitest-run.mjs",
36
37
  "typecheck": "tsc --noEmit"
37
38
  },
38
39
  "engines": {
39
40
  "node": ">=20.0.0"
40
41
  },
41
42
  "dependencies": {
42
- "@jarkkojs/readseek": "^0.7.6",
43
+ "@jarkkojs/readseek": "^0.7.8",
43
44
  "diff": "^8.0.3",
44
45
  "xxhash-wasm": "^1.1.0"
45
46
  },
package/index.ts DELETED
@@ -1,147 +0,0 @@
1
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
- import { registerReadTool } from "./src/read.js";
3
- import { registerEditTool } from "./src/edit.js";
4
- import { registerGrepTool } from "./src/grep.js";
5
- import { registerSearchTool } from "./src/search.js";
6
- import { registerRefsTool } from "./src/refs.js";
7
- import { registerRenameTool } from "./src/rename.js";
8
- import { registerHoverTool } from "./src/hover.js";
9
- import { registerWriteTool } from "./src/write.js";
10
- import { registerDefTool } from "./src/def.js";
11
- import { registerCheckTool } from "./src/check.js";
12
- import { registerViewTool } from "./src/view.js";
13
- import { SessionAnchors } from "./src/session-anchors.js";
14
- import { readSeekBinaryAvailability } from "./src/readseek-client.js";
15
- import { resolveReadSeekJsonSettings, type ReadSeekSettingsWarning } from "./src/readseek-settings.js";
16
-
17
- /** Built-in tool names that pi-readseek can replace with a readSeek-backed implementation. */
18
- const REPLACEABLE_TOOLS = {
19
- read: "readSeek_read",
20
- edit: "readSeek_edit",
21
- write: "readSeek_write",
22
- grep: "readSeek_grep",
23
- } as const;
24
-
25
- type ReplacedBuiltIn = keyof typeof REPLACEABLE_TOOLS;
26
-
27
- /**
28
- * Canonical list of readSeek tools in registration/activation order. Each
29
- * replaceable entry carries the built-in name it swaps in when replaced; null
30
- * entries are readSeek-only and never replaced.
31
- */
32
- const READSEEK_TOOL_ENTRIES: ReadonlyArray<{ builtIn: ReplacedBuiltIn | null; readSeekName: string }> = [
33
- { builtIn: "read", readSeekName: "readSeek_read" },
34
- { builtIn: "edit", readSeekName: "readSeek_edit" },
35
- { builtIn: "grep", readSeekName: "readSeek_grep" },
36
- { builtIn: null, readSeekName: "readSeek_search" },
37
- { builtIn: null, readSeekName: "readSeek_refs" },
38
- { builtIn: null, readSeekName: "readSeek_rename" },
39
- { builtIn: null, readSeekName: "readSeek_hover" },
40
- { builtIn: "write", readSeekName: "readSeek_write" },
41
- { builtIn: null, readSeekName: "readSeek_def" },
42
- { builtIn: null, readSeekName: "readSeek_check" },
43
- { builtIn: null, readSeekName: "readSeek_view" },
44
- ];
45
-
46
- function formatSettingsWarning(warning: ReadSeekSettingsWarning): string {
47
- return `${warning.message} (${warning.source})`;
48
- }
49
-
50
- function editingPolicy(readName: string, editName: string, writeName: string): string {
51
- return [
52
- "ReadSeek editing policy:",
53
- `- Prefer ${readName} when preparing to edit existing text; its LINE:HASH anchors are required by ${editName}.`,
54
- `- Prefer ${editName} for existing text files, ${writeName} for whole-file creation or replacement, and readSeek_rename for symbol renames.`,
55
- "- Do not use Pi's built-in edit or write when the corresponding ReadSeek tool is available.",
56
- "- Use readSeek_check after source edits for a quick syntax check.",
57
- ].join("\n");
58
- }
59
-
60
- export default function piReadSeekExtension(pi: ExtensionAPI): void {
61
- const sessionAnchors = new SessionAnchors();
62
- const markAnchored = (absolutePath: string) => sessionAnchors.markAnchored(absolutePath);
63
- const hasFreshAnchors = (absolutePath: string) => sessionAnchors.hasFreshAnchors(absolutePath);
64
- const forgetAnchors = (absolutePath: string) => sessionAnchors.forget(absolutePath);
65
-
66
- // Resolve replacedTools at load time so the readSeek implementation is
67
- // registered under the built-in name (e.g. "edit") when replaced. Extension
68
- // tool registrations override built-ins of the same name in pi's tool
69
- // definition registry, so registering under "edit" makes calls to `edit`
70
- // dispatch to the readSeek implementation.
71
- const { settings } = resolveReadSeekJsonSettings();
72
- const replacedBuiltIns = new Set<ReplacedBuiltIn>(
73
- (settings.replacedTools ?? []) as ReplacedBuiltIn[],
74
- );
75
- // Only swap registration under the built-in name when the readSeek binary
76
- // is available; otherwise keep readSeek_* names so pi's built-ins stay
77
- // intact and usable.
78
- const binaryAvailable = readSeekBinaryAvailability().available;
79
- const swap = (builtIn: ReplacedBuiltIn, readSeekName: string) =>
80
- binaryAvailable && replacedBuiltIns.has(builtIn) ? builtIn : readSeekName;
81
-
82
- const readName = swap("read", "readSeek_read");
83
- const editName = swap("edit", "readSeek_edit");
84
- const writeName = swap("write", "readSeek_write");
85
- const grepName = swap("grep", "readSeek_grep");
86
- const toolAliases = {
87
- readSeek_read: readName,
88
- readSeek_edit: editName,
89
- readSeek_grep: grepName,
90
- readSeek_write: writeName,
91
- };
92
-
93
- registerReadTool(pi, { onSuccessfulRead: markAnchored, name: readName });
94
- registerEditTool(pi, { wasReadInSession: hasFreshAnchors, onFileMutated: forgetAnchors, name: editName, toolAliases });
95
- registerGrepTool(pi, { onFileAnchored: markAnchored, name: grepName });
96
- registerSearchTool(pi, { onFileAnchored: markAnchored });
97
- registerRefsTool(pi, { onFileAnchored: markAnchored });
98
- registerRenameTool(pi, { onFileMutated: forgetAnchors });
99
- registerHoverTool(pi);
100
- registerDefTool(pi, { onFileAnchored: markAnchored });
101
- registerCheckTool(pi);
102
- registerViewTool(pi);
103
- registerWriteTool(pi, { onFileAnchored: markAnchored, name: writeName });
104
-
105
- pi.on("before_agent_start", (event) => {
106
- if (!binaryAvailable) return;
107
- return { systemPrompt: `${event.systemPrompt}\n\n${editingPolicy(readName, editName, writeName)}` };
108
- });
109
-
110
- pi.on("session_start", (_event, ctx) => {
111
- sessionAnchors.clear();
112
- const { warnings } = resolveReadSeekJsonSettings();
113
- const problems = warnings.map(formatSettingsWarning);
114
-
115
- const availability = readSeekBinaryAvailability();
116
- if (!availability.available) {
117
- problems.push(`readseek tools are inactive: ${availability.reason}`);
118
- }
119
-
120
- for (const problem of problems) {
121
- if (ctx.hasUI) ctx.ui.notify(problem, "warning");
122
- else console.warn(problem);
123
- }
124
-
125
- if (!availability.available) return;
126
-
127
- // readSeek tools were registered at load under names chosen from the
128
- // load-time replacedTools. Replaced built-ins are registered under the
129
- // built-in name, so the built-in stays active (now readSeek-backed);
130
- // their readSeek_* variant was never registered and is excluded.
131
- const activeReadSeekNames = READSEEK_TOOL_ENTRIES.map((entry) =>
132
- entry.builtIn && replacedBuiltIns.has(entry.builtIn) ? entry.builtIn : entry.readSeekName,
133
- );
134
- const inactiveReadSeekNames = new Set(
135
- READSEEK_TOOL_ENTRIES.filter(
136
- (entry): entry is { builtIn: ReplacedBuiltIn; readSeekName: string } =>
137
- entry.builtIn !== null && replacedBuiltIns.has(entry.builtIn),
138
- ).map((entry) => entry.readSeekName),
139
- );
140
-
141
- const activeTools = [...pi.getActiveTools(), ...activeReadSeekNames].filter(
142
- (name) => !inactiveReadSeekNames.has(name),
143
- );
144
- pi.setActiveTools([...new Set(activeTools)]);
145
- });
146
-
147
- }
@@ -1,22 +0,0 @@
1
- /**
2
- * Returns true if the buffer appears to contain binary (non-text) content.
3
- *
4
- * Detection strategy:
5
- * 1. NUL byte (0x00) — universally indicates binary
6
- * 2. Invalid UTF-8 sequences — Node.js decodes them as U+FFFD replacement
7
- * characters. To avoid false-positives on valid UTF-8 text that
8
- * intentionally contains U+FFFD (encoded as EF BF BD), we do a round-trip
9
- * check: if re-encoding the decoded string produces a different byte
10
- * sequence, then U+FFFD was introduced by the decoder — not present in
11
- * the original bytes as valid UTF-8.
12
- */
13
- export function looksLikeBinary(buf: Buffer): boolean {
14
- if (buf.length === 0) return false;
15
- // Fast path: NUL byte is a reliable binary marker
16
- if (buf.includes(0)) return true;
17
- const decoded = buf.toString("utf8");
18
- if (!decoded.includes("\uFFFD")) return false;
19
-
20
- // If U+FFFD came from invalid byte sequences, re-encoding will differ.
21
- return !Buffer.from(decoded, "utf8").equals(buf);
22
- }
package/src/check.ts DELETED
@@ -1,92 +0,0 @@
1
- import { readFile } from "node:fs/promises";
2
-
3
- import type { ExtensionAPI, ToolRenderResultOptions } from "@earendil-works/pi-coding-agent";
4
- import { Text } from "@earendil-works/pi-tui";
5
- import { Type } from "@sinclair/typebox";
6
-
7
- import { classifyReadSeekFailure, readSeekCheck, type ReadSeekCheckOutput } from "./readseek-client.js";
8
- import { buildToolErrorResult } from "./readseek-value.js";
9
- import { defineToolPromptMetadata } from "./tool-prompt-metadata.js";
10
- import { formatFsError } from "./fs-error.js";
11
- import { resolveToCwd } from "./path-utils.js";
12
- import { filePathParam, registerReadSeekTool } from "./register-tool.js";
13
- import { clampLineToWidth, clampLinesToWidth, linkToolPath, renderErrorResult, renderPendingResult, resolveRenderResultContext, summaryLine } from "./tui-render-utils.js";
14
-
15
- const CHECK_PROMPT_METADATA = defineToolPromptMetadata({
16
- promptUrl: new URL("../prompts/check.md", import.meta.url),
17
- promptSnippet: "Check a source file for parser errors and missing syntax",
18
- });
19
-
20
- export async function executeCheck(opts: {
21
- params: unknown;
22
- signal: AbortSignal | undefined;
23
- cwd: string;
24
- }): Promise<any> {
25
- const { params, signal, cwd } = opts;
26
- const p = params as { path: string };
27
- const filePath = resolveToCwd(p.path, cwd);
28
-
29
- let content: string;
30
- try {
31
- content = await readFile(filePath, "utf-8");
32
- } catch (err: any) {
33
- const { code, message } = formatFsError(err, "check-error");
34
- return buildToolErrorResult("check", code, message, { path: p.path });
35
- }
36
-
37
- try {
38
- const output = await readSeekCheck(filePath, content, { signal });
39
- const lines = output.diagnostics.map((diagnostic) =>
40
- `${diagnostic.kind}: lines ${diagnostic.start_line}-${diagnostic.end_line}`,
41
- );
42
- const summary = output.errorCount === 0 && output.missingCount === 0
43
- ? "No syntax errors found."
44
- : `${output.errorCount} error(s), ${output.missingCount} missing syntax node(s)`;
45
- return {
46
- content: [{ type: "text", text: [summary, ...lines].join("\n") }],
47
- details: {
48
- readSeekValue: { tool: "check", ok: true, path: filePath, output },
49
- },
50
- };
51
- } catch (err: any) {
52
- const failure = classifyReadSeekFailure(err);
53
- return buildToolErrorResult("check", failure.code, failure.message, failure.hint ? { hint: failure.hint } : {});
54
- }
55
- }
56
-
57
- export function registerCheckTool(pi: ExtensionAPI) {
58
- return registerReadSeekTool(pi, {
59
- name: "readSeek_check",
60
- label: "Check",
61
- description: CHECK_PROMPT_METADATA.description,
62
- promptSnippet: CHECK_PROMPT_METADATA.promptSnippet,
63
- promptGuidelines: CHECK_PROMPT_METADATA.promptGuidelines,
64
- parameters: Type.Object({ path: filePathParam() }),
65
- async execute(_toolCallId, params, signal, _onUpdate, ctx) {
66
- return executeCheck({ params, signal, cwd: ctx.cwd });
67
- },
68
- renderCall(args: any, theme: any, ...rest: any[]) {
69
- const context = rest[0] ?? {};
70
- const cwd = context.cwd ?? process.cwd();
71
- const displayPath = typeof args?.path === "string" ? args.path : "?";
72
- const text = `${theme.fg("toolTitle", theme.bold("check"))} ${linkToolPath(theme.fg("accent", displayPath), displayPath, cwd)}`;
73
- return new Text(clampLineToWidth(text, context.width), 0, 0);
74
- },
75
- renderResult(result: any, options: ToolRenderResultOptions, theme: any, ...rest: any[]) {
76
- const { isPartial, isError, expanded, width } = resolveRenderResultContext(options, rest);
77
- if (isPartial) return renderPendingResult("pending check", width, theme);
78
- const textContent = result.content?.[0]?.type === "text" ? result.content[0].text : "";
79
- if (isError || result.isError) {
80
- return renderErrorResult(textContent, { expanded, width, fallback: "check failed", theme });
81
- }
82
- const output = (result.details as any)?.readSeekValue?.output as ReadSeekCheckOutput | undefined;
83
- const issueCount = (output?.errorCount ?? 0) + (output?.missingCount ?? 0);
84
- let text = summaryLine(issueCount === 0 ? "syntax valid" : `${issueCount} syntax issue(s)`, {
85
- theme,
86
- style: issueCount === 0 ? "success" : "warning",
87
- });
88
- if (expanded && textContent) text += `\n${textContent}`;
89
- return new Text(clampLinesToWidth(text.split("\n"), width).join("\n"), 0, 0);
90
- },
91
- });
92
- }
@@ -1,41 +0,0 @@
1
- const BASE10_INT_RE = /^-?\d+$/;
2
- const MIN_SAFE_INTEGER = BigInt(Number.MIN_SAFE_INTEGER);
3
- const MAX_SAFE_INTEGER = BigInt(Number.MAX_SAFE_INTEGER);
4
-
5
- type CoercedIntResult =
6
- | { ok: true; value: number | undefined }
7
- | { ok: false; message: string };
8
-
9
- function formatReceived(value: unknown): string {
10
- if (typeof value === "number" || typeof value === "bigint") return String(value);
11
- return JSON.stringify(value) ?? String(value);
12
- }
13
-
14
- function invalidInteger(value: unknown, name: string): CoercedIntResult {
15
- return {
16
- ok: false,
17
- message: `Invalid ${name}: expected a safe base-10 integer, received ${formatReceived(value)}.`,
18
- };
19
- }
20
-
21
- export function coerceObviousBase10Int(value: unknown, name: string): CoercedIntResult {
22
- if (value === undefined) {
23
- return { ok: true, value: undefined };
24
- }
25
-
26
- if (typeof value === "number") {
27
- if (Number.isSafeInteger(value)) {
28
- return { ok: true, value };
29
- }
30
- return invalidInteger(value, name);
31
- }
32
-
33
- if (typeof value === "string" && BASE10_INT_RE.test(value)) {
34
- const parsed = BigInt(value);
35
- if (parsed >= MIN_SAFE_INTEGER && parsed <= MAX_SAFE_INTEGER) {
36
- return { ok: true, value: Number(parsed) };
37
- }
38
- }
39
-
40
- return invalidInteger(value, name);
41
- }
@@ -1,6 +0,0 @@
1
- /**
2
- * Unicode hyphen-like characters normalized to ASCII "-" during text
3
- * comparison so edits with typographic hyphens match their originals.
4
- */
5
- export const CONFUSABLE_HYPHENS_RE =
6
- /[\u2010\u2011\u2012\u2013\u2014\u2015\u2212\uFE63\uFF0D]/g;
package/src/def.ts DELETED
@@ -1,143 +0,0 @@
1
- import type { ExtensionAPI, ToolRenderResultOptions } from "@earendil-works/pi-coding-agent";
2
- import { Type } from "@sinclair/typebox";
3
- import path from "node:path";
4
-
5
- import { defineToolPromptMetadata } from "./tool-prompt-metadata.js";
6
- import { buildReadSeekLineWithHash, buildToolErrorResult } from "./readseek-value.js";
7
- import { resolveToCwd } from "./path-utils.js";
8
- import { statSearchPathOrError } from "./stat-search-path.js";
9
- import { classifyReadSeekFailure, readSeekDef } from "./readseek-client.js";
10
- import { searchPathParam, langParam, readSeekGitSearchParams, validateIgnoredRequiresOthers } from "./readseek-params.js";
11
- import { registerReadSeekTool } from "./register-tool.js";
12
-
13
- import { renderAnchoredFilesResult, renderReadSeekSearchCall } from "./tui-render-utils.js";
14
- import type { FileAnchoredCallback } from "./tool-types.js";
15
-
16
- const DEF_PROMPT_METADATA = defineToolPromptMetadata({
17
- promptUrl: new URL("../prompts/def.md", import.meta.url),
18
- promptSnippet: "Find where a symbol is defined",
19
- });
20
-
21
- type DefParams = {
22
- name?: string;
23
- path?: string;
24
- lang?: string;
25
- cached?: boolean;
26
- others?: boolean;
27
- ignored?: boolean;
28
- };
29
-
30
- interface DefToolOptions {
31
- onFileAnchored?: FileAnchoredCallback;
32
- }
33
-
34
- export interface ExecuteDefOptions {
35
- params: unknown;
36
- signal: AbortSignal | undefined;
37
- cwd: string;
38
- onFileAnchored?: FileAnchoredCallback;
39
- }
40
-
41
- export async function executeDef(opts: ExecuteDefOptions): Promise<any> {
42
- const { params, signal, cwd, onFileAnchored } = opts;
43
- const p = params as DefParams;
44
- const ignoredError = validateIgnoredRequiresOthers("def", p);
45
- if (ignoredError) return ignoredError;
46
-
47
- if (!p.name || !p.name.trim()) {
48
- return buildToolErrorResult("def", "invalid-parameter", "readSeek_def requires 'name'");
49
- }
50
-
51
- const searchPath = resolveToCwd(p.path ?? ".", cwd);
52
-
53
- const statResult = await statSearchPathOrError("def", p.path, searchPath);
54
- if (!statResult.ok) return statResult.error;
55
-
56
- try {
57
- const definitions = await readSeekDef(searchPath, {
58
- name: p.name,
59
- language: p.lang,
60
- cached: p.cached,
61
- others: p.others,
62
- ignored: p.ignored,
63
- signal,
64
- });
65
-
66
- if (definitions.length === 0) {
67
- return {
68
- content: [{ type: "text", text: "no definitions found" }],
69
- details: {
70
- readSeekValue: { tool: "def", ok: true, path: searchPath, definitions: [] },
71
- },
72
- };
73
- }
74
-
75
- const files = new Map<string, { displayPath: string; path: string; lines: ReturnType<typeof buildReadSeekLineWithHash>[] }>();
76
- for (const def of definitions) {
77
- const abs = path.isAbsolute(def.file) ? def.file : path.resolve(cwd, def.file);
78
- let file = files.get(abs);
79
- if (!file) {
80
- file = { displayPath: path.relative(cwd, abs) || abs, path: abs, lines: [] };
81
- files.set(abs, file);
82
- }
83
- file.lines.push(buildReadSeekLineWithHash(def.line, def.line_hash, def.text));
84
- }
85
-
86
- const fileList = [...files.values()];
87
- for (const file of fileList) {
88
- onFileAnchored?.(file.path);
89
- }
90
-
91
- const textParts: string[] = [];
92
- for (const file of fileList) {
93
- textParts.push(file.displayPath);
94
- for (const line of file.lines) {
95
- textParts.push(` ${line.line}:${line.hash} ${line.display}`);
96
- }
97
- }
98
-
99
- return {
100
- content: [{ type: "text", text: textParts.join("\n") }],
101
- details: {
102
- readSeekValue: { tool: "def", ok: true, path: searchPath, definitions },
103
- },
104
- };
105
- } catch (err: any) {
106
- const failure = classifyReadSeekFailure(err);
107
- return buildToolErrorResult("def", failure.code, failure.message, failure.hint ? { hint: failure.hint } : {});
108
- }
109
- }
110
-
111
- export function registerDefTool(pi: ExtensionAPI, options: DefToolOptions = {}) {
112
- registerReadSeekTool(pi, {
113
- name: "readSeek_def",
114
- label: "Definition",
115
- description: DEF_PROMPT_METADATA.description,
116
- promptSnippet: DEF_PROMPT_METADATA.promptSnippet,
117
- promptGuidelines: DEF_PROMPT_METADATA.promptGuidelines,
118
- parameters: Type.Object({
119
- name: Type.String({ description: "Qualified or unqualified symbol name" }),
120
- path: searchPathParam(),
121
- lang: langParam(),
122
- ...readSeekGitSearchParams(),
123
- }),
124
- async execute(_toolCallId, params, signal, _onUpdate, ctx) {
125
- return executeDef({ params, signal, cwd: ctx.cwd, onFileAnchored: options.onFileAnchored });
126
- },
127
- renderCall(args: any, theme: any, ...rest: any[]) {
128
- return renderReadSeekSearchCall(args, theme, rest, {
129
- label: "def",
130
- accent: args.name,
131
- flags: [args.cached && "cached", args.others && "others", args.ignored && "ignored"],
132
- });
133
- },
134
- renderResult(result: any, options: ToolRenderResultOptions, theme: any, ...rest: any[]) {
135
- return renderAnchoredFilesResult(result, options, theme, rest, {
136
- pendingLabel: "pending def",
137
- emptyLabel: "no definitions",
138
- unitSingular: "definition",
139
- unitPlural: "definitions",
140
- });
141
- },
142
- });
143
- }