pi-readseek 0.4.12 → 0.4.14
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 +2 -2
- 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 +43 -31
- 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 +165 -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 +5 -5
- package/src/write.ts +18 -33
- package/src/image-detect.ts +0 -47
- 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,48 @@ 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
|
-
export interface
|
|
111
|
+
export interface ReadSeekImageInfo {
|
|
112
|
+
format: string;
|
|
113
|
+
width: number;
|
|
114
|
+
height: number;
|
|
115
|
+
animated: boolean;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface ReadSeekOcrLine {
|
|
119
|
+
text: string;
|
|
120
|
+
bbox: [number, number, number, number];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface ReadSeekOcrText {
|
|
124
|
+
text: string;
|
|
125
|
+
lines: ReadSeekOcrLine[];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface ReadSeekDetection {
|
|
129
|
+
file: string;
|
|
130
|
+
language: string;
|
|
131
|
+
engine: string | null;
|
|
132
|
+
supported: boolean;
|
|
133
|
+
binary: boolean;
|
|
134
|
+
mime: string | null;
|
|
135
|
+
syntax: string | null;
|
|
136
|
+
image: ReadSeekImageInfo | null;
|
|
137
|
+
ocr?: ReadSeekOcrText;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
interface ReadSeekSearchOptions {
|
|
112
141
|
language?: string;
|
|
113
142
|
cached?: boolean;
|
|
114
143
|
others?: boolean;
|
|
@@ -131,7 +160,7 @@ function parentQualifiedNameFor(qualifiedName: string): string {
|
|
|
131
160
|
return lastDot === -1 ? "" : qualifiedName.slice(0, lastDot);
|
|
132
161
|
}
|
|
133
162
|
|
|
134
|
-
function
|
|
163
|
+
function symbolsFromReadSeek(symbols: ReadSeekSymbol[]): FileSymbol[] {
|
|
135
164
|
const symbolsByQualifiedName = new Map<string, FileSymbol[]>();
|
|
136
165
|
const entries: Array<{ parentQualifiedName: string; symbol: FileSymbol }> = [];
|
|
137
166
|
|
|
@@ -168,13 +197,13 @@ function symbolsFromReadseek(symbols: ReadseekSymbol[]): FileSymbol[] {
|
|
|
168
197
|
|
|
169
198
|
const require = createRequire(import.meta.url);
|
|
170
199
|
const READSEEK_REPO_PACKAGE_NAMES = new Set(["@jarkkojs/readseek", "readseek"]);
|
|
171
|
-
let
|
|
200
|
+
let defaultReadSeekDirInit: Promise<string | null> | undefined;
|
|
172
201
|
|
|
173
202
|
function readseekPackageDir(): string {
|
|
174
203
|
return path.dirname(require.resolve("@jarkkojs/readseek/package.json"));
|
|
175
204
|
}
|
|
176
205
|
|
|
177
|
-
|
|
206
|
+
function readseekBinaryPath(): string {
|
|
178
207
|
if (process.env.READSEEK_BIN) return process.env.READSEEK_BIN;
|
|
179
208
|
|
|
180
209
|
const platformPackage = (() => {
|
|
@@ -194,7 +223,7 @@ export function readseekBinaryPath(): string {
|
|
|
194
223
|
return path.join(path.dirname(packageJson), "bin", process.platform === "win32" ? "readseek.exe" : "readseek");
|
|
195
224
|
}
|
|
196
225
|
|
|
197
|
-
export function
|
|
226
|
+
export function isReadSeekAvailable(): boolean {
|
|
198
227
|
try {
|
|
199
228
|
readseekBinaryPath();
|
|
200
229
|
return true;
|
|
@@ -203,7 +232,7 @@ export function isReadseekAvailable(): boolean {
|
|
|
203
232
|
}
|
|
204
233
|
}
|
|
205
234
|
|
|
206
|
-
|
|
235
|
+
interface ReadSeekFailure {
|
|
207
236
|
code: "readseek-not-installed" | "readseek-execution-error";
|
|
208
237
|
message: string;
|
|
209
238
|
hint?: string;
|
|
@@ -214,7 +243,7 @@ export interface ReadseekFailure {
|
|
|
214
243
|
* taxonomy: a missing binary or package (`readseek-not-installed`, with an
|
|
215
244
|
* install hint) versus any other execution error.
|
|
216
245
|
*/
|
|
217
|
-
export function
|
|
246
|
+
export function classifyReadSeekFailure(err: unknown): ReadSeekFailure {
|
|
218
247
|
const message = String((err as { message?: unknown } | null)?.message || err);
|
|
219
248
|
const missing =
|
|
220
249
|
(err as { code?: unknown } | null)?.code === "ENOENT" ||
|
|
@@ -233,7 +262,7 @@ function directoryExists(dirPath: string): boolean {
|
|
|
233
262
|
}
|
|
234
263
|
}
|
|
235
264
|
|
|
236
|
-
function
|
|
265
|
+
function isOwnReadSeekRepository(cwd = process.cwd()): boolean {
|
|
237
266
|
let dir = path.resolve(cwd);
|
|
238
267
|
while (true) {
|
|
239
268
|
const packageJsonPath = path.join(dir, "package.json");
|
|
@@ -252,12 +281,12 @@ function isOwnReadseekRepository(cwd = process.cwd()): boolean {
|
|
|
252
281
|
}
|
|
253
282
|
}
|
|
254
283
|
|
|
255
|
-
function
|
|
284
|
+
function defaultReadSeekDir(): string | null {
|
|
256
285
|
const home = homedir();
|
|
257
286
|
return home ? path.join(home, ".pi", "readseek") : null;
|
|
258
287
|
}
|
|
259
288
|
|
|
260
|
-
async function
|
|
289
|
+
async function spawnReadSeekRaw(args: string[], options: RunReadSeekOptions = {}): Promise<string> {
|
|
261
290
|
return new Promise<string>((resolve, reject) => {
|
|
262
291
|
let settled = false;
|
|
263
292
|
const fail = (error: Error): void => {
|
|
@@ -318,38 +347,38 @@ async function spawnReadseekRaw(args: string[], options: RunReadseekOptions = {}
|
|
|
318
347
|
});
|
|
319
348
|
}
|
|
320
349
|
|
|
321
|
-
async function
|
|
322
|
-
const dir =
|
|
350
|
+
async function ensureDefaultReadSeekDir(): Promise<string | null> {
|
|
351
|
+
const dir = defaultReadSeekDir();
|
|
323
352
|
if (!dir) return null;
|
|
324
353
|
if (directoryExists(dir)) return dir;
|
|
325
354
|
|
|
326
|
-
|
|
355
|
+
defaultReadSeekDirInit ??= spawnReadSeekRaw(["--readseek-dir", dir, "init"])
|
|
327
356
|
.then(() => (directoryExists(dir) ? dir : null))
|
|
328
357
|
.catch(() => null)
|
|
329
358
|
.finally(() => {
|
|
330
|
-
|
|
359
|
+
defaultReadSeekDirInit = undefined;
|
|
331
360
|
});
|
|
332
|
-
return
|
|
361
|
+
return defaultReadSeekDirInit;
|
|
333
362
|
}
|
|
334
363
|
|
|
335
364
|
async function readseekInvocationArgs(args: string[]): Promise<string[]> {
|
|
336
|
-
if (
|
|
365
|
+
if (isOwnReadSeekRepository()) return args;
|
|
337
366
|
|
|
338
|
-
const readseekDir = await
|
|
367
|
+
const readseekDir = await ensureDefaultReadSeekDir();
|
|
339
368
|
return readseekDir ? ["--readseek-dir", readseekDir, ...args] : args;
|
|
340
369
|
}
|
|
341
370
|
|
|
342
|
-
interface
|
|
371
|
+
interface RunReadSeekOptions {
|
|
343
372
|
signal?: AbortSignal;
|
|
344
373
|
stdin?: string;
|
|
345
374
|
}
|
|
346
375
|
|
|
347
|
-
async function
|
|
348
|
-
return
|
|
376
|
+
async function runReadSeekRaw(args: string[], options: RunReadSeekOptions = {}): Promise<string> {
|
|
377
|
+
return spawnReadSeekRaw(await readseekInvocationArgs(args), options);
|
|
349
378
|
}
|
|
350
379
|
|
|
351
|
-
async function
|
|
352
|
-
const stdout = await
|
|
380
|
+
async function runReadSeek(args: string[], options: RunReadSeekOptions = {}): Promise<unknown> {
|
|
381
|
+
const stdout = await runReadSeekRaw(args, options);
|
|
353
382
|
return JSON.parse(stdout) as unknown;
|
|
354
383
|
}
|
|
355
384
|
|
|
@@ -363,7 +392,12 @@ function requireString(value: unknown, field: string): string {
|
|
|
363
392
|
return value;
|
|
364
393
|
}
|
|
365
394
|
|
|
366
|
-
function
|
|
395
|
+
function requireBoolean(value: unknown, field: string): boolean {
|
|
396
|
+
if (typeof value !== "boolean") throw new Error(`invalid readseek ${field}: expected boolean`);
|
|
397
|
+
return value;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function parseReadOutput(value: unknown): ReadSeekReadOutput {
|
|
367
401
|
if (!value || typeof value !== "object") throw new Error("invalid readseek read output");
|
|
368
402
|
const output = value as Record<string, unknown>;
|
|
369
403
|
const hashlines = output.hashlines;
|
|
@@ -379,7 +413,7 @@ function parseReadOutput(value: unknown): ReadseekReadOutput {
|
|
|
379
413
|
};
|
|
380
414
|
}
|
|
381
415
|
|
|
382
|
-
function parseMapOutput(value: unknown):
|
|
416
|
+
function parseMapOutput(value: unknown): ReadSeekMapOutput {
|
|
383
417
|
if (!value || typeof value !== "object") throw new Error("invalid readseek map output");
|
|
384
418
|
const output = value as Record<string, unknown>;
|
|
385
419
|
const symbols = output.symbols;
|
|
@@ -405,7 +439,7 @@ function parseMapOutput(value: unknown): ReadseekMapOutput {
|
|
|
405
439
|
};
|
|
406
440
|
}
|
|
407
441
|
|
|
408
|
-
function parseHashline(value: unknown, field: string):
|
|
442
|
+
function parseHashline(value: unknown, field: string): ReadSeekHashline {
|
|
409
443
|
if (!value || typeof value !== "object") throw new Error(`invalid readseek ${field}`);
|
|
410
444
|
const item = value as Record<string, unknown>;
|
|
411
445
|
return {
|
|
@@ -415,12 +449,12 @@ function parseHashline(value: unknown, field: string): ReadseekHashline {
|
|
|
415
449
|
};
|
|
416
450
|
}
|
|
417
451
|
|
|
418
|
-
function parseSearchHashlines(value: unknown, field: string):
|
|
452
|
+
function parseSearchHashlines(value: unknown, field: string): ReadSeekHashline[] {
|
|
419
453
|
if (!Array.isArray(value)) throw new Error(`invalid readseek ${field}`);
|
|
420
454
|
return value.map((line) => parseHashline(line, field));
|
|
421
455
|
}
|
|
422
456
|
|
|
423
|
-
function parseSearchOutput(value: unknown):
|
|
457
|
+
function parseSearchOutput(value: unknown): ReadSeekSearchOutput {
|
|
424
458
|
if (!value || typeof value !== "object") throw new Error("invalid readseek search output");
|
|
425
459
|
const output = value as Record<string, unknown>;
|
|
426
460
|
if (!Array.isArray(output.results)) throw new Error("invalid readseek search results");
|
|
@@ -463,14 +497,14 @@ function parseSearchOutput(value: unknown): ReadseekSearchOutput {
|
|
|
463
497
|
};
|
|
464
498
|
}
|
|
465
499
|
|
|
466
|
-
export async function readseekRead(filePath: string, startLine?: number, endLine?: number): Promise<
|
|
500
|
+
export async function readseekRead(filePath: string, startLine?: number, endLine?: number): Promise<ReadSeekReadOutput> {
|
|
467
501
|
const args = ["read", filePath];
|
|
468
502
|
if (startLine !== undefined) args.push("--start", String(startLine));
|
|
469
503
|
if (endLine !== undefined) args.push("--end", String(endLine));
|
|
470
|
-
return parseReadOutput(await
|
|
504
|
+
return parseReadOutput(await runReadSeek(args));
|
|
471
505
|
}
|
|
472
506
|
|
|
473
|
-
function
|
|
507
|
+
function fileMapFromReadSeekOutput(output: ReadSeekMapOutput, filePath: string, totalBytes: number): FileMap | null {
|
|
474
508
|
if (output.language === "unknown" && output.symbols.length === 0) return null;
|
|
475
509
|
return {
|
|
476
510
|
path: filePath,
|
|
@@ -478,8 +512,7 @@ function fileMapFromReadseekOutput(output: ReadseekMapOutput, filePath: string,
|
|
|
478
512
|
totalBytes,
|
|
479
513
|
language: normalizeLanguage(output.language),
|
|
480
514
|
detailLevel: DetailLevel.Full,
|
|
481
|
-
|
|
482
|
-
symbols: symbolsFromReadseek(output.symbols),
|
|
515
|
+
symbols: symbolsFromReadSeek(output.symbols),
|
|
483
516
|
};
|
|
484
517
|
}
|
|
485
518
|
|
|
@@ -488,21 +521,21 @@ export async function readseekMap(
|
|
|
488
521
|
totalBytes: number,
|
|
489
522
|
options: { signal?: AbortSignal } = {},
|
|
490
523
|
): Promise<FileMap | null> {
|
|
491
|
-
const output = parseMapOutput(await
|
|
492
|
-
return
|
|
524
|
+
const output = parseMapOutput(await runReadSeek(["map", filePath], { signal: options.signal }));
|
|
525
|
+
return fileMapFromReadSeekOutput(output, filePath, totalBytes);
|
|
493
526
|
}
|
|
494
527
|
|
|
495
528
|
export async function readseekSearch(
|
|
496
529
|
target: string,
|
|
497
530
|
pattern: string,
|
|
498
|
-
options:
|
|
499
|
-
): Promise<
|
|
531
|
+
options: ReadSeekSearchOptions = {},
|
|
532
|
+
): Promise<ReadSeekSearchFileOutput[]> {
|
|
500
533
|
const args = ["search", target, pattern];
|
|
501
534
|
if (options.language) args.push("--language", options.language);
|
|
502
535
|
if (options.cached) args.push("--cached");
|
|
503
536
|
if (options.others) args.push("--others");
|
|
504
537
|
if (options.ignored) args.push("--ignored");
|
|
505
|
-
return parseSearchOutput(await
|
|
538
|
+
return parseSearchOutput(await runReadSeek(args, { signal: options.signal })).results;
|
|
506
539
|
}
|
|
507
540
|
|
|
508
541
|
export async function readseekMapContent(
|
|
@@ -511,9 +544,9 @@ export async function readseekMapContent(
|
|
|
511
544
|
options: { signal?: AbortSignal } = {},
|
|
512
545
|
): Promise<FileMap | null> {
|
|
513
546
|
const output = parseMapOutput(
|
|
514
|
-
await
|
|
547
|
+
await runReadSeek(["map", "--stdin", filePath], { signal: options.signal, stdin: content }),
|
|
515
548
|
);
|
|
516
|
-
return
|
|
549
|
+
return fileMapFromReadSeekOutput(output, filePath, Buffer.byteLength(content, "utf8"));
|
|
517
550
|
}
|
|
518
551
|
|
|
519
552
|
function optionalString(value: unknown, field: string): string | undefined {
|
|
@@ -521,7 +554,12 @@ function optionalString(value: unknown, field: string): string | undefined {
|
|
|
521
554
|
return requireString(value, field);
|
|
522
555
|
}
|
|
523
556
|
|
|
524
|
-
function
|
|
557
|
+
function nullableString(value: unknown, field: string): string | null {
|
|
558
|
+
if (value === undefined || value === null) return null;
|
|
559
|
+
return requireString(value, field);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
function parseRefsOutput(value: unknown): ReadSeekRefsOutput {
|
|
525
563
|
if (!value || typeof value !== "object") throw new Error("invalid readseek refs output");
|
|
526
564
|
const output = value as Record<string, unknown>;
|
|
527
565
|
if (!Array.isArray(output.references)) throw new Error("invalid readseek references");
|
|
@@ -549,8 +587,8 @@ function parseRefsOutput(value: unknown): ReadseekRefsOutput {
|
|
|
549
587
|
export async function readseekRefs(
|
|
550
588
|
target: string,
|
|
551
589
|
name: string,
|
|
552
|
-
options:
|
|
553
|
-
): Promise<
|
|
590
|
+
options: ReadSeekRefsOptions = {},
|
|
591
|
+
): Promise<ReadSeekReference[]> {
|
|
554
592
|
const args = ["refs", target, name];
|
|
555
593
|
if (options.scope) args.push("--scope");
|
|
556
594
|
if (options.line !== undefined) args.push("--line", String(options.line));
|
|
@@ -559,15 +597,15 @@ export async function readseekRefs(
|
|
|
559
597
|
if (options.cached) args.push("--cached");
|
|
560
598
|
if (options.others) args.push("--others");
|
|
561
599
|
if (options.ignored) args.push("--ignored");
|
|
562
|
-
return parseRefsOutput(await
|
|
600
|
+
return parseRefsOutput(await runReadSeek(args, { signal: options.signal })).references;
|
|
563
601
|
}
|
|
564
602
|
|
|
565
|
-
function parseDiagnosticKind(value: unknown):
|
|
603
|
+
function parseDiagnosticKind(value: unknown): ReadSeekDiagnostic["kind"] {
|
|
566
604
|
if (value === "error" || value === "missing") return value;
|
|
567
605
|
throw new Error("invalid readseek diagnostic.kind");
|
|
568
606
|
}
|
|
569
607
|
|
|
570
|
-
function parseCheckOutput(value: unknown):
|
|
608
|
+
function parseCheckOutput(value: unknown): ReadSeekCheckOutput {
|
|
571
609
|
if (!value || typeof value !== "object") throw new Error("invalid readseek check output");
|
|
572
610
|
const output = value as Record<string, unknown>;
|
|
573
611
|
if (!Array.isArray(output.diagnostics)) throw new Error("invalid readseek diagnostics");
|
|
@@ -590,8 +628,66 @@ export async function readseekCheck(
|
|
|
590
628
|
filePath: string,
|
|
591
629
|
content: string,
|
|
592
630
|
options: { signal?: AbortSignal } = {},
|
|
593
|
-
): Promise<
|
|
631
|
+
): Promise<ReadSeekCheckOutput> {
|
|
594
632
|
return parseCheckOutput(
|
|
595
|
-
await
|
|
633
|
+
await runReadSeek(["check", "--stdin", filePath], { signal: options.signal, stdin: content }),
|
|
596
634
|
);
|
|
597
635
|
}
|
|
636
|
+
|
|
637
|
+
function parseImageInfo(value: unknown): ReadSeekImageInfo | null {
|
|
638
|
+
if (value === undefined || value === null) return null;
|
|
639
|
+
if (typeof value !== "object") throw new Error("invalid readseek detect image");
|
|
640
|
+
const image = value as Record<string, unknown>;
|
|
641
|
+
return {
|
|
642
|
+
format: requireString(image.format, "image.format"),
|
|
643
|
+
width: requireNumber(image.width, "image.width"),
|
|
644
|
+
height: requireNumber(image.height, "image.height"),
|
|
645
|
+
animated: requireBoolean(image.animated, "image.animated"),
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
function parseOcrText(value: unknown): ReadSeekOcrText | undefined {
|
|
650
|
+
if (value === undefined || value === null) return undefined;
|
|
651
|
+
if (typeof value !== "object") throw new Error("invalid readseek detect ocr");
|
|
652
|
+
const ocr = value as Record<string, unknown>;
|
|
653
|
+
if (!Array.isArray(ocr.lines)) throw new Error("invalid readseek detect ocr.lines");
|
|
654
|
+
return {
|
|
655
|
+
text: requireString(ocr.text, "ocr.text"),
|
|
656
|
+
lines: ocr.lines.map((line) => {
|
|
657
|
+
if (!line || typeof line !== "object") throw new Error("invalid readseek detect ocr line");
|
|
658
|
+
const item = line as Record<string, unknown>;
|
|
659
|
+
const bbox = item.bbox;
|
|
660
|
+
if (!Array.isArray(bbox) || bbox.length !== 4) throw new Error("invalid readseek detect ocr bbox");
|
|
661
|
+
return {
|
|
662
|
+
text: requireString(item.text, "ocr.line.text"),
|
|
663
|
+
bbox: bbox.map((n, i) => requireNumber(n, `ocr.line.bbox[${i}]`)) as [number, number, number, number],
|
|
664
|
+
};
|
|
665
|
+
}),
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
function parseDetectOutput(value: unknown): ReadSeekDetection {
|
|
670
|
+
if (!value || typeof value !== "object") throw new Error("invalid readseek detect output");
|
|
671
|
+
const output = value as Record<string, unknown>;
|
|
672
|
+
return {
|
|
673
|
+
file: requireString(output.file, "file"),
|
|
674
|
+
language: requireString(output.language, "language"),
|
|
675
|
+
engine: nullableString(output.engine, "engine"),
|
|
676
|
+
supported: requireBoolean(output.supported, "supported"),
|
|
677
|
+
binary: requireBoolean(output.binary, "binary"),
|
|
678
|
+
mime: nullableString(output.mime, "mime"),
|
|
679
|
+
syntax: nullableString(output.syntax, "syntax"),
|
|
680
|
+
image: parseImageInfo(output.image),
|
|
681
|
+
ocr: parseOcrText(output.ocr),
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
export async function readseekDetect(
|
|
686
|
+
filePath: string,
|
|
687
|
+
options: { ocr?: boolean; signal?: AbortSignal } = {},
|
|
688
|
+
): Promise<ReadSeekDetection> {
|
|
689
|
+
const args = ["detect"];
|
|
690
|
+
if (options.ocr) args.push("--ocr");
|
|
691
|
+
args.push(filePath);
|
|
692
|
+
return parseDetectOutput(await runReadSeek(args, { signal: options.signal }));
|
|
693
|
+
}
|
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
|
}
|