pi-readseek 0.4.12 → 0.4.13
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/index.ts +1 -1
- package/package.json +1 -1
- package/src/edit-output.ts +3 -3
- package/src/edit-syntax-validate.ts +5 -5
- package/src/edit.ts +38 -6
- package/src/file-map.ts +17 -0
- package/src/grep-budget.ts +3 -3
- package/src/grep-output.ts +8 -8
- package/src/grep-render-helpers.ts +3 -3
- package/src/grep-symbol-scope.ts +4 -4
- package/src/grep.ts +7 -7
- package/src/hashline.ts +5 -5
- package/src/pending-diff-preview.ts +3 -3
- package/src/read-output.ts +21 -21
- package/src/read-render-helpers.ts +2 -2
- package/src/read.ts +17 -17
- package/src/readseek/constants.ts +0 -4
- package/src/readseek/formatter.ts +9 -74
- package/src/readseek/symbol-lookup.ts +1 -43
- package/src/readseek/types.ts +0 -20
- package/src/readseek-client.ts +68 -69
- package/src/readseek-settings.ts +17 -17
- package/src/readseek-value.ts +27 -27
- package/src/refs-output.ts +4 -4
- package/src/refs.ts +10 -10
- package/src/register-tool.ts +10 -10
- package/src/replace-symbol.ts +4 -3
- package/src/sg-output.ts +5 -5
- package/src/sg.ts +12 -12
- package/src/tool-prompt-metadata.ts +3 -3
- package/src/write.ts +18 -33
- package/src/map-cache.ts +0 -82
package/src/readseek-client.ts
CHANGED
|
@@ -8,23 +8,23 @@ import { DetailLevel } from "./readseek/enums.js";
|
|
|
8
8
|
import type { FileMap, FileSymbol } from "./readseek/types.js";
|
|
9
9
|
import { SymbolKind } from "./readseek/enums.js";
|
|
10
10
|
|
|
11
|
-
export interface
|
|
11
|
+
export interface ReadSeekHashline {
|
|
12
12
|
line: number;
|
|
13
13
|
hash: string;
|
|
14
14
|
text: string;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
interface ReadSeekReadOutput {
|
|
18
18
|
file: string;
|
|
19
19
|
language: string;
|
|
20
20
|
line_count: number;
|
|
21
21
|
file_hash: string;
|
|
22
22
|
start_line: number;
|
|
23
23
|
end_line: number;
|
|
24
|
-
hashlines:
|
|
24
|
+
hashlines: ReadSeekHashline[];
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
interface
|
|
27
|
+
interface ReadSeekSymbol {
|
|
28
28
|
kind: string;
|
|
29
29
|
name: string;
|
|
30
30
|
qualified_name: string;
|
|
@@ -34,45 +34,45 @@ interface ReadseekSymbol {
|
|
|
34
34
|
end_hash: string;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
interface
|
|
37
|
+
interface ReadSeekMapOutput {
|
|
38
38
|
file: string;
|
|
39
39
|
language: string;
|
|
40
40
|
line_count: number;
|
|
41
41
|
file_hash: string;
|
|
42
|
-
symbols:
|
|
42
|
+
symbols: ReadSeekSymbol[];
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
interface ReadSeekSearchCapture {
|
|
46
46
|
name: string;
|
|
47
47
|
start_line: number;
|
|
48
48
|
end_line: number;
|
|
49
49
|
start_hash: string;
|
|
50
50
|
end_hash: string;
|
|
51
|
-
hashlines:
|
|
51
|
+
hashlines: ReadSeekHashline[];
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
interface ReadSeekSearchMatch {
|
|
55
55
|
pattern_index: number;
|
|
56
56
|
start_line: number;
|
|
57
57
|
end_line: number;
|
|
58
58
|
start_hash: string;
|
|
59
59
|
end_hash: string;
|
|
60
|
-
hashlines:
|
|
61
|
-
captures:
|
|
60
|
+
hashlines: ReadSeekHashline[];
|
|
61
|
+
captures: ReadSeekSearchCapture[];
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
export interface
|
|
64
|
+
export interface ReadSeekSearchFileOutput {
|
|
65
65
|
file: string;
|
|
66
66
|
language: string;
|
|
67
67
|
file_hash: string;
|
|
68
|
-
matches:
|
|
68
|
+
matches: ReadSeekSearchMatch[];
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
interface
|
|
72
|
-
results:
|
|
71
|
+
interface ReadSeekSearchOutput {
|
|
72
|
+
results: ReadSeekSearchFileOutput[];
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
export interface
|
|
75
|
+
export interface ReadSeekReference {
|
|
76
76
|
file: string;
|
|
77
77
|
line: number;
|
|
78
78
|
column: number;
|
|
@@ -81,11 +81,11 @@ export interface ReadseekReference {
|
|
|
81
81
|
enclosingSymbol?: string;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
interface
|
|
85
|
-
references:
|
|
84
|
+
interface ReadSeekRefsOutput {
|
|
85
|
+
references: ReadSeekReference[];
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
interface ReadSeekRefsOptions {
|
|
89
89
|
scope?: boolean;
|
|
90
90
|
line?: number;
|
|
91
91
|
column?: number;
|
|
@@ -96,19 +96,19 @@ export interface ReadseekRefsOptions {
|
|
|
96
96
|
signal?: AbortSignal;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
export interface
|
|
99
|
+
export interface ReadSeekDiagnostic {
|
|
100
100
|
kind: "error" | "missing";
|
|
101
101
|
start_line: number;
|
|
102
102
|
end_line: number;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
export interface
|
|
105
|
+
export interface ReadSeekCheckOutput {
|
|
106
106
|
errorCount: number;
|
|
107
107
|
missingCount: number;
|
|
108
|
-
diagnostics:
|
|
108
|
+
diagnostics: ReadSeekDiagnostic[];
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
interface ReadSeekSearchOptions {
|
|
112
112
|
language?: string;
|
|
113
113
|
cached?: boolean;
|
|
114
114
|
others?: boolean;
|
|
@@ -131,7 +131,7 @@ function parentQualifiedNameFor(qualifiedName: string): string {
|
|
|
131
131
|
return lastDot === -1 ? "" : qualifiedName.slice(0, lastDot);
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
function
|
|
134
|
+
function symbolsFromReadSeek(symbols: ReadSeekSymbol[]): FileSymbol[] {
|
|
135
135
|
const symbolsByQualifiedName = new Map<string, FileSymbol[]>();
|
|
136
136
|
const entries: Array<{ parentQualifiedName: string; symbol: FileSymbol }> = [];
|
|
137
137
|
|
|
@@ -168,13 +168,13 @@ function symbolsFromReadseek(symbols: ReadseekSymbol[]): FileSymbol[] {
|
|
|
168
168
|
|
|
169
169
|
const require = createRequire(import.meta.url);
|
|
170
170
|
const READSEEK_REPO_PACKAGE_NAMES = new Set(["@jarkkojs/readseek", "readseek"]);
|
|
171
|
-
let
|
|
171
|
+
let defaultReadSeekDirInit: Promise<string | null> | undefined;
|
|
172
172
|
|
|
173
173
|
function readseekPackageDir(): string {
|
|
174
174
|
return path.dirname(require.resolve("@jarkkojs/readseek/package.json"));
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
|
|
177
|
+
function readseekBinaryPath(): string {
|
|
178
178
|
if (process.env.READSEEK_BIN) return process.env.READSEEK_BIN;
|
|
179
179
|
|
|
180
180
|
const platformPackage = (() => {
|
|
@@ -194,7 +194,7 @@ export function readseekBinaryPath(): string {
|
|
|
194
194
|
return path.join(path.dirname(packageJson), "bin", process.platform === "win32" ? "readseek.exe" : "readseek");
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
-
export function
|
|
197
|
+
export function isReadSeekAvailable(): boolean {
|
|
198
198
|
try {
|
|
199
199
|
readseekBinaryPath();
|
|
200
200
|
return true;
|
|
@@ -203,7 +203,7 @@ export function isReadseekAvailable(): boolean {
|
|
|
203
203
|
}
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
|
|
206
|
+
interface ReadSeekFailure {
|
|
207
207
|
code: "readseek-not-installed" | "readseek-execution-error";
|
|
208
208
|
message: string;
|
|
209
209
|
hint?: string;
|
|
@@ -214,7 +214,7 @@ export interface ReadseekFailure {
|
|
|
214
214
|
* taxonomy: a missing binary or package (`readseek-not-installed`, with an
|
|
215
215
|
* install hint) versus any other execution error.
|
|
216
216
|
*/
|
|
217
|
-
export function
|
|
217
|
+
export function classifyReadSeekFailure(err: unknown): ReadSeekFailure {
|
|
218
218
|
const message = String((err as { message?: unknown } | null)?.message || err);
|
|
219
219
|
const missing =
|
|
220
220
|
(err as { code?: unknown } | null)?.code === "ENOENT" ||
|
|
@@ -233,7 +233,7 @@ function directoryExists(dirPath: string): boolean {
|
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
-
function
|
|
236
|
+
function isOwnReadSeekRepository(cwd = process.cwd()): boolean {
|
|
237
237
|
let dir = path.resolve(cwd);
|
|
238
238
|
while (true) {
|
|
239
239
|
const packageJsonPath = path.join(dir, "package.json");
|
|
@@ -252,12 +252,12 @@ function isOwnReadseekRepository(cwd = process.cwd()): boolean {
|
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
-
function
|
|
255
|
+
function defaultReadSeekDir(): string | null {
|
|
256
256
|
const home = homedir();
|
|
257
257
|
return home ? path.join(home, ".pi", "readseek") : null;
|
|
258
258
|
}
|
|
259
259
|
|
|
260
|
-
async function
|
|
260
|
+
async function spawnReadSeekRaw(args: string[], options: RunReadSeekOptions = {}): Promise<string> {
|
|
261
261
|
return new Promise<string>((resolve, reject) => {
|
|
262
262
|
let settled = false;
|
|
263
263
|
const fail = (error: Error): void => {
|
|
@@ -318,38 +318,38 @@ async function spawnReadseekRaw(args: string[], options: RunReadseekOptions = {}
|
|
|
318
318
|
});
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
-
async function
|
|
322
|
-
const dir =
|
|
321
|
+
async function ensureDefaultReadSeekDir(): Promise<string | null> {
|
|
322
|
+
const dir = defaultReadSeekDir();
|
|
323
323
|
if (!dir) return null;
|
|
324
324
|
if (directoryExists(dir)) return dir;
|
|
325
325
|
|
|
326
|
-
|
|
326
|
+
defaultReadSeekDirInit ??= spawnReadSeekRaw(["--readseek-dir", dir, "init"])
|
|
327
327
|
.then(() => (directoryExists(dir) ? dir : null))
|
|
328
328
|
.catch(() => null)
|
|
329
329
|
.finally(() => {
|
|
330
|
-
|
|
330
|
+
defaultReadSeekDirInit = undefined;
|
|
331
331
|
});
|
|
332
|
-
return
|
|
332
|
+
return defaultReadSeekDirInit;
|
|
333
333
|
}
|
|
334
334
|
|
|
335
335
|
async function readseekInvocationArgs(args: string[]): Promise<string[]> {
|
|
336
|
-
if (
|
|
336
|
+
if (isOwnReadSeekRepository()) return args;
|
|
337
337
|
|
|
338
|
-
const readseekDir = await
|
|
338
|
+
const readseekDir = await ensureDefaultReadSeekDir();
|
|
339
339
|
return readseekDir ? ["--readseek-dir", readseekDir, ...args] : args;
|
|
340
340
|
}
|
|
341
341
|
|
|
342
|
-
interface
|
|
342
|
+
interface RunReadSeekOptions {
|
|
343
343
|
signal?: AbortSignal;
|
|
344
344
|
stdin?: string;
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
-
async function
|
|
348
|
-
return
|
|
347
|
+
async function runReadSeekRaw(args: string[], options: RunReadSeekOptions = {}): Promise<string> {
|
|
348
|
+
return spawnReadSeekRaw(await readseekInvocationArgs(args), options);
|
|
349
349
|
}
|
|
350
350
|
|
|
351
|
-
async function
|
|
352
|
-
const stdout = await
|
|
351
|
+
async function runReadSeek(args: string[], options: RunReadSeekOptions = {}): Promise<unknown> {
|
|
352
|
+
const stdout = await runReadSeekRaw(args, options);
|
|
353
353
|
return JSON.parse(stdout) as unknown;
|
|
354
354
|
}
|
|
355
355
|
|
|
@@ -363,7 +363,7 @@ function requireString(value: unknown, field: string): string {
|
|
|
363
363
|
return value;
|
|
364
364
|
}
|
|
365
365
|
|
|
366
|
-
function parseReadOutput(value: unknown):
|
|
366
|
+
function parseReadOutput(value: unknown): ReadSeekReadOutput {
|
|
367
367
|
if (!value || typeof value !== "object") throw new Error("invalid readseek read output");
|
|
368
368
|
const output = value as Record<string, unknown>;
|
|
369
369
|
const hashlines = output.hashlines;
|
|
@@ -379,7 +379,7 @@ function parseReadOutput(value: unknown): ReadseekReadOutput {
|
|
|
379
379
|
};
|
|
380
380
|
}
|
|
381
381
|
|
|
382
|
-
function parseMapOutput(value: unknown):
|
|
382
|
+
function parseMapOutput(value: unknown): ReadSeekMapOutput {
|
|
383
383
|
if (!value || typeof value !== "object") throw new Error("invalid readseek map output");
|
|
384
384
|
const output = value as Record<string, unknown>;
|
|
385
385
|
const symbols = output.symbols;
|
|
@@ -405,7 +405,7 @@ function parseMapOutput(value: unknown): ReadseekMapOutput {
|
|
|
405
405
|
};
|
|
406
406
|
}
|
|
407
407
|
|
|
408
|
-
function parseHashline(value: unknown, field: string):
|
|
408
|
+
function parseHashline(value: unknown, field: string): ReadSeekHashline {
|
|
409
409
|
if (!value || typeof value !== "object") throw new Error(`invalid readseek ${field}`);
|
|
410
410
|
const item = value as Record<string, unknown>;
|
|
411
411
|
return {
|
|
@@ -415,12 +415,12 @@ function parseHashline(value: unknown, field: string): ReadseekHashline {
|
|
|
415
415
|
};
|
|
416
416
|
}
|
|
417
417
|
|
|
418
|
-
function parseSearchHashlines(value: unknown, field: string):
|
|
418
|
+
function parseSearchHashlines(value: unknown, field: string): ReadSeekHashline[] {
|
|
419
419
|
if (!Array.isArray(value)) throw new Error(`invalid readseek ${field}`);
|
|
420
420
|
return value.map((line) => parseHashline(line, field));
|
|
421
421
|
}
|
|
422
422
|
|
|
423
|
-
function parseSearchOutput(value: unknown):
|
|
423
|
+
function parseSearchOutput(value: unknown): ReadSeekSearchOutput {
|
|
424
424
|
if (!value || typeof value !== "object") throw new Error("invalid readseek search output");
|
|
425
425
|
const output = value as Record<string, unknown>;
|
|
426
426
|
if (!Array.isArray(output.results)) throw new Error("invalid readseek search results");
|
|
@@ -463,14 +463,14 @@ function parseSearchOutput(value: unknown): ReadseekSearchOutput {
|
|
|
463
463
|
};
|
|
464
464
|
}
|
|
465
465
|
|
|
466
|
-
export async function readseekRead(filePath: string, startLine?: number, endLine?: number): Promise<
|
|
466
|
+
export async function readseekRead(filePath: string, startLine?: number, endLine?: number): Promise<ReadSeekReadOutput> {
|
|
467
467
|
const args = ["read", filePath];
|
|
468
468
|
if (startLine !== undefined) args.push("--start", String(startLine));
|
|
469
469
|
if (endLine !== undefined) args.push("--end", String(endLine));
|
|
470
|
-
return parseReadOutput(await
|
|
470
|
+
return parseReadOutput(await runReadSeek(args));
|
|
471
471
|
}
|
|
472
472
|
|
|
473
|
-
function
|
|
473
|
+
function fileMapFromReadSeekOutput(output: ReadSeekMapOutput, filePath: string, totalBytes: number): FileMap | null {
|
|
474
474
|
if (output.language === "unknown" && output.symbols.length === 0) return null;
|
|
475
475
|
return {
|
|
476
476
|
path: filePath,
|
|
@@ -478,8 +478,7 @@ function fileMapFromReadseekOutput(output: ReadseekMapOutput, filePath: string,
|
|
|
478
478
|
totalBytes,
|
|
479
479
|
language: normalizeLanguage(output.language),
|
|
480
480
|
detailLevel: DetailLevel.Full,
|
|
481
|
-
|
|
482
|
-
symbols: symbolsFromReadseek(output.symbols),
|
|
481
|
+
symbols: symbolsFromReadSeek(output.symbols),
|
|
483
482
|
};
|
|
484
483
|
}
|
|
485
484
|
|
|
@@ -488,21 +487,21 @@ export async function readseekMap(
|
|
|
488
487
|
totalBytes: number,
|
|
489
488
|
options: { signal?: AbortSignal } = {},
|
|
490
489
|
): Promise<FileMap | null> {
|
|
491
|
-
const output = parseMapOutput(await
|
|
492
|
-
return
|
|
490
|
+
const output = parseMapOutput(await runReadSeek(["map", filePath], { signal: options.signal }));
|
|
491
|
+
return fileMapFromReadSeekOutput(output, filePath, totalBytes);
|
|
493
492
|
}
|
|
494
493
|
|
|
495
494
|
export async function readseekSearch(
|
|
496
495
|
target: string,
|
|
497
496
|
pattern: string,
|
|
498
|
-
options:
|
|
499
|
-
): Promise<
|
|
497
|
+
options: ReadSeekSearchOptions = {},
|
|
498
|
+
): Promise<ReadSeekSearchFileOutput[]> {
|
|
500
499
|
const args = ["search", target, pattern];
|
|
501
500
|
if (options.language) args.push("--language", options.language);
|
|
502
501
|
if (options.cached) args.push("--cached");
|
|
503
502
|
if (options.others) args.push("--others");
|
|
504
503
|
if (options.ignored) args.push("--ignored");
|
|
505
|
-
return parseSearchOutput(await
|
|
504
|
+
return parseSearchOutput(await runReadSeek(args, { signal: options.signal })).results;
|
|
506
505
|
}
|
|
507
506
|
|
|
508
507
|
export async function readseekMapContent(
|
|
@@ -511,9 +510,9 @@ export async function readseekMapContent(
|
|
|
511
510
|
options: { signal?: AbortSignal } = {},
|
|
512
511
|
): Promise<FileMap | null> {
|
|
513
512
|
const output = parseMapOutput(
|
|
514
|
-
await
|
|
513
|
+
await runReadSeek(["map", "--stdin", filePath], { signal: options.signal, stdin: content }),
|
|
515
514
|
);
|
|
516
|
-
return
|
|
515
|
+
return fileMapFromReadSeekOutput(output, filePath, Buffer.byteLength(content, "utf8"));
|
|
517
516
|
}
|
|
518
517
|
|
|
519
518
|
function optionalString(value: unknown, field: string): string | undefined {
|
|
@@ -521,7 +520,7 @@ function optionalString(value: unknown, field: string): string | undefined {
|
|
|
521
520
|
return requireString(value, field);
|
|
522
521
|
}
|
|
523
522
|
|
|
524
|
-
function parseRefsOutput(value: unknown):
|
|
523
|
+
function parseRefsOutput(value: unknown): ReadSeekRefsOutput {
|
|
525
524
|
if (!value || typeof value !== "object") throw new Error("invalid readseek refs output");
|
|
526
525
|
const output = value as Record<string, unknown>;
|
|
527
526
|
if (!Array.isArray(output.references)) throw new Error("invalid readseek references");
|
|
@@ -549,8 +548,8 @@ function parseRefsOutput(value: unknown): ReadseekRefsOutput {
|
|
|
549
548
|
export async function readseekRefs(
|
|
550
549
|
target: string,
|
|
551
550
|
name: string,
|
|
552
|
-
options:
|
|
553
|
-
): Promise<
|
|
551
|
+
options: ReadSeekRefsOptions = {},
|
|
552
|
+
): Promise<ReadSeekReference[]> {
|
|
554
553
|
const args = ["refs", target, name];
|
|
555
554
|
if (options.scope) args.push("--scope");
|
|
556
555
|
if (options.line !== undefined) args.push("--line", String(options.line));
|
|
@@ -559,15 +558,15 @@ export async function readseekRefs(
|
|
|
559
558
|
if (options.cached) args.push("--cached");
|
|
560
559
|
if (options.others) args.push("--others");
|
|
561
560
|
if (options.ignored) args.push("--ignored");
|
|
562
|
-
return parseRefsOutput(await
|
|
561
|
+
return parseRefsOutput(await runReadSeek(args, { signal: options.signal })).references;
|
|
563
562
|
}
|
|
564
563
|
|
|
565
|
-
function parseDiagnosticKind(value: unknown):
|
|
564
|
+
function parseDiagnosticKind(value: unknown): ReadSeekDiagnostic["kind"] {
|
|
566
565
|
if (value === "error" || value === "missing") return value;
|
|
567
566
|
throw new Error("invalid readseek diagnostic.kind");
|
|
568
567
|
}
|
|
569
568
|
|
|
570
|
-
function parseCheckOutput(value: unknown):
|
|
569
|
+
function parseCheckOutput(value: unknown): ReadSeekCheckOutput {
|
|
571
570
|
if (!value || typeof value !== "object") throw new Error("invalid readseek check output");
|
|
572
571
|
const output = value as Record<string, unknown>;
|
|
573
572
|
if (!Array.isArray(output.diagnostics)) throw new Error("invalid readseek diagnostics");
|
|
@@ -590,8 +589,8 @@ export async function readseekCheck(
|
|
|
590
589
|
filePath: string,
|
|
591
590
|
content: string,
|
|
592
591
|
options: { signal?: AbortSignal } = {},
|
|
593
|
-
): Promise<
|
|
592
|
+
): Promise<ReadSeekCheckOutput> {
|
|
594
593
|
return parseCheckOutput(
|
|
595
|
-
await
|
|
594
|
+
await runReadSeek(["check", "--stdin", filePath], { signal: options.signal, stdin: content }),
|
|
596
595
|
);
|
|
597
596
|
}
|
package/src/readseek-settings.ts
CHANGED
|
@@ -2,20 +2,20 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
interface ReadSeekJsonSettings {
|
|
6
6
|
grep?: { maxLines?: number; maxBytes?: number };
|
|
7
7
|
edit?: { diffDisplay?: "collapsed" | "expanded" };
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
interface ReadSeekSettingsWarning {
|
|
11
11
|
source: string;
|
|
12
12
|
message: string;
|
|
13
13
|
path?: string;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
settings:
|
|
18
|
-
warnings:
|
|
16
|
+
interface ReadSeekSettingsResult {
|
|
17
|
+
settings: ReadSeekJsonSettings;
|
|
18
|
+
warnings: ReadSeekSettingsWarning[];
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
|
|
@@ -31,7 +31,7 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
31
31
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
function invalid(source: string, path: string):
|
|
34
|
+
function invalid(source: string, path: string): ReadSeekSettingsWarning {
|
|
35
35
|
return { source, path, message: `Invalid readseek setting at ${path}` };
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -40,7 +40,7 @@ function readPositive(
|
|
|
40
40
|
key: string,
|
|
41
41
|
path: string,
|
|
42
42
|
source: string,
|
|
43
|
-
warnings:
|
|
43
|
+
warnings: ReadSeekSettingsWarning[],
|
|
44
44
|
): number | undefined {
|
|
45
45
|
if (!(key in raw)) return undefined;
|
|
46
46
|
const val = raw[key];
|
|
@@ -50,13 +50,13 @@ function readPositive(
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
function validateSettings(raw: unknown, source: string):
|
|
54
|
-
const settings:
|
|
55
|
-
const warnings:
|
|
53
|
+
function validateSettings(raw: unknown, source: string): ReadSeekSettingsResult {
|
|
54
|
+
const settings: ReadSeekJsonSettings = {};
|
|
55
|
+
const warnings: ReadSeekSettingsWarning[] = [];
|
|
56
56
|
if (!isRecord(raw)) return { settings, warnings };
|
|
57
57
|
|
|
58
58
|
if (isRecord(raw.grep)) {
|
|
59
|
-
const grep: NonNullable<
|
|
59
|
+
const grep: NonNullable<ReadSeekJsonSettings["grep"]> = {};
|
|
60
60
|
const maxLines = readPositive(raw.grep, "maxLines", "grep.maxLines", source, warnings);
|
|
61
61
|
if (maxLines !== undefined) grep.maxLines = maxLines;
|
|
62
62
|
const maxBytes = readPositive(raw.grep, "maxBytes", "grep.maxBytes", source, warnings);
|
|
@@ -66,7 +66,7 @@ function validateSettings(raw: unknown, source: string): ReadseekSettingsResult
|
|
|
66
66
|
|
|
67
67
|
|
|
68
68
|
if (isRecord(raw.edit)) {
|
|
69
|
-
const edit: NonNullable<
|
|
69
|
+
const edit: NonNullable<ReadSeekJsonSettings["edit"]> = {};
|
|
70
70
|
if ("diffDisplay" in raw.edit) {
|
|
71
71
|
const value = raw.edit.diffDisplay;
|
|
72
72
|
if (value === "collapsed" || value === "expanded") edit.diffDisplay = value;
|
|
@@ -78,7 +78,7 @@ function validateSettings(raw: unknown, source: string): ReadseekSettingsResult
|
|
|
78
78
|
return { settings, warnings };
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
function readSettingsFile(path: string):
|
|
81
|
+
function readSettingsFile(path: string): ReadSeekSettingsResult {
|
|
82
82
|
if (!existsSync(path)) return { settings: {}, warnings: [] };
|
|
83
83
|
|
|
84
84
|
try {
|
|
@@ -90,8 +90,8 @@ function readSettingsFile(path: string): ReadseekSettingsResult {
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
function mergeSettings(base:
|
|
94
|
-
const merged:
|
|
93
|
+
function mergeSettings(base: ReadSeekJsonSettings, override: ReadSeekJsonSettings): ReadSeekJsonSettings {
|
|
94
|
+
const merged: ReadSeekJsonSettings = {};
|
|
95
95
|
const grep = { ...(base.grep ?? {}), ...(override.grep ?? {}) };
|
|
96
96
|
if (Object.keys(grep).length > 0) merged.grep = grep;
|
|
97
97
|
const edit = { ...(base.edit ?? {}), ...(override.edit ?? {}) };
|
|
@@ -99,7 +99,7 @@ function mergeSettings(base: ReadseekJsonSettings, override: ReadseekJsonSetting
|
|
|
99
99
|
return merged;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
export function
|
|
102
|
+
export function resolveReadSeekJsonSettings(): ReadSeekSettingsResult {
|
|
103
103
|
const globalResult = readSettingsFile(defaultGlobalSettingsPath());
|
|
104
104
|
const projectResult = readSettingsFile(defaultProjectSettingsPath());
|
|
105
105
|
return {
|
|
@@ -114,7 +114,7 @@ export function resolveEditDiffDisplay(env: NodeJS.ProcessEnv = process.env): "c
|
|
|
114
114
|
const normalized = raw.trim().toLowerCase();
|
|
115
115
|
if (normalized === "expanded" || normalized === "collapsed") return normalized;
|
|
116
116
|
}
|
|
117
|
-
const json =
|
|
117
|
+
const json = resolveReadSeekJsonSettings().settings.edit?.diffDisplay;
|
|
118
118
|
if (json === "expanded" || json === "collapsed") return json;
|
|
119
119
|
return "collapsed";
|
|
120
120
|
}
|
package/src/readseek-value.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { computeLineHash, escapeControlCharsForDisplay } from "./hashline.js";
|
|
2
2
|
import type { DiffData } from "./diff-data.js";
|
|
3
3
|
|
|
4
|
-
export interface
|
|
4
|
+
export interface ReadSeekLine {
|
|
5
5
|
line: number;
|
|
6
6
|
hash: string;
|
|
7
7
|
anchor: string;
|
|
@@ -9,29 +9,29 @@ export interface ReadseekLine {
|
|
|
9
9
|
display: string;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export interface
|
|
12
|
+
export interface ReadSeekWarningSymbol {
|
|
13
13
|
name: string;
|
|
14
14
|
kind: string;
|
|
15
15
|
startLine: number;
|
|
16
16
|
endLine: number;
|
|
17
17
|
parentName?: string;
|
|
18
18
|
}
|
|
19
|
-
export interface
|
|
19
|
+
export interface ReadSeekWarning {
|
|
20
20
|
code: string;
|
|
21
21
|
message: string;
|
|
22
22
|
tier?: "camelCase" | "substring";
|
|
23
|
-
symbol?:
|
|
24
|
-
otherCandidates?:
|
|
23
|
+
symbol?: ReadSeekWarningSymbol;
|
|
24
|
+
otherCandidates?: ReadSeekWarningSymbol[];
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export interface
|
|
27
|
+
export interface ReadSeekError {
|
|
28
28
|
code: string;
|
|
29
29
|
message: string;
|
|
30
30
|
hint?: string;
|
|
31
31
|
details?: unknown;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
export interface
|
|
34
|
+
export interface ReadSeekRange {
|
|
35
35
|
startLine: number;
|
|
36
36
|
endLine: number;
|
|
37
37
|
totalLines?: number;
|
|
@@ -42,7 +42,7 @@ export interface SemanticSummary {
|
|
|
42
42
|
difftasticAvailable: boolean;
|
|
43
43
|
movedBlocks?: number;
|
|
44
44
|
}
|
|
45
|
-
export interface
|
|
45
|
+
export interface ReadSeekEditResult {
|
|
46
46
|
tool: "edit";
|
|
47
47
|
ok: boolean;
|
|
48
48
|
path: string;
|
|
@@ -56,11 +56,11 @@ export interface ReadseekEditResult {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
|
-
* Build a {@link
|
|
59
|
+
* Build a {@link ReadSeekLine} from an already-known hash. Use this when the
|
|
60
60
|
* hash is supplied by readseek (search, refs) rather than computed from `raw`;
|
|
61
|
-
* {@link
|
|
61
|
+
* {@link buildReadSeekLine} delegates here after hashing.
|
|
62
62
|
*/
|
|
63
|
-
export function
|
|
63
|
+
export function buildReadSeekLineWithHash(line: number, hash: string, raw: string): ReadSeekLine {
|
|
64
64
|
return {
|
|
65
65
|
line,
|
|
66
66
|
hash,
|
|
@@ -70,36 +70,36 @@ export function buildReadseekLineWithHash(line: number, hash: string, raw: strin
|
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
export function
|
|
74
|
-
return
|
|
73
|
+
export function buildReadSeekLine(line: number, raw: string): ReadSeekLine {
|
|
74
|
+
return buildReadSeekLineWithHash(line, computeLineHash(raw), raw);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
export function
|
|
78
|
-
return rawLines.map((raw, index) =>
|
|
77
|
+
export function buildReadSeekLines(startLine: number, rawLines: string[]): ReadSeekLine[] {
|
|
78
|
+
return rawLines.map((raw, index) => buildReadSeekLine(startLine + index, raw));
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
function
|
|
81
|
+
function renderReadSeekLine(line: ReadSeekLine): string {
|
|
82
82
|
return `${line.anchor}|${line.display}`;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
export function
|
|
86
|
-
return lines.map(
|
|
85
|
+
export function renderReadSeekLines(lines: ReadSeekLine[]): string {
|
|
86
|
+
return lines.map(renderReadSeekLine).join("\n");
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
export function
|
|
89
|
+
export function buildReadSeekWarning(
|
|
90
90
|
code: string,
|
|
91
91
|
message: string,
|
|
92
|
-
metadata: Omit<
|
|
93
|
-
):
|
|
92
|
+
metadata: Omit<ReadSeekWarning, "code" | "message"> = {},
|
|
93
|
+
): ReadSeekWarning {
|
|
94
94
|
return { code, message, ...metadata };
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
export function
|
|
97
|
+
export function buildReadSeekError(
|
|
98
98
|
code: string,
|
|
99
99
|
message: string,
|
|
100
100
|
hint?: string,
|
|
101
101
|
details?: unknown,
|
|
102
|
-
):
|
|
102
|
+
): ReadSeekError {
|
|
103
103
|
return {
|
|
104
104
|
code,
|
|
105
105
|
message,
|
|
@@ -117,7 +117,7 @@ export interface ToolErrorResult {
|
|
|
117
117
|
/**
|
|
118
118
|
* Build the standard failure envelope shared by every read-family tool: a text
|
|
119
119
|
* content block plus a `readseekValue` carrying `ok: false` and a
|
|
120
|
-
* {@link
|
|
120
|
+
* {@link buildReadSeekError} payload.
|
|
121
121
|
*
|
|
122
122
|
* `path` is included only when provided, and `extra` is merged into
|
|
123
123
|
* `readseekValue` so callers can attach tool-specific fields (e.g. write's
|
|
@@ -138,13 +138,13 @@ export function buildToolErrorResult(
|
|
|
138
138
|
...(opts.extra ?? {}),
|
|
139
139
|
ok: false,
|
|
140
140
|
...(opts.path !== undefined ? { path: opts.path } : {}),
|
|
141
|
-
error:
|
|
141
|
+
error: buildReadSeekError(code, message, opts.hint, opts.details),
|
|
142
142
|
},
|
|
143
143
|
},
|
|
144
144
|
};
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
export function
|
|
147
|
+
export function buildReadSeekEditResult(input: {
|
|
148
148
|
ok?: boolean;
|
|
149
149
|
path: string;
|
|
150
150
|
summary: string;
|
|
@@ -154,7 +154,7 @@ export function buildReadseekEditResult(input: {
|
|
|
154
154
|
warnings: string[];
|
|
155
155
|
noopEdits: unknown[];
|
|
156
156
|
semanticSummary?: SemanticSummary;
|
|
157
|
-
}):
|
|
157
|
+
}): ReadSeekEditResult {
|
|
158
158
|
return {
|
|
159
159
|
tool: "edit",
|
|
160
160
|
ok: input.ok ?? true,
|
package/src/refs-output.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ReadSeekLine } from "./readseek-value.js";
|
|
2
2
|
|
|
3
|
-
export interface RefsOutputLine extends
|
|
3
|
+
export interface RefsOutputLine extends ReadSeekLine {
|
|
4
4
|
enclosingSymbol?: string;
|
|
5
5
|
}
|
|
6
6
|
|
|
@@ -10,12 +10,12 @@ export interface RefsOutputFile {
|
|
|
10
10
|
lines: RefsOutputLine[];
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
interface BuildRefsOutputInput {
|
|
14
14
|
name: string;
|
|
15
15
|
files: RefsOutputFile[];
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
interface RefsOutputResult {
|
|
19
19
|
text: string;
|
|
20
20
|
readseekValue: {
|
|
21
21
|
tool: "refs";
|