pi-readseek 0.4.29 → 0.5.0

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.
@@ -17,7 +17,7 @@ export interface BuildEditOutputInput {
17
17
  export interface EditOutputResult {
18
18
  text: string;
19
19
  patch: string;
20
- readseekValue: ReturnType<typeof buildReadSeekEditResult>;
20
+ readSeekValue: ReturnType<typeof buildReadSeekEditResult>;
21
21
  }
22
22
 
23
23
  const EDIT_OPERATION_NAMES = ["set_line", "replace_lines", "insert_after", "replace"] as const;
@@ -76,21 +76,15 @@ function formatWhitespaceOnlyWarning(semanticSummary: SemanticSummary | undefine
76
76
  if (!extractNewTextValues(edits).some((text) => /\S/.test(text))) return undefined;
77
77
  return "⚠ Edit classified as whitespace-only — if you intended a behavior change, re-read to verify.";
78
78
  }
79
- function formatSemanticSuffix(semanticSummary: SemanticSummary | undefined): string {
80
- if (!semanticSummary) return "";
81
-
82
- const movedBlocks = semanticSummary.movedBlocks ?? 0;
83
- if (movedBlocks <= 0) return "";
84
-
85
- const blockWord = movedBlocks === 1 ? "block" : "blocks";
86
- return ` [semantic: ${semanticSummary.classification}, ${movedBlocks} ${blockWord} moved]`;
79
+ function formatSemanticSuffix(_semanticSummary: SemanticSummary | undefined): string {
80
+ return "";
87
81
  }
88
82
  function formatReplaceHint(edits: unknown[] | undefined, noopEdits: unknown[]): string | undefined {
89
83
  if ((noopEdits ?? []).length > 0) return undefined;
90
84
  const counts = countEditTypes(edits);
91
85
  if (counts.replace === 0) return undefined;
92
86
  if (counts.replace !== counts.total) return undefined;
93
- return "[info: this edit used replace (unverified). For safer future edits, prefer set_line/replace_lines with an anchor from read/grep/search.]";
87
+ return "[info: this edit used replace (unverified). For safer future edits, prefer set_line/replace_lines with an anchor from readSeek_read/readSeek_grep/readSeek_search.]";
94
88
  }
95
89
  export function buildEditOutput(input: BuildEditOutputInput): EditOutputResult {
96
90
  const summary = `Updated ${input.displayPath}`;
@@ -105,7 +99,7 @@ export function buildEditOutput(input: BuildEditOutputInput): EditOutputResult {
105
99
  return {
106
100
  text,
107
101
  patch: input.patch ?? "",
108
- readseekValue: buildReadSeekEditResult({
102
+ readSeekValue: buildReadSeekEditResult({
109
103
  path: input.path,
110
104
  summary,
111
105
  diff: input.diff,
@@ -1,4 +1,4 @@
1
- import { readseekCheck, type ReadSeekCheckOutput, type ReadSeekDiagnostic } from "./readseek-client.js";
1
+ import { readSeekCheck, type ReadSeekCheckOutput, type ReadSeekDiagnostic } from "./readseek-client.js";
2
2
 
3
3
  export interface ValidateInput {
4
4
  filePath: string;
@@ -41,12 +41,13 @@ const EMPTY: ReadSeekCheckOutput = { errorCount: 0, missingCount: 0, diagnostics
41
41
  */
42
42
  export async function validateSyntaxRegression(
43
43
  input: ValidateInput,
44
+ options: { signal?: AbortSignal } = {},
44
45
  ): Promise<ValidateResult | null> {
45
46
  let before: ReadSeekCheckOutput;
46
47
  let after: ReadSeekCheckOutput;
47
48
  try {
48
- before = input.before === undefined ? EMPTY : await readseekCheck(input.filePath, input.before);
49
- after = await readseekCheck(input.filePath, input.after);
49
+ before = input.before === undefined ? EMPTY : await readSeekCheck(input.filePath, input.before, { signal: options.signal });
50
+ after = await readSeekCheck(input.filePath, input.after, { signal: options.signal });
50
51
  } catch {
51
52
  return null;
52
53
  }
package/src/edit.ts CHANGED
@@ -14,7 +14,7 @@ import { looksLikeBinary } from "./binary-detect.js";
14
14
  import { throwIfAborted } from "./runtime.js";
15
15
  import { formatFsError } from "./fs-error.js";
16
16
  import { buildEditOutput } from "./edit-output.js";
17
- import { classifyEdit, isDifftAvailable, runDifftastic } from "./edit-classify.js";
17
+ import { classifyEdit } from "./edit-classify.js";
18
18
  import type { SemanticSummary } from "./readseek-value.js";
19
19
  import { buildReadSeekError } from "./readseek-value.js";
20
20
  import { classifyReadSeekFailure } from "./readseek-client.js";
@@ -29,8 +29,6 @@ import { upsertDiffComponent, upsertTextComponent } from "./tui-diff-component.j
29
29
  import type { FreshAnchorsPredicate } from "./tool-types.js";
30
30
  import { filePathParam, registerReadSeekTool } from "./register-tool.js";
31
31
 
32
- import { resolveEditDiffDisplay } from "./readseek-settings.js";
33
-
34
32
  const EDIT_PENDING_PREVIEW_STATE_KEY = "hashline-edit-pending-preview";
35
33
 
36
34
  function pendingPreviewLines(summary: string, preview: PendingDiffPreviewResult | undefined, expanded: boolean): { lines: string[]; diffData?: ReturnType<typeof buildDiffData>; headerLabel?: string } {
@@ -73,7 +71,7 @@ type HashlineParams = Static<typeof hashlineEditSchema>;
73
71
 
74
72
  const EDIT_PROMPT_METADATA = defineToolPromptMetadata({
75
73
  promptUrl: new URL("../prompts/edit.md", import.meta.url),
76
- promptSnippet: "Edit files using hash-verified anchors from read/grep/search/write",
74
+ promptSnippet: "Edit files using hash-verified anchors from readSeek_read/readSeek_grep/readSeek_search/readSeek_write",
77
75
  });
78
76
 
79
77
  function buildEditError(
@@ -85,7 +83,7 @@ function buildEditError(
85
83
  ): {
86
84
  content: [{ type: "text"; text: string }];
87
85
  isError: true;
88
- details: EditToolDetails & { readseekValue: any };
86
+ details: EditToolDetails & { readSeekValue: any };
89
87
  } {
90
88
  return {
91
89
  content: [{ type: "text", text: message }],
@@ -94,13 +92,13 @@ function buildEditError(
94
92
  diff: "",
95
93
  patch: "",
96
94
  firstChangedLine: undefined,
97
- readseekValue: {
95
+ readSeekValue: {
98
96
  tool: "edit",
99
97
  ok: false,
100
98
  path,
101
99
  error: buildReadSeekError(code, message, hint, errorDetails),
102
100
  },
103
- } as EditToolDetails & { readseekValue: any },
101
+ } as EditToolDetails & { readSeekValue: any },
104
102
  };
105
103
  }
106
104
 
@@ -137,14 +135,14 @@ export async function executeEdit(opts: ExecuteEditOptions): Promise<any> {
137
135
  if (wasReadInSession && !wasReadInSession(absolutePath)) {
138
136
  const message = [
139
137
  `You must get fresh anchors for ${absolutePath} before editing it.`,
140
- `Call read(${JSON.stringify(rawPath)}) first, or use grep, search, or write to produce fresh anchors for this file.`,
141
- "edit requires fresh LINE:HASH anchors from read, grep, search, or write so the hashes match the current file contents.",
138
+ `Call readSeek_read(${JSON.stringify(rawPath)}) first, or use readSeek_grep, readSeek_search, or readSeek_write to produce fresh anchors for this file.`,
139
+ "readSeek_edit requires fresh LINE:HASH anchors from readSeek_read, readSeek_grep, readSeek_search, or readSeek_write so the hashes match the current file contents.",
142
140
  ].join(" ");
143
141
  return buildEditError(
144
142
  absolutePath,
145
143
  "file-not-read",
146
144
  message,
147
- `Call read(${JSON.stringify(rawPath)}) first, or use grep, search, or write to produce fresh anchors for this file.`,
145
+ `Call readSeek_read(${JSON.stringify(rawPath)}) first, or use readSeek_grep, readSeek_search, or readSeek_write to produce fresh anchors for this file.`,
148
146
  );
149
147
  }
150
148
  const hasTopLevelReplaceInput =
@@ -253,7 +251,7 @@ export async function executeEdit(opts: ExecuteEditOptions): Promise<any> {
253
251
  // Error-precedence order: replace_symbol resolution > anchor-overlap > anchored-edit.
254
252
  //
255
253
  // store successful probe results and reuse them in the apply loop so
256
- // readseekMapContent is invoked at most once per replace_symbol edit.
254
+ // readSeekMapContent is invoked at most once per replace_symbol edit.
257
255
  const replaceSymbolRanges: { start: number; end: number }[] = [];
258
256
  const rsProbeResults: { type: "ok"; content: string; replacement: string; warnings: string[]; range: { start: number; end: number } }[] = [];
259
257
  try {
@@ -365,7 +363,7 @@ export async function executeEdit(opts: ExecuteEditOptions): Promise<any> {
365
363
  // Apply pass: reuse all probe results. The probe pass resolved every
366
364
  // replace_symbol against originalNormalized; apply those replacements in
367
365
  // reverse source order so original line ranges stay valid and no second
368
- // replaceSymbol/readseekMapContent call is needed.
366
+ // replaceSymbol/readSeekMapContent call is needed.
369
367
  const replaceSymbolWarnings: string[] = [];
370
368
  if (rsProbeResults.length > 0) {
371
369
  const lines = originalNormalized.split("\n");
@@ -454,7 +452,7 @@ export async function executeEdit(opts: ExecuteEditOptions): Promise<any> {
454
452
  filePath: absolutePath,
455
453
  before: originalNormalized,
456
454
  after: result,
457
- });
455
+ }, { signal });
458
456
  if (regression) {
459
457
  const lines = regression.errorLines.join(", ");
460
458
  const message = `syntax-regression: lines ${lines}`;
@@ -516,23 +514,9 @@ export async function executeEdit(opts: ExecuteEditOptions): Promise<any> {
516
514
  if (syntaxWarning) warnings.push(syntaxWarning);
517
515
  // Semantic classification
518
516
  const internalClassification = classifyEdit(originalNormalized, result);
519
- const difftAvailable = await isDifftAvailable();
520
- let semanticSummary: SemanticSummary = {
517
+ const semanticSummary: SemanticSummary = {
521
518
  classification: internalClassification.classification,
522
- difftasticAvailable: difftAvailable,
523
519
  };
524
-
525
- if (difftAvailable) {
526
- const ext = path.split(".").pop() ?? "txt";
527
- const difftResult = await runDifftastic(originalNormalized, result, ext);
528
- if (difftResult) {
529
- semanticSummary = {
530
- classification: difftResult.classification,
531
- difftasticAvailable: true,
532
- ...(difftResult.movedBlocks > 0 ? { movedBlocks: difftResult.movedBlocks } : {}),
533
- };
534
- }
535
- }
536
520
  const builtOutput = buildEditOutput({
537
521
  path: absolutePath,
538
522
  displayPath: path,
@@ -553,10 +537,10 @@ export async function executeEdit(opts: ExecuteEditOptions): Promise<any> {
553
537
  patch: builtOutput.patch,
554
538
  diffData,
555
539
  firstChangedLine: anchorResult.firstChangedLine ?? diffResult.firstChangedLine,
556
- readseekValue: builtOutput.readseekValue,
540
+ readSeekValue: builtOutput.readSeekValue,
557
541
  } as EditToolDetails & {
558
542
  diffData: typeof diffData;
559
- readseekValue: {
543
+ readSeekValue: {
560
544
  tool: string;
561
545
  ok: boolean;
562
546
  path: string;
@@ -581,11 +565,7 @@ export async function executeEdit(opts: ExecuteEditOptions): Promise<any> {
581
565
 
582
566
  export function registerEditTool(pi: ExtensionAPI, options: EditToolOptions = {}) {
583
567
  const tool = registerReadSeekTool(pi, {
584
- policy: "mutating",
585
- pythonName: "edit",
586
- defaultExposure: "not-safe-by-default",
587
- }, {
588
- name: "edit",
568
+ name: "readSeek_edit",
589
569
  label: "Edit",
590
570
  description: EDIT_PROMPT_METADATA.description,
591
571
  promptSnippet: EDIT_PROMPT_METADATA.promptSnippet,
@@ -631,7 +611,7 @@ export function registerEditTool(pi: ExtensionAPI, options: EditToolOptions = {}
631
611
  previewKey,
632
612
  () => buildPendingEditPreviewData(args ?? {}, context.cwd ?? process.cwd()),
633
613
  );
634
- const expanded = !!context.expanded || resolveEditDiffDisplay() === "expanded";
614
+ const expanded = !!context.expanded;
635
615
  const preview2 = pendingPreviewLines(text, preview, expanded);
636
616
  if (preview2.diffData) {
637
617
  return upsertDiffComponent(context.lastComponent, { prefixLines: preview2.lines, diffData: preview2.diffData, theme, expanded: true });
@@ -639,7 +619,7 @@ export function registerEditTool(pi: ExtensionAPI, options: EditToolOptions = {}
639
619
  return upsertTextComponent(context.lastComponent, clampLinesToWidth(preview2.lines, context.width).join("\n"));
640
620
  },
641
621
  renderResult(result: any, options: ToolRenderResultOptions, theme: any, ...rest: any[]) {
642
- const { isPartial, isError, expanded: baseExpanded, width, context } = resolveRenderResultContext(options, rest);
622
+ const { isPartial, isError, expanded, width, context } = resolveRenderResultContext(options, rest);
643
623
 
644
624
  if (isPartial) {
645
625
  return renderPendingResult("pending edit", width);
@@ -652,13 +632,13 @@ export function registerEditTool(pi: ExtensionAPI, options: EditToolOptions = {}
652
632
  .join("\n") ?? "";
653
633
  const details = result.details ?? {};
654
634
  const diff: string = details.diff ?? "";
655
- const readseekValue = details.readseekValue as {
635
+ const readSeekValue = details.readSeekValue as {
656
636
  warnings?: string[];
657
637
  noopEdits?: unknown[];
658
638
  } | undefined;
659
- const warnings = readseekValue?.warnings ?? [];
660
- const noopEdits = readseekValue?.noopEdits ?? [];
661
- const semanticClassification = (readseekValue as any)?.semanticSummary?.classification as string | undefined;
639
+ const warnings = readSeekValue?.warnings ?? [];
640
+ const noopEdits = readSeekValue?.noopEdits ?? [];
641
+ const semanticClassification = (readSeekValue as any)?.semanticSummary?.classification as string | undefined;
662
642
 
663
643
  const info = formatEditResultText({
664
644
  isError: isError || !!result.isError,
@@ -669,7 +649,6 @@ export function registerEditTool(pi: ExtensionAPI, options: EditToolOptions = {}
669
649
  semanticClassification: semanticClassification as any,
670
650
  });
671
651
 
672
- const expanded = baseExpanded || resolveEditDiffDisplay() === "expanded";
673
652
  const diffData = (details as any).diffData;
674
653
  const stats = diffData?.stats ?? { added: 0, removed: 0 };
675
654
  let text = "";
package/src/file-map.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { stat } from "node:fs/promises";
2
2
 
3
3
  import type { FileMap } from "./readseek/types.js";
4
- import { readseekMap } from "./readseek-client.js";
4
+ import { readSeekMap } from "./readseek-client.js";
5
5
 
6
6
  /**
7
7
  * Fetch a structural file map from readseek, which maintains its own on-disk
@@ -10,7 +10,7 @@ import { readseekMap } from "./readseek-client.js";
10
10
  export async function getOrGenerateMap(absPath: string): Promise<FileMap | null> {
11
11
  try {
12
12
  const { size } = await stat(absPath);
13
- return await readseekMap(absPath, size);
13
+ return await readSeekMap(absPath, size);
14
14
  } catch {
15
15
  return null;
16
16
  }
@@ -1,29 +1,6 @@
1
1
  import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES } from "@earendil-works/pi-coding-agent";
2
2
  import { resolveReadSeekJsonSettings } from "./readseek-settings.js";
3
3
 
4
- const POSITIVE_BASE10_INT = /^[1-9][0-9]*$/;
5
-
6
- /**
7
- * Strict positive base-10 integer parser used by readseek env knobs.
8
- *
9
- * Accepts: trimmed strings matching /^[1-9][0-9]*$/ that parse to a finite
10
- * positive integer.
11
- *
12
- * Rejects: undefined, empty, whitespace-only, "0", negative, signed, hex
13
- * ("0x10"), exponent ("1e3"), decimal ("3.14"), separators ("1,000" /
14
- * "1_000"), embedded whitespace ("5 5").
15
- *
16
- * Returns `undefined` on rejection; never throws.
17
- */
18
- function parsePositiveBase10Int(raw: string | undefined | null): number | undefined {
19
- if (raw === undefined || raw === null) return undefined;
20
- const trimmed = String(raw).trim();
21
- if (!POSITIVE_BASE10_INT.test(trimmed)) return undefined;
22
- const parsed = Number.parseInt(trimmed, 10);
23
- if (!Number.isFinite(parsed) || parsed <= 0) return undefined;
24
- return parsed;
25
- }
26
-
27
4
  export interface GrepOutputBudget {
28
5
  maxLines: number;
29
6
  maxBytes: number;
@@ -31,7 +8,7 @@ export interface GrepOutputBudget {
31
8
 
32
9
  /**
33
10
  * Effective grep-output ceilings used as clamp upper bounds and as the
34
- * fallback defaults when env vars are unset/invalid.
11
+ * fallback defaults when the settings are unset.
35
12
  *
36
13
  * The bytes ceiling is the already-tightened 50 KiB used by `buildGrepOutput`
37
14
  * today, NOT the unclamped `DEFAULT_MAX_BYTES`.
@@ -39,41 +16,20 @@ export interface GrepOutputBudget {
39
16
  export const GREP_OUTPUT_DEFAULT_MAX_LINES = DEFAULT_MAX_LINES;
40
17
  export const GREP_OUTPUT_DEFAULT_MAX_BYTES = Math.min(DEFAULT_MAX_BYTES, 50 * 1024);
41
18
 
42
- function resolveEnvDimension(rawEnvValue: string | undefined, ceiling: number): number | undefined {
43
- const parsed = parsePositiveBase10Int(rawEnvValue);
44
- return parsed === undefined ? undefined : Math.min(parsed, ceiling);
45
- }
46
-
47
- function resolveDimension(rawEnvValue: string | undefined, jsonValue: number | undefined, ceiling: number): number {
48
- if (rawEnvValue !== undefined) {
49
- const envValue = resolveEnvDimension(rawEnvValue, ceiling);
50
- if (envValue !== undefined) return envValue;
51
- }
19
+ function resolveDimension(jsonValue: number | undefined, ceiling: number): number {
52
20
  if (jsonValue !== undefined) return Math.min(jsonValue, ceiling);
53
21
  return ceiling;
54
22
  }
55
23
 
56
24
  /**
57
- * Resolve the effective grep visible-output budget. Re-reads `process.env`
58
- * on every call (no memoization) so tests and long-lived agent sessions can
59
- * change the env vars dynamically.
60
- *
61
- * Invalid / zero / negative env values fall back to the current defaults.
62
- * Above-default values clamp to the current defaults. Below-default values
63
- * are used as-is.
25
+ * Resolve the effective grep visible-output budget from the readseek
26
+ * settings. Below-default values are used as-is; above-default values clamp
27
+ * to the current defaults.
64
28
  */
65
29
  export function resolveGrepOutputBudget(): GrepOutputBudget {
66
30
  const settings = resolveReadSeekJsonSettings().settings.grep;
67
31
  return {
68
- maxLines: resolveDimension(
69
- process.env.READSEEK_GREP_MAX_LINES,
70
- settings?.maxLines,
71
- GREP_OUTPUT_DEFAULT_MAX_LINES,
72
- ),
73
- maxBytes: resolveDimension(
74
- process.env.READSEEK_GREP_MAX_BYTES,
75
- settings?.maxBytes,
76
- GREP_OUTPUT_DEFAULT_MAX_BYTES,
77
- ),
32
+ maxLines: resolveDimension(settings?.maxLines, GREP_OUTPUT_DEFAULT_MAX_LINES),
33
+ maxBytes: resolveDimension(settings?.maxBytes, GREP_OUTPUT_DEFAULT_MAX_BYTES),
78
34
  };
79
35
  }
@@ -68,7 +68,7 @@ interface BuildGrepOutputInput {
68
68
 
69
69
  interface GrepOutputResult {
70
70
  text: string;
71
- readseekValue: {
71
+ readSeekValue: {
72
72
  tool: "grep";
73
73
  summary: boolean;
74
74
  totalMatches: number;
@@ -159,7 +159,7 @@ export function buildGrepOutput(input: BuildGrepOutputInput): GrepOutputResult {
159
159
  if (truncated.truncated) {
160
160
  text = `${truncated.content}\n\n[Output truncated: showing ${truncated.outputLines} of ${truncated.totalLines} lines (${formatSize(truncated.outputBytes)} of ${formatSize(truncated.totalBytes)}). Refine pattern or increase limit.]`;
161
161
  }
162
- const readseekValue: GrepOutputResult["readseekValue"] = {
162
+ const readSeekValue: GrepOutputResult["readSeekValue"] = {
163
163
  tool: "grep",
164
164
  summary: input.summary,
165
165
  totalMatches: input.totalMatches,
@@ -171,11 +171,11 @@ export function buildGrepOutput(input: BuildGrepOutputInput): GrepOutputResult {
171
171
  })),
172
172
  };
173
173
  if (!input.summary && input.scopeMode === "symbol") {
174
- readseekValue.scopes = buildScopeMetadata(input.groups, scopeWarnings);
174
+ readSeekValue.scopes = buildScopeMetadata(input.groups, scopeWarnings);
175
175
  }
176
176
 
177
177
  return {
178
178
  text,
179
- readseekValue,
179
+ readSeekValue,
180
180
  };
181
181
  }
package/src/grep.ts CHANGED
@@ -168,7 +168,6 @@ function escapeForRegex(s: string): string {
168
168
  }
169
169
 
170
170
  interface GrepToolOptions {
171
- searchGuideline?: string;
172
171
  onFileAnchored?: FileAnchoredCallback;
173
172
  }
174
173
 
@@ -253,7 +252,7 @@ export async function executeGrep(opts: ExecuteGrepOptions): Promise<any> {
253
252
  ),
254
253
  details: {
255
254
  ...(typeof result.details === "object" && result.details !== null ? result.details : {}),
256
- readseekValue: {
255
+ readSeekValue: {
257
256
  tool: "grep",
258
257
  summary: !!p.summary,
259
258
  totalMatches: 0,
@@ -410,7 +409,7 @@ export async function executeGrep(opts: ExecuteGrepOptions): Promise<any> {
410
409
  ...result,
411
410
  details: {
412
411
  ...passthroughDetails,
413
- readseekValue: {
412
+ readSeekValue: {
414
413
  tool: "grep",
415
414
  summary: true,
416
415
  totalMatches: 0,
@@ -436,7 +435,7 @@ export async function executeGrep(opts: ExecuteGrepOptions): Promise<any> {
436
435
  ...passthroughDetails,
437
436
  hashlinePassthrough: true,
438
437
  hashlineWarning: warning,
439
- readseekValue: {
438
+ readSeekValue: {
440
439
  tool: "grep",
441
440
  summary: !!p.summary,
442
441
  totalMatches: 0,
@@ -477,20 +476,20 @@ export async function executeGrep(opts: ExecuteGrepOptions): Promise<any> {
477
476
  renderedGroups = scoped.groups;
478
477
  scopeWarnings = scoped.warnings;
479
478
  }
480
- const readseekRecords = recordsFromGroups(renderedGroups);
479
+ const readSeekRecords = recordsFromGroups(renderedGroups);
481
480
  const builtOutput = buildGrepOutput({
482
481
  summary: !!summary,
483
482
  totalMatches,
484
483
  groups: renderedGroups,
485
484
  limit: effectiveLimit,
486
- records: readseekRecords,
485
+ records: readSeekRecords,
487
486
  scopeMode: p.scope === "symbol" && !summary ? "symbol" : undefined,
488
487
  scopeWarnings,
489
488
  passthroughLines,
490
489
  });
491
490
 
492
- if (!summary && readseekRecords.length > 0) {
493
- const anchoredPaths = new Set(readseekRecords.map((record) => record.path));
491
+ if (!summary && readSeekRecords.length > 0) {
492
+ const anchoredPaths = new Set(readSeekRecords.map((record) => record.path));
494
493
  for (const absolutePath of anchoredPaths) {
495
494
  onFileAnchored?.(absolutePath);
496
495
  }
@@ -508,25 +507,19 @@ export async function executeGrep(opts: ExecuteGrepOptions): Promise<any> {
508
507
  ),
509
508
  details: {
510
509
  ...compactDetails,
511
- readseekValue: builtOutput.readseekValue,
510
+ readSeekValue: builtOutput.readSeekValue,
512
511
  },
513
512
  };
514
513
  }
515
514
 
516
515
  export function registerGrepTool(pi: ExtensionAPI, options: GrepToolOptions = {}) {
517
516
  const tool = registerReadSeekTool(pi, {
518
- policy: "read-only",
519
- pythonName: "grep",
520
- defaultExposure: "safe-by-default",
521
- }, {
522
- name: "grep",
517
+ name: "readSeek_grep",
523
518
  label: "grep",
524
519
  description: GREP_PROMPT_METADATA.description,
525
520
  parameters: grepSchema,
526
521
  promptSnippet: GREP_PROMPT_METADATA.promptSnippet,
527
- promptGuidelines: options.searchGuideline
528
- ? [GREP_PROMPT_METADATA.promptGuidelines[0], options.searchGuideline]
529
- : GREP_PROMPT_METADATA.promptGuidelines,
522
+ promptGuidelines: GREP_PROMPT_METADATA.promptGuidelines,
530
523
  async execute(toolCallId, params, signal, onUpdate, ctx) {
531
524
  return executeGrep({
532
525
  toolCallId,
@@ -558,14 +551,14 @@ export function registerGrepTool(pi: ExtensionAPI, options: GrepToolOptions = {}
558
551
  renderResult(result: any, options: ToolRenderResultOptions, theme: any, ...rest: any[]) {
559
552
  const { isPartial, isError, expanded, cwd, width } = resolveRenderResultContext(options, rest);
560
553
 
561
- if (isPartial) return renderPendingResult("pending search", width);
554
+ if (isPartial) return renderPendingResult("pending grep", width);
562
555
 
563
556
  const content = result.content?.[0];
564
557
  const textContent = content?.type === "text" ? content.text : "";
565
558
 
566
559
  if (isError || result.isError) return renderErrorResult(textContent, { expanded, width });
567
560
 
568
- const readseekValue = (result.details as any)?.readseekValue as {
561
+ const readSeekValue = (result.details as any)?.readSeekValue as {
569
562
  tool: "grep";
570
563
  summary: boolean;
571
564
  totalMatches: number;
@@ -575,26 +568,26 @@ export function registerGrepTool(pi: ExtensionAPI, options: GrepToolOptions = {}
575
568
  const hasBinaryWarning = textContent.includes("appears to be a binary file");
576
569
 
577
570
  const fileSet = new Set<string>();
578
- for (const r of readseekValue?.records ?? []) {
571
+ for (const r of readSeekValue?.records ?? []) {
579
572
  if (r.path) fileSet.add(r.path);
580
573
  }
581
574
 
582
575
  const info = formatGrepResultText({
583
- totalMatches: readseekValue?.totalMatches ?? 0,
584
- summary: readseekValue?.summary ?? false,
585
- records: readseekValue?.records ?? [],
576
+ totalMatches: readSeekValue?.totalMatches ?? 0,
577
+ summary: readSeekValue?.summary ?? false,
578
+ records: readSeekValue?.records ?? [],
586
579
  fileCount: fileSet.size,
587
580
  hasBinaryWarning,
588
581
  });
589
582
 
590
583
  if (info.noMatches && !hasBinaryWarning) return new Text(summaryLine("no matches"), 0, 0);
591
- const matchCount = readseekValue?.totalMatches ?? 0;
584
+ const matchCount = readSeekValue?.totalMatches ?? 0;
592
585
  const matchWord = matchCount === 1 ? "match" : "matches";
593
586
  let text = summaryLine(`${matchCount} ${matchWord} returned`, { hidden: !!textContent && !expanded });
594
587
  for (const badge of info.badges) text += theme.fg(badge.startsWith("⚠") ? "warning" : "dim", ` ${badge}`);
595
- if (expanded && readseekValue?.records) {
588
+ if (expanded && readSeekValue?.records) {
596
589
  const fileCounts = new Map<string, number>();
597
- for (const r of readseekValue.records) if (r.path && r.kind === "match") fileCounts.set(r.path, (fileCounts.get(r.path) ?? 0) + 1);
590
+ for (const r of readSeekValue.records) if (r.path && r.kind === "match") fileCounts.set(r.path, (fileCounts.get(r.path) ?? 0) + 1);
598
591
  for (const [filePath, count] of [...fileCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 20)) {
599
592
  const display = path.relative(cwd, filePath) || filePath;
600
593
  text += "\n" + theme.fg("dim", ` ${display} (${count})`);
package/src/hover.ts CHANGED
@@ -8,7 +8,7 @@ import { defineToolPromptMetadata } from "./tool-prompt-metadata.js";
8
8
  import { buildToolErrorResult } from "./readseek-value.js";
9
9
  import { resolveToCwd } from "./path-utils.js";
10
10
  import { formatFsError } from "./fs-error.js";
11
- import { classifyReadSeekFailure, readseekIdentify } from "./readseek-client.js";
11
+ import { classifyReadSeekFailure, readSeekIdentify } from "./readseek-client.js";
12
12
  import { filePathParam, registerReadSeekTool } from "./register-tool.js";
13
13
 
14
14
  import { clampLinesToWidth, linkToolPath, renderPendingResult, resolveRenderResultContext, summaryLine } from "./tui-render-utils.js";
@@ -55,7 +55,7 @@ export async function executeHover(opts: ExecuteHoverOptions): Promise<any> {
55
55
  }
56
56
 
57
57
  try {
58
- const output = await readseekIdentify(filePath, content, {
58
+ const output = await readSeekIdentify(filePath, content, {
59
59
  line: p.line,
60
60
  column: p.column,
61
61
  signal,
@@ -77,7 +77,7 @@ export async function executeHover(opts: ExecuteHoverOptions): Promise<any> {
77
77
  return {
78
78
  content: [{ type: "text", text: lines.join("\n") }],
79
79
  details: {
80
- readseekValue: {
80
+ readSeekValue: {
81
81
  tool: "hover",
82
82
  ok: true,
83
83
  path: filePath,
@@ -93,11 +93,7 @@ export async function executeHover(opts: ExecuteHoverOptions): Promise<any> {
93
93
 
94
94
  export function registerHoverTool(pi: ExtensionAPI) {
95
95
  registerReadSeekTool(pi, {
96
- policy: "read-only",
97
- pythonName: "hover",
98
- defaultExposure: "opt-in",
99
- }, {
100
- name: "hover",
96
+ name: "readSeek_hover",
101
97
  label: "Hover",
102
98
  description: HOVER_PROMPT_METADATA.description,
103
99
  promptSnippet: HOVER_PROMPT_METADATA.promptSnippet,
@@ -63,7 +63,7 @@ interface ReadOutputInput {
63
63
  interface ReadOutputResult {
64
64
  text: string;
65
65
  lines: ReadSeekLine[];
66
- readseekValue: {
66
+ readSeekValue: {
67
67
  tool: "read";
68
68
  path: string;
69
69
  range: {
@@ -141,7 +141,7 @@ export function buildReadOutput(input: ReadOutputInput): ReadOutputResult {
141
141
  text = `${warnings.map((warning) => warning.message).join("\n\n")}\n\n${text}`;
142
142
  }
143
143
 
144
- const readseekValue: ReadOutputResult["readseekValue"] = {
144
+ const readSeekValue: ReadOutputResult["readSeekValue"] = {
145
145
  tool: "read",
146
146
  path: input.path,
147
147
  range: {
@@ -160,7 +160,7 @@ export function buildReadOutput(input: ReadOutputInput): ReadOutputResult {
160
160
  };
161
161
 
162
162
  if (input.bundle) {
163
- readseekValue.bundle = {
163
+ readSeekValue.bundle = {
164
164
  mode: input.bundle.mode,
165
165
  applied: input.bundle.applied,
166
166
  localSupport: input.bundle.localSupport.map((item) => {
@@ -181,6 +181,6 @@ export function buildReadOutput(input: ReadOutputInput): ReadOutputResult {
181
181
  return {
182
182
  text,
183
183
  lines,
184
- readseekValue,
184
+ readSeekValue,
185
185
  };
186
186
  }