pi-readseek 0.4.30 → 0.5.1

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.
@@ -2,30 +2,42 @@ import { readFileSync, statSync, type Stats } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { join } from "node:path";
4
4
 
5
+ export type ReadSeekImageAnalysisMode = "force" | "off" | "auto";
6
+ export type ReadSeekSyntaxValidationMode = "warn" | "block" | "off";
7
+
8
+ interface ReadSeekGrepSettings {
9
+ maxLines?: number;
10
+ maxBytes?: number;
11
+ }
12
+
5
13
  interface ReadSeekJsonSettings {
6
- grep?: { maxLines?: number; maxBytes?: number };
7
- edit?: { diffDisplay?: "collapsed" | "expanded" };
8
- read?: { ocrMode?: "on" | "off" | "auto" };
14
+ excludeTools?: string[];
15
+ imageMode?: ReadSeekImageAnalysisMode;
16
+ syntaxValidation?: ReadSeekSyntaxValidationMode;
17
+ timeoutMs?: number;
18
+ grep?: ReadSeekGrepSettings;
9
19
  }
10
20
 
11
- interface ReadSeekSettingsWarning {
21
+ export interface ReadSeekSettingsWarning {
12
22
  source: string;
13
23
  message: string;
14
24
  path?: string;
15
25
  }
16
26
 
17
- interface ReadSeekSettingsResult {
27
+ export interface ReadSeekSettingsResult {
18
28
  settings: ReadSeekJsonSettings;
19
29
  warnings: ReadSeekSettingsWarning[];
20
30
  }
21
31
 
32
+ const READSEEK_KEYS = ["excludeTools", "imageMode", "syntaxValidation", "timeoutMs", "grep"];
33
+ const READSEEK_GREP_KEYS = ["maxLines", "maxBytes"];
22
34
 
23
- function defaultGlobalSettingsPath(): string {
24
- return join(homedir(), ".pi/agent/readseek/settings.json");
35
+ function globalSettingsPath(): string {
36
+ return join(homedir(), ".pi/agent/settings.json");
25
37
  }
26
38
 
27
- function defaultProjectSettingsPath(): string {
28
- return join(process.cwd(), ".pi/readseek/settings.json");
39
+ function projectSettingsPath(): string {
40
+ return join(process.cwd(), ".pi/settings.json");
29
41
  }
30
42
 
31
43
  function isRecord(value: unknown): value is Record<string, unknown> {
@@ -36,6 +48,20 @@ function invalid(source: string, path: string): ReadSeekSettingsWarning {
36
48
  return { source, path, message: `Invalid readseek setting at ${path}` };
37
49
  }
38
50
 
51
+ function warnUnknownKeys(
52
+ raw: Record<string, unknown>,
53
+ known: string[],
54
+ prefix: string,
55
+ source: string,
56
+ warnings: ReadSeekSettingsWarning[],
57
+ ): void {
58
+ for (const key of Object.keys(raw)) {
59
+ if (known.includes(key)) continue;
60
+ const path = `${prefix}.${key}`;
61
+ warnings.push({ source, path, message: `Unknown readseek setting at ${path}` });
62
+ }
63
+ }
64
+
39
65
  function readPositive(
40
66
  raw: Record<string, unknown>,
41
67
  key: string,
@@ -50,40 +76,111 @@ function readPositive(
50
76
  return undefined;
51
77
  }
52
78
 
79
+ function readEnum<T extends string>(
80
+ raw: Record<string, unknown>,
81
+ key: string,
82
+ values: readonly T[],
83
+ path: string,
84
+ source: string,
85
+ warnings: ReadSeekSettingsWarning[],
86
+ ): T | undefined {
87
+ if (!(key in raw)) return undefined;
88
+ const val = raw[key];
89
+ if (typeof val === "string" && (values as readonly string[]).includes(val)) return val as T;
90
+ warnings.push(invalid(source, path));
91
+ return undefined;
92
+ }
93
+
94
+ function readImageMode(
95
+ raw: Record<string, unknown>,
96
+ source: string,
97
+ warnings: ReadSeekSettingsWarning[],
98
+ ): ReadSeekImageAnalysisMode | undefined {
99
+ if (!("imageMode" in raw)) return undefined;
100
+ const val = raw.imageMode;
101
+ if (val === "on") return "force";
102
+ if (val === "force" || val === "off" || val === "auto") return val;
103
+ warnings.push(invalid(source, "readseek.imageMode"));
104
+ return undefined;
105
+ }
106
+
107
+ function readStringArray(
108
+ raw: Record<string, unknown>,
109
+ key: string,
110
+ path: string,
111
+ source: string,
112
+ warnings: ReadSeekSettingsWarning[],
113
+ ): string[] | undefined {
114
+ if (!(key in raw)) return undefined;
115
+ const value = raw[key];
116
+ if (!Array.isArray(value)) {
117
+ warnings.push(invalid(source, path));
118
+ return undefined;
119
+ }
120
+
121
+ const strings: string[] = [];
122
+ value.forEach((item, index) => {
123
+ if (typeof item !== "string" || item.trim() === "") {
124
+ warnings.push(invalid(source, `${path}[${index}]`));
125
+ return;
126
+ }
127
+ strings.push(item);
128
+ });
129
+ return strings;
130
+ }
53
131
 
54
132
  function validateSettings(raw: unknown, source: string): ReadSeekSettingsResult {
55
133
  const settings: ReadSeekJsonSettings = {};
56
134
  const warnings: ReadSeekSettingsWarning[] = [];
57
- if (!isRecord(raw)) return { settings, warnings };
58
-
59
- if (isRecord(raw.grep)) {
60
- const grep: NonNullable<ReadSeekJsonSettings["grep"]> = {};
61
- const maxLines = readPositive(raw.grep, "maxLines", "grep.maxLines", source, warnings);
62
- if (maxLines !== undefined) grep.maxLines = maxLines;
63
- const maxBytes = readPositive(raw.grep, "maxBytes", "grep.maxBytes", source, warnings);
64
- if (maxBytes !== undefined) grep.maxBytes = maxBytes;
65
- if (Object.keys(grep).length > 0) settings.grep = grep;
135
+ if (!isRecord(raw)) {
136
+ warnings.push({ source, message: "Invalid readseek settings: expected a JSON object" });
137
+ return { settings, warnings };
66
138
  }
67
-
68
-
69
- if (isRecord(raw.edit)) {
70
- const edit: NonNullable<ReadSeekJsonSettings["edit"]> = {};
71
- if ("diffDisplay" in raw.edit) {
72
- const value = raw.edit.diffDisplay;
73
- if (value === "collapsed" || value === "expanded") edit.diffDisplay = value;
74
- else warnings.push(invalid(source, "edit.diffDisplay"));
139
+ if (!("readseek" in raw)) {
140
+ if (Object.keys(raw).length > 0) {
141
+ warnings.push({ source, path: "readseek", message: "Missing readseek section: every setting belongs under \"readseek\"" });
75
142
  }
76
- if (Object.keys(edit).length > 0) settings.edit = edit;
143
+ return { settings, warnings };
144
+ }
145
+ if (!isRecord(raw.readseek)) {
146
+ warnings.push(invalid(source, "readseek"));
147
+ return { settings, warnings };
77
148
  }
78
149
 
79
- if (isRecord(raw.read)) {
80
- const read: NonNullable<ReadSeekJsonSettings["read"]> = {};
81
- if ("ocrMode" in raw.read) {
82
- const value = raw.read.ocrMode;
83
- if (value === "on" || value === "off" || value === "auto") read.ocrMode = value;
84
- else warnings.push(invalid(source, "read.ocrMode"));
150
+ const section = raw.readseek;
151
+ warnUnknownKeys(section, READSEEK_KEYS, "readseek", source, warnings);
152
+
153
+ const excludeTools = readStringArray(section, "excludeTools", "readseek.excludeTools", source, warnings);
154
+ if (excludeTools !== undefined) settings.excludeTools = excludeTools;
155
+
156
+ const imageMode = readImageMode(section, source, warnings);
157
+ if (imageMode !== undefined) settings.imageMode = imageMode;
158
+
159
+ const syntaxValidation = readEnum(
160
+ section,
161
+ "syntaxValidation",
162
+ ["warn", "block", "off"] as const,
163
+ "readseek.syntaxValidation",
164
+ source,
165
+ warnings,
166
+ );
167
+ if (syntaxValidation !== undefined) settings.syntaxValidation = syntaxValidation;
168
+
169
+ const timeoutMs = readPositive(section, "timeoutMs", "readseek.timeoutMs", source, warnings);
170
+ if (timeoutMs !== undefined) settings.timeoutMs = timeoutMs;
171
+
172
+ if ("grep" in section) {
173
+ if (isRecord(section.grep)) {
174
+ warnUnknownKeys(section.grep, READSEEK_GREP_KEYS, "readseek.grep", source, warnings);
175
+ const grep: ReadSeekGrepSettings = {};
176
+ const maxLines = readPositive(section.grep, "maxLines", "readseek.grep.maxLines", source, warnings);
177
+ if (maxLines !== undefined) grep.maxLines = maxLines;
178
+ const maxBytes = readPositive(section.grep, "maxBytes", "readseek.grep.maxBytes", source, warnings);
179
+ if (maxBytes !== undefined) grep.maxBytes = maxBytes;
180
+ if (Object.keys(grep).length > 0) settings.grep = grep;
181
+ } else {
182
+ warnings.push(invalid(source, "readseek.grep"));
85
183
  }
86
- if (Object.keys(read).length > 0) settings.read = read;
87
184
  }
88
185
 
89
186
  return { settings, warnings };
@@ -128,43 +225,29 @@ function readSettingsFile(path: string): ReadSeekSettingsResult {
128
225
  }
129
226
 
130
227
  function mergeSettings(base: ReadSeekJsonSettings, override: ReadSeekJsonSettings): ReadSeekJsonSettings {
131
- const merged: ReadSeekJsonSettings = {};
228
+ const settings: ReadSeekJsonSettings = { ...base, ...override };
132
229
  const grep = { ...(base.grep ?? {}), ...(override.grep ?? {}) };
133
- if (Object.keys(grep).length > 0) merged.grep = grep;
134
- const edit = { ...(base.edit ?? {}), ...(override.edit ?? {}) };
135
- if (Object.keys(edit).length > 0) merged.edit = edit;
136
- const read = { ...(base.read ?? {}), ...(override.read ?? {}) };
137
- if (Object.keys(read).length > 0) merged.read = read;
138
- return merged;
230
+ if (Object.keys(grep).length > 0) settings.grep = grep;
231
+ return settings;
139
232
  }
140
233
 
141
234
  export function resolveReadSeekJsonSettings(): ReadSeekSettingsResult {
142
- const globalResult = readSettingsFile(defaultGlobalSettingsPath());
143
- const projectResult = readSettingsFile(defaultProjectSettingsPath());
235
+ const globalResult = readSettingsFile(globalSettingsPath());
236
+ const projectResult = readSettingsFile(projectSettingsPath());
144
237
  return {
145
238
  settings: mergeSettings(globalResult.settings, projectResult.settings),
146
239
  warnings: [...globalResult.warnings, ...projectResult.warnings],
147
240
  };
148
241
  }
149
242
 
150
- export function resolveEditDiffDisplay(env: NodeJS.ProcessEnv = process.env): "collapsed" | "expanded" {
151
- const raw = env.READSEEK_EDIT_DIFF_DISPLAY;
152
- if (typeof raw === "string") {
153
- const normalized = raw.trim().toLowerCase();
154
- if (normalized === "expanded" || normalized === "collapsed") return normalized;
155
- }
156
- const json = resolveReadSeekJsonSettings().settings.edit?.diffDisplay;
157
- if (json === "expanded" || json === "collapsed") return json;
158
- return "collapsed";
243
+ export function resolveReadSeekImageMode(): ReadSeekImageAnalysisMode {
244
+ return resolveReadSeekJsonSettings().settings.imageMode ?? "force";
159
245
  }
160
246
 
161
- export function resolveReadSeekOcrMode(env: NodeJS.ProcessEnv = process.env): "on" | "off" | "auto" {
162
- const raw = env.READSEEK_READ_OCR_MODE;
163
- if (typeof raw === "string") {
164
- const normalized = raw.trim().toLowerCase();
165
- if (normalized === "on" || normalized === "off" || normalized === "auto") return normalized;
166
- }
167
- const json = resolveReadSeekJsonSettings().settings.read?.ocrMode;
168
- if (json === "on" || json === "off" || json === "auto") return json;
169
- return "on";
247
+ export function resolveReadSeekSyntaxValidation(): ReadSeekSyntaxValidationMode | undefined {
248
+ return resolveReadSeekJsonSettings().settings.syntaxValidation;
249
+ }
250
+
251
+ export function resolveReadSeekTimeoutMs(): number | undefined {
252
+ return resolveReadSeekJsonSettings().settings.timeoutMs;
170
253
  }
@@ -129,16 +129,16 @@ export function buildReadSeekError(
129
129
  export interface ToolErrorResult {
130
130
  content: [{ type: "text"; text: string }];
131
131
  isError: true;
132
- details: { readseekValue: Record<string, unknown> };
132
+ details: { readSeekValue: Record<string, unknown> };
133
133
  }
134
134
 
135
135
  /**
136
136
  * Build the standard failure envelope shared by every read-family tool: a text
137
- * content block plus a `readseekValue` carrying `ok: false` and a
137
+ * content block plus a `readSeekValue` carrying `ok: false` and a
138
138
  * {@link buildReadSeekError} payload.
139
139
  *
140
140
  * `path` is included only when provided, and `extra` is merged into
141
- * `readseekValue` so callers can attach tool-specific fields (e.g. write's
141
+ * `readSeekValue` so callers can attach tool-specific fields (e.g. write's
142
142
  * `lines`/`warnings`).
143
143
  */
144
144
  export function buildToolErrorResult(
@@ -151,7 +151,7 @@ export function buildToolErrorResult(
151
151
  content: [{ type: "text", text: message }],
152
152
  isError: true,
153
153
  details: {
154
- readseekValue: {
154
+ readSeekValue: {
155
155
  tool,
156
156
  ...(opts.extra ?? {}),
157
157
  ok: false,
@@ -17,7 +17,7 @@ interface BuildRefsOutputInput {
17
17
 
18
18
  interface RefsOutputResult {
19
19
  text: string;
20
- readseekValue: {
20
+ readSeekValue: {
21
21
  tool: "refs";
22
22
  files: Array<{
23
23
  path: string;
@@ -30,13 +30,13 @@ export function buildRefsOutput(input: BuildRefsOutputInput): RefsOutputResult {
30
30
  if (input.files.length === 0) {
31
31
  return {
32
32
  text: `No references found for: ${input.name}`,
33
- readseekValue: { tool: "refs", files: [] },
33
+ readSeekValue: { tool: "refs", files: [] },
34
34
  };
35
35
  }
36
36
 
37
37
  return {
38
38
  text: formatAnchoredFileBlocks(input.files, (line) => (line.enclosingSymbol ? ` (in ${line.enclosingSymbol})` : "")),
39
- readseekValue: {
39
+ readSeekValue: {
40
40
  tool: "refs",
41
41
  files: input.files.map((file) => ({
42
42
  path: file.path,
package/src/refs.ts CHANGED
@@ -5,10 +5,10 @@ import { defineToolPromptMetadata } from "./tool-prompt-metadata.js";
5
5
  import { statSearchPathOrError } from "./stat-search-path.js";
6
6
  import { buildReadSeekLineWithHash, buildToolErrorResult } from "./readseek-value.js";
7
7
  import { resolveToCwd } from "./path-utils.js";
8
- import { classifyReadSeekFailure, readseekRefs, type ReadSeekReference } from "./readseek-client.js";
8
+ import { classifyReadSeekFailure, readSeekRefs, type ReadSeekReference } from "./readseek-client.js";
9
9
  import { buildRefsOutput, type RefsOutputFile, type RefsOutputLine } from "./refs-output.js";
10
10
  import type { FileAnchoredCallback } from "./tool-types.js";
11
- import { langParam, readseekGitSearchParams, searchPathParam, validateIgnoredRequiresOthers } from "./readseek-params.js";
11
+ import { langParam, readSeekGitSearchParams, searchPathParam, validateIgnoredRequiresOthers } from "./readseek-params.js";
12
12
  import { registerReadSeekTool } from "./register-tool.js";
13
13
 
14
14
  import { renderAnchoredFilesResult, renderReadSeekSearchCall } from "./tui-render-utils.js";
@@ -94,7 +94,7 @@ export async function executeRefs(opts: ExecuteRefsOptions): Promise<any> {
94
94
  if (!statResult.ok) return statResult.error;
95
95
 
96
96
  try {
97
- const references = await readseekRefs(searchPath, p.name, {
97
+ const references = await readSeekRefs(searchPath, p.name, {
98
98
  scope: p.scope,
99
99
  line: p.line,
100
100
  column: p.column,
@@ -111,7 +111,7 @@ export async function executeRefs(opts: ExecuteRefsOptions): Promise<any> {
111
111
  }
112
112
  return {
113
113
  content: [{ type: "text", text: builtOutput.text }],
114
- details: { readseekValue: builtOutput.readseekValue },
114
+ details: { readSeekValue: builtOutput.readSeekValue },
115
115
  };
116
116
  } catch (err: any) {
117
117
  const failure = classifyReadSeekFailure(err);
@@ -124,11 +124,7 @@ export async function executeRefs(opts: ExecuteRefsOptions): Promise<any> {
124
124
 
125
125
  export function registerRefsTool(pi: ExtensionAPI, options: RefsToolOptions = {}) {
126
126
  const tool = registerReadSeekTool(pi, {
127
- policy: "read-only",
128
- pythonName: "refs",
129
- defaultExposure: "opt-in",
130
- }, {
131
- name: "refs",
127
+ name: "readSeek_refs",
132
128
  label: "References",
133
129
  description: REFS_PROMPT_METADATA.description,
134
130
  promptSnippet: REFS_PROMPT_METADATA.promptSnippet,
@@ -140,7 +136,7 @@ export function registerRefsTool(pi: ExtensionAPI, options: RefsToolOptions = {}
140
136
  scope: Type.Optional(Type.Boolean({ description: "Restrict to the binding under line/column (single file)" })),
141
137
  line: Type.Optional(Type.Number({ description: "One-based cursor line, used with scope" })),
142
138
  column: Type.Optional(Type.Number({ description: "One-based cursor byte column, used with scope" })),
143
- ...readseekGitSearchParams(),
139
+ ...readSeekGitSearchParams(),
144
140
  }),
145
141
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
146
142
  return executeRefs({ params, signal, cwd: ctx.cwd, onFileAnchored: options.onFileAnchored });
@@ -20,48 +20,10 @@ export function mapParam() {
20
20
  return Type.Optional(Type.Boolean({ description: "Append structural map" }));
21
21
  }
22
22
 
23
- export type ReadSeekToolPolicy = "read-only" | "mutating";
24
-
25
- export type ReadSeekToolExposure = "safe-by-default" | "opt-in" | "not-safe-by-default";
26
-
27
- /**
28
- * Per-tool fields of the pi tool config (`ptc`) that genuinely differ between
29
- * readseek tools. The remaining fields are constant or derived.
30
- */
31
- export interface ReadSeekToolConfig {
32
- policy: ReadSeekToolPolicy;
33
- pythonName: string;
34
- defaultExposure: ReadSeekToolExposure;
35
- }
36
-
37
- export interface ReadSeekToolPtc extends ReadSeekToolConfig {
38
- callable: true;
39
- enabled: true;
40
- readOnly: boolean;
41
- }
42
-
43
23
  type ToolSpec = Parameters<ExtensionAPI["registerTool"]>[0];
44
24
 
45
- /**
46
- * Attach the standard `ptc` envelope to a tool definition and register it.
47
- *
48
- * `callable` and `enabled` are always true and `readOnly` is derived from
49
- * `policy`, so callers supply only the fields that vary between tools.
50
- */
51
- export function registerReadSeekTool<T extends ToolSpec>(
52
- pi: ExtensionAPI,
53
- config: ReadSeekToolConfig,
54
- tool: T,
55
- ): T & { ptc: ReadSeekToolPtc } {
56
- const ptc: ReadSeekToolPtc = {
57
- callable: true,
58
- enabled: true,
59
- policy: config.policy,
60
- readOnly: config.policy === "read-only",
61
- pythonName: config.pythonName,
62
- defaultExposure: config.defaultExposure,
63
- };
64
- const registered = { ...tool, ptc };
65
- pi.registerTool(registered);
66
- return registered;
25
+ /** Register a readseek-backed tool definition. */
26
+ export function registerReadSeekTool<T extends ToolSpec>(pi: ExtensionAPI, tool: T): T {
27
+ pi.registerTool(tool);
28
+ return tool;
67
29
  }
package/src/rename.ts CHANGED
@@ -6,7 +6,7 @@ import { Text } from "@earendil-works/pi-tui";
6
6
  import { defineToolPromptMetadata } from "./tool-prompt-metadata.js";
7
7
  import { buildToolErrorResult } from "./readseek-value.js";
8
8
  import { resolveToCwd } from "./path-utils.js";
9
- import { classifyReadSeekFailure, readseekRename, type RenameOutput } from "./readseek-client.js";
9
+ import { classifyReadSeekFailure, readSeekRename, type RenameOutput } from "./readseek-client.js";
10
10
  import { filePathParam, registerReadSeekTool } from "./register-tool.js";
11
11
 
12
12
  import { clampLinesToWidth, linkToolPath, renderPendingResult, resolveRenderResultContext, summaryLine } from "./tui-render-utils.js";
@@ -54,7 +54,7 @@ export async function executeRename(opts: ExecuteRenameOptions): Promise<any> {
54
54
  const filePath = resolveToCwd(p.path, cwd);
55
55
 
56
56
  try {
57
- const output = await readseekRename(filePath, {
57
+ const output = await readSeekRename(filePath, {
58
58
  to: p.to,
59
59
  line: p.line,
60
60
  column: p.column,
@@ -92,7 +92,7 @@ export async function executeRename(opts: ExecuteRenameOptions): Promise<any> {
92
92
  return {
93
93
  content: [{ type: "text", text }],
94
94
  details: {
95
- readseekValue: {
95
+ readSeekValue: {
96
96
  tool: "rename",
97
97
  ok: true,
98
98
  path: filePath,
@@ -108,11 +108,7 @@ export async function executeRename(opts: ExecuteRenameOptions): Promise<any> {
108
108
 
109
109
  export function registerRenameTool(pi: ExtensionAPI) {
110
110
  registerReadSeekTool(pi, {
111
- policy: "mutating",
112
- pythonName: "rename",
113
- defaultExposure: "not-safe-by-default",
114
- }, {
115
- name: "rename",
111
+ name: "readSeek_rename",
116
112
  label: "Rename",
117
113
  description: RENAME_PROMPT_METADATA.description,
118
114
  promptSnippet: RENAME_PROMPT_METADATA.promptSnippet,
@@ -138,8 +134,8 @@ export function registerRenameTool(pi: ExtensionAPI) {
138
134
 
139
135
  const content = result.content?.[0];
140
136
  const textContent = content?.type === "text" ? content.text : "";
141
- const readseekValue = (result.details as any)?.readseekValue;
142
- const output = readseekValue?.output as RenameOutput | undefined;
137
+ const readSeekValue = (result.details as any)?.readSeekValue;
138
+ const output = readSeekValue?.output as RenameOutput | undefined;
143
139
 
144
140
  if (isError || result.isError) {
145
141
  return new Text(textContent || "rename failed", 0, 0);
@@ -1,4 +1,4 @@
1
- import { readseekMapContent } from "./readseek-client.js";
1
+ import { readSeekMapContent } from "./readseek-client.js";
2
2
  import { findSymbol } from "./readseek/symbol-lookup.js";
3
3
  import { formatAmbiguous, formatNotFound } from "./readseek/symbol-error-format.js";
4
4
  import { normalizeToLF } from "./edit-diff.js";
@@ -33,7 +33,7 @@ function reindent(text: string, indent: string): string {
33
33
  }
34
34
 
35
35
  export async function replaceSymbol(input: ReplaceSymbolInput): Promise<ReplaceSymbolResult> {
36
- const map = await readseekMapContent(input.filePath, input.content);
36
+ const map = await readSeekMapContent(input.filePath, input.content);
37
37
  if (!map) {
38
38
  return {
39
39
  type: "unsupported",
package/src/sg-output.ts CHANGED
@@ -15,7 +15,7 @@ export interface BuildSgOutputInput {
15
15
 
16
16
  export interface SgOutputResult {
17
17
  text: string;
18
- readseekValue: {
18
+ readSeekValue: {
19
19
  tool: "search";
20
20
  files: Array<{
21
21
  path: string;
@@ -29,7 +29,7 @@ export function buildSgOutput(input: BuildSgOutputInput): SgOutputResult {
29
29
  if (input.files.length === 0) {
30
30
  return {
31
31
  text: `No matches found for pattern: ${input.pattern}`,
32
- readseekValue: {
32
+ readSeekValue: {
33
33
  tool: "search",
34
34
  files: [],
35
35
  },
@@ -38,7 +38,7 @@ export function buildSgOutput(input: BuildSgOutputInput): SgOutputResult {
38
38
 
39
39
  return {
40
40
  text: formatAnchoredFileBlocks(input.files),
41
- readseekValue: {
41
+ readSeekValue: {
42
42
  tool: "search",
43
43
  files: input.files.map((file) => ({
44
44
  path: file.path,
package/src/sg.ts CHANGED
@@ -7,10 +7,10 @@ import { defineToolPromptMetadata } from "./tool-prompt-metadata.js";
7
7
  import { buildReadSeekLineWithHash, buildToolErrorResult, type ReadSeekLine } from "./readseek-value.js";
8
8
  import { resolveToCwd } from "./path-utils.js";
9
9
  import { statSearchPathOrError } from "./stat-search-path.js";
10
- import { classifyReadSeekFailure, readseekSearch, type ReadSeekHashline, type ReadSeekSearchFileOutput } from "./readseek-client.js";
10
+ import { classifyReadSeekFailure, readSeekSearch, type ReadSeekHashline, type ReadSeekSearchFileOutput } from "./readseek-client.js";
11
11
  import { buildSgOutput } from "./sg-output.js";
12
12
  import type { FileAnchoredCallback } from "./tool-types.js";
13
- import { langParam, readseekGitSearchParams, searchPathParam, validateIgnoredRequiresOthers } from "./readseek-params.js";
13
+ import { langParam, readSeekGitSearchParams, searchPathParam, validateIgnoredRequiresOthers } from "./readseek-params.js";
14
14
  import { registerReadSeekTool } from "./register-tool.js";
15
15
 
16
16
  import { renderAnchoredFilesResult, renderReadSeekSearchCall } from "./tui-render-utils.js";
@@ -66,7 +66,7 @@ export interface ExecuteSgOptions {
66
66
  onFileAnchored?: FileAnchoredCallback;
67
67
  }
68
68
 
69
- function readseekLineFromSearch(line: ReadSeekHashline): ReadSeekLine {
69
+ function readSeekLineFromSearch(line: ReadSeekHashline): ReadSeekLine {
70
70
  return buildReadSeekLineWithHash(line.line, line.hash, line.text);
71
71
  }
72
72
 
@@ -74,7 +74,7 @@ function linesFromSearchResult(result: ReadSeekSearchFileOutput, ranges: SgRange
74
74
  const lineMap = new Map<number, ReadSeekLine>();
75
75
  for (const match of result.matches) {
76
76
  for (const line of match.hashlines) {
77
- lineMap.set(line.line, readseekLineFromSearch(line));
77
+ lineMap.set(line.line, readSeekLineFromSearch(line));
78
78
  }
79
79
  }
80
80
 
@@ -83,16 +83,16 @@ function linesFromSearchResult(result: ReadSeekSearchFileOutput, ranges: SgRange
83
83
  for (const range of ranges) {
84
84
  for (let line = range.startLine; line <= range.endLine; line++) {
85
85
  if (seen.has(line)) continue;
86
- const readseekLine = lineMap.get(line);
87
- if (!readseekLine) continue;
86
+ const readSeekLine = lineMap.get(line);
87
+ if (!readSeekLine) continue;
88
88
  seen.add(line);
89
- lines.push(readseekLine);
89
+ lines.push(readSeekLine);
90
90
  }
91
91
  }
92
92
  return lines;
93
93
  }
94
94
 
95
- function readseekLanguageForPath(language: string | undefined, searchPath: string, isFile: boolean): string | undefined {
95
+ function readSeekLanguageForPath(language: string | undefined, searchPath: string, isFile: boolean): string | undefined {
96
96
  if (language === "typescript" && isFile && path.extname(searchPath).toLowerCase() === ".tsx") return "tsx";
97
97
  return language;
98
98
  }
@@ -112,8 +112,8 @@ export async function executeSg(opts: ExecuteSgOptions): Promise<any> {
112
112
  const searchPathIsFile = statResult.stats.isFile();
113
113
 
114
114
  try {
115
- const effectiveLang = readseekLanguageForPath(p.lang, searchPath, searchPathIsFile);
116
- const results = await readseekSearch(searchPath, p.pattern, {
115
+ const effectiveLang = readSeekLanguageForPath(p.lang, searchPath, searchPathIsFile);
116
+ const results = await readSeekSearch(searchPath, p.pattern, {
117
117
  language: effectiveLang,
118
118
  cached: p.cached,
119
119
  others: p.others,
@@ -125,12 +125,12 @@ export async function executeSg(opts: ExecuteSgOptions): Promise<any> {
125
125
  return {
126
126
  content: [{ type: "text", text: emptyOutput.text }],
127
127
  details: {
128
- readseekValue: emptyOutput.readseekValue,
128
+ readSeekValue: emptyOutput.readSeekValue,
129
129
  },
130
130
  };
131
131
  }
132
132
 
133
- const readseekFiles: Array<{
133
+ const readSeekFiles: Array<{
134
134
  displayPath: string;
135
135
  path: string;
136
136
  ranges: SgRange[];
@@ -145,7 +145,7 @@ export async function executeSg(opts: ExecuteSgOptions): Promise<any> {
145
145
  const mergedRanges = mergeRanges(ranges);
146
146
  const lines = linesFromSearchResult(result, mergedRanges);
147
147
  if (lines.length === 0) continue;
148
- readseekFiles.push({
148
+ readSeekFiles.push({
149
149
  displayPath: display,
150
150
  path: abs,
151
151
  ranges: mergedRanges.map((range) => ({ ...range })),
@@ -153,27 +153,27 @@ export async function executeSg(opts: ExecuteSgOptions): Promise<any> {
153
153
  });
154
154
  }
155
155
 
156
- if (readseekFiles.length === 0) {
156
+ if (readSeekFiles.length === 0) {
157
157
  const emptyOutput = buildSgOutput({ pattern: p.pattern, files: [] });
158
158
  return {
159
159
  content: [{ type: "text", text: emptyOutput.text }],
160
160
  details: {
161
- readseekValue: emptyOutput.readseekValue,
161
+ readSeekValue: emptyOutput.readSeekValue,
162
162
  },
163
163
  };
164
164
  }
165
165
 
166
166
  const builtOutput = buildSgOutput({
167
167
  pattern: p.pattern,
168
- files: readseekFiles,
168
+ files: readSeekFiles,
169
169
  });
170
- for (const readseekFile of readseekFiles) {
171
- onFileAnchored?.(readseekFile.path);
170
+ for (const readSeekFile of readSeekFiles) {
171
+ onFileAnchored?.(readSeekFile.path);
172
172
  }
173
173
  return {
174
174
  content: [{ type: "text", text: builtOutput.text }],
175
175
  details: {
176
- readseekValue: builtOutput.readseekValue,
176
+ readSeekValue: builtOutput.readSeekValue,
177
177
  },
178
178
  };
179
179
  } catch (err: any) {
@@ -184,11 +184,7 @@ export async function executeSg(opts: ExecuteSgOptions): Promise<any> {
184
184
 
185
185
  export function registerSgTool(pi: ExtensionAPI, options: SgToolOptions = {}) {
186
186
  const tool = registerReadSeekTool(pi, {
187
- policy: "read-only",
188
- pythonName: "search",
189
- defaultExposure: "opt-in",
190
- }, {
191
- name: "search",
187
+ name: "readSeek_search",
192
188
  label: "Structural Search",
193
189
  description: SG_PROMPT_METADATA.description,
194
190
  promptSnippet: SG_PROMPT_METADATA.promptSnippet,
@@ -197,7 +193,7 @@ export function registerSgTool(pi: ExtensionAPI, options: SgToolOptions = {}) {
197
193
  pattern: Type.String({ description: "AST pattern" }),
198
194
  lang: langParam(),
199
195
  path: searchPathParam(),
200
- ...readseekGitSearchParams(),
196
+ ...readSeekGitSearchParams(),
201
197
  }),
202
198
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
203
199
  return executeSg({ params, signal, cwd: ctx.cwd, onFileAnchored: options.onFileAnchored });