pi-readseek 0.7.6 → 0.7.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +28 -46
  2. package/dist/index.js +7333 -0
  3. package/package.json +8 -7
  4. package/index.ts +0 -147
  5. package/src/binary-detect.ts +0 -22
  6. package/src/check.ts +0 -92
  7. package/src/coerce-obvious-int.ts +0 -41
  8. package/src/confusable-hyphens.ts +0 -6
  9. package/src/def.ts +0 -143
  10. package/src/diff-data.ts +0 -302
  11. package/src/edit-classify.ts +0 -58
  12. package/src/edit-diff.ts +0 -359
  13. package/src/edit-output.ts +0 -110
  14. package/src/edit-render-helpers.ts +0 -141
  15. package/src/edit-syntax-validate.ts +0 -65
  16. package/src/edit.ts +0 -686
  17. package/src/file-map.ts +0 -17
  18. package/src/fs-error.ts +0 -33
  19. package/src/grep-budget.ts +0 -35
  20. package/src/grep-output.ts +0 -181
  21. package/src/grep-render-helpers.ts +0 -77
  22. package/src/grep-symbol-scope.ts +0 -184
  23. package/src/grep.ts +0 -608
  24. package/src/hashline.ts +0 -736
  25. package/src/hover.ts +0 -140
  26. package/src/path-utils.ts +0 -24
  27. package/src/pending-diff-preview.ts +0 -269
  28. package/src/read-local-bundle.ts +0 -75
  29. package/src/read-output.ts +0 -186
  30. package/src/read-render-helpers.ts +0 -104
  31. package/src/read.ts +0 -661
  32. package/src/readseek/constants.ts +0 -17
  33. package/src/readseek/enums.ts +0 -38
  34. package/src/readseek/formatter.ts +0 -350
  35. package/src/readseek/symbol-error-format.ts +0 -18
  36. package/src/readseek/symbol-lookup.ts +0 -240
  37. package/src/readseek/symbol-tree.ts +0 -21
  38. package/src/readseek/types.ts +0 -49
  39. package/src/readseek-client.ts +0 -1250
  40. package/src/readseek-params.ts +0 -39
  41. package/src/readseek-settings.ts +0 -263
  42. package/src/readseek-value.ts +0 -184
  43. package/src/refs-output.ts +0 -47
  44. package/src/refs.ts +0 -161
  45. package/src/register-tool.ts +0 -29
  46. package/src/rename.ts +0 -182
  47. package/src/replace-symbol.ts +0 -77
  48. package/src/runtime.ts +0 -3
  49. package/src/search-output.ts +0 -50
  50. package/src/search.ts +0 -214
  51. package/src/session-anchors.ts +0 -34
  52. package/src/stat-search-path.ts +0 -29
  53. package/src/syntax-validate-mode.ts +0 -15
  54. package/src/tool-prompt-metadata.ts +0 -107
  55. package/src/tool-types.ts +0 -12
  56. package/src/tui-diff-component.ts +0 -108
  57. package/src/tui-diff-renderer.ts +0 -92
  58. package/src/tui-render-utils.ts +0 -286
  59. package/src/view.ts +0 -158
  60. package/src/write.ts +0 -357
package/src/diff-data.ts DELETED
@@ -1,302 +0,0 @@
1
- export type DiffEntry =
2
- | { kind: "context"; oldLine: number; newLine: number; text: string }
3
- | { kind: "add"; newLine: number; text: string }
4
- | { kind: "remove"; oldLine: number; text: string }
5
- | { kind: "meta"; text: string };
6
-
7
- export type DiffSpan =
8
- | { kind: "equal"; text: string }
9
- | { kind: "add"; text: string }
10
- | { kind: "remove"; text: string };
11
-
12
- export type InlineDiff = {
13
- removeLineIndex: number;
14
- addLineIndex: number;
15
- removeSpans: DiffSpan[];
16
- addSpans: DiffSpan[];
17
- };
18
-
19
- export type DiffBlockRange = {
20
- kind: "add" | "remove";
21
- startLine: number;
22
- endLine: number;
23
- };
24
-
25
- export type DiffData = {
26
- version: 1;
27
- entries: DiffEntry[];
28
- stats: { added: number; removed: number; context: number };
29
- language?: string;
30
- blockRanges?: DiffBlockRange[];
31
- inlineDiffs?: InlineDiff[];
32
- };
33
-
34
- export type BuildDiffDataInput = {
35
- path: string;
36
- oldContent: string;
37
- newContent: string;
38
- diff: string;
39
- blockRanges?: DiffBlockRange[];
40
- };
41
-
42
- export const MAX_INLINE_DIFF_LINE_LENGTH = 4096;
43
- export const MAX_INLINE_DIFF_TOKENS = 512;
44
- export const MAX_INLINE_DIFF_CELLS = 200_000;
45
- export const MAX_INLINE_DIFF_PAIRS = 200;
46
-
47
- const INLINE_SIMILARITY_THRESHOLD = 0.35;
48
- const INLINE_TOKEN_PATTERN = /([A-Za-z_$][\w$]*|\d+|\s+|[^A-Za-z_$\w\s]+)/gu;
49
-
50
- const LANGUAGE_BY_EXTENSION = new Map<string, string>([
51
- [".ts", "typescript"],
52
- [".tsx", "typescript"],
53
- [".js", "javascript"],
54
- [".jsx", "javascript"],
55
- [".py", "python"],
56
- [".rs", "rust"],
57
- [".java", "java"],
58
- ]);
59
-
60
- function inferLanguage(path: string): string | undefined {
61
- const extensionMatch = path.match(/\.[^.\/\\]+$/);
62
- if (!extensionMatch) return undefined;
63
- return LANGUAGE_BY_EXTENSION.get(extensionMatch[0].toLowerCase());
64
- }
65
-
66
- function parseFullDiffEntries(diff: string): DiffEntry[] {
67
- const entries: DiffEntry[] = [];
68
- let nextOldLine = 1;
69
- let nextNewLine = 1;
70
-
71
- for (const line of diff ? diff.split("\n") : []) {
72
- const removeMatch = line.match(/^-\s*(\d+) (.*)$/);
73
- if (removeMatch) {
74
- const oldLine = Number(removeMatch[1]);
75
- entries.push({ kind: "remove", oldLine, text: removeMatch[2] ?? "" });
76
- nextOldLine = oldLine + 1;
77
- continue;
78
- }
79
-
80
- const addMatch = line.match(/^\+\s*(\d+) (.*)$/);
81
- if (addMatch) {
82
- const newLine = Number(addMatch[1]);
83
- entries.push({ kind: "add", newLine, text: addMatch[2] ?? "" });
84
- nextNewLine = newLine + 1;
85
- continue;
86
- }
87
-
88
- const contextMatch = line.match(/^ \s*(\d+) (.*)$/);
89
- if (contextMatch) {
90
- const oldLine = Number(contextMatch[1]);
91
- const lineDelta = nextNewLine - nextOldLine;
92
- const newLine = oldLine + lineDelta;
93
- entries.push({ kind: "context", oldLine, newLine, text: contextMatch[2] ?? "" });
94
- nextOldLine = oldLine + 1;
95
- nextNewLine = newLine + 1;
96
- continue;
97
- }
98
-
99
- entries.push({ kind: "meta", text: line });
100
- }
101
-
102
- return entries;
103
- }
104
-
105
- function parseCompactDiffEntries(diff: string, oldContent: string, newContent: string): DiffEntry[] | undefined {
106
- const oldLines = oldContent.split("\n");
107
- const newLines = newContent.split("\n");
108
-
109
- const compactDelete = diff.match(/^(\d+):[0-9a-f]{3}\|(.*) → \[deleted\]$/);
110
- if (compactDelete) {
111
- const oldLine = Number(compactDelete[1]);
112
- const text = compactDelete[2] ?? "";
113
- if (oldLines[oldLine - 1] === text) return [{ kind: "remove", oldLine, text }];
114
- }
115
-
116
- const compactPrefix = diff.match(/^(\d+):[0-9a-f]{3}\|/);
117
- if (!compactPrefix) return undefined;
118
-
119
- const oldLine = Number(compactPrefix[1]);
120
- const oldTextStart = compactPrefix[0].length;
121
- const separatorPattern = / → (\d+):[0-9a-f]{3}\|/g;
122
- let separator: RegExpExecArray | null;
123
- while ((separator = separatorPattern.exec(diff)) !== null) {
124
- const newLine = Number(separator[1]);
125
- const oldText = diff.slice(oldTextStart, separator.index);
126
- const newText = diff.slice(separator.index + separator[0].length);
127
- if (oldLines[oldLine - 1] === oldText && newLines[newLine - 1] === newText) {
128
- return [
129
- { kind: "remove", oldLine, text: oldText },
130
- { kind: "add", newLine, text: newText },
131
- ];
132
- }
133
- }
134
-
135
- return undefined;
136
- }
137
-
138
- function buildStats(entries: DiffEntry[]): DiffData["stats"] {
139
- return entries.reduce(
140
- (stats, entry) => {
141
- if (entry.kind === "add") stats.added++;
142
- else if (entry.kind === "remove") stats.removed++;
143
- else if (entry.kind === "context") stats.context++;
144
- return stats;
145
- },
146
- { added: 0, removed: 0, context: 0 },
147
- );
148
- }
149
-
150
- function tokenizeInlineDiff(text: string): string[] {
151
- return text.match(INLINE_TOKEN_PATTERN) ?? (text ? [text] : []);
152
- }
153
-
154
- function longestCommonSubsequence(a: string[], b: string[]): Array<[number, number]> {
155
- const rows = a.length + 1;
156
- const cols = b.length + 1;
157
- const table: number[][] = Array.from({ length: rows }, () => Array<number>(cols).fill(0));
158
-
159
- for (let i = a.length - 1; i >= 0; i--) {
160
- for (let j = b.length - 1; j >= 0; j--) {
161
- table[i]![j] = a[i] === b[j] ? table[i + 1]![j + 1]! + 1 : Math.max(table[i + 1]![j]!, table[i]![j + 1]!);
162
- }
163
- }
164
-
165
- const pairs: Array<[number, number]> = [];
166
- let i = 0;
167
- let j = 0;
168
- while (i < a.length && j < b.length) {
169
- if (a[i] === b[j]) {
170
- pairs.push([i, j]);
171
- i++;
172
- j++;
173
- } else if (table[i + 1]![j]! >= table[i]![j + 1]!) {
174
- i++;
175
- } else {
176
- j++;
177
- }
178
- }
179
-
180
- return pairs;
181
- }
182
-
183
- function pushMergedSpan(spans: DiffSpan[], span: DiffSpan): void {
184
- if (!span.text) return;
185
- const previous = spans[spans.length - 1];
186
- if (previous?.kind === span.kind) {
187
- previous.text += span.text;
188
- return;
189
- }
190
- spans.push({ ...span });
191
- }
192
-
193
- function buildInlineSpans(removeText: string, addText: string): { removeSpans: DiffSpan[]; addSpans: DiffSpan[] } | undefined {
194
- if (removeText.length > MAX_INLINE_DIFF_LINE_LENGTH || addText.length > MAX_INLINE_DIFF_LINE_LENGTH) return undefined;
195
-
196
- const removeTokens = tokenizeInlineDiff(removeText);
197
- const addTokens = tokenizeInlineDiff(addText);
198
- if (!removeTokens.length || !addTokens.length) return undefined;
199
- if (removeTokens.length > MAX_INLINE_DIFF_TOKENS || addTokens.length > MAX_INLINE_DIFF_TOKENS) return undefined;
200
- if ((removeTokens.length + 1) * (addTokens.length + 1) > MAX_INLINE_DIFF_CELLS) return undefined;
201
-
202
- const pairs = longestCommonSubsequence(removeTokens, addTokens);
203
- const meaningfulRemoveTokenCount = removeTokens.filter((token) => token.trim().length > 0).length;
204
- const meaningfulAddTokenCount = addTokens.filter((token) => token.trim().length > 0).length;
205
- const meaningfulEqualTokenCount = pairs.filter(([removeIndex, addIndex]) => {
206
- const removeToken = removeTokens[removeIndex] ?? "";
207
- const addToken = addTokens[addIndex] ?? "";
208
- return removeToken === addToken && removeToken.trim().length > 0 && addToken.trim().length > 0;
209
- }).length;
210
- const similarity = meaningfulEqualTokenCount / Math.max(meaningfulRemoveTokenCount, meaningfulAddTokenCount);
211
- if (similarity < INLINE_SIMILARITY_THRESHOLD) return undefined;
212
-
213
- const removeSpans: DiffSpan[] = [];
214
- const addSpans: DiffSpan[] = [];
215
- let removeCursor = 0;
216
- let addCursor = 0;
217
-
218
- for (const [removeIndex, addIndex] of pairs) {
219
- if (removeCursor < removeIndex) {
220
- pushMergedSpan(removeSpans, { kind: "remove", text: removeTokens.slice(removeCursor, removeIndex).join("") });
221
- }
222
- if (addCursor < addIndex) {
223
- pushMergedSpan(addSpans, { kind: "add", text: addTokens.slice(addCursor, addIndex).join("") });
224
- }
225
- pushMergedSpan(removeSpans, { kind: "equal", text: removeTokens[removeIndex]! });
226
- pushMergedSpan(addSpans, { kind: "equal", text: addTokens[addIndex]! });
227
- removeCursor = removeIndex + 1;
228
- addCursor = addIndex + 1;
229
- }
230
-
231
- if (removeCursor < removeTokens.length) {
232
- pushMergedSpan(removeSpans, { kind: "remove", text: removeTokens.slice(removeCursor).join("") });
233
- }
234
- if (addCursor < addTokens.length) {
235
- pushMergedSpan(addSpans, { kind: "add", text: addTokens.slice(addCursor).join("") });
236
- }
237
-
238
- if (!removeSpans.some((span) => span.kind === "remove") || !addSpans.some((span) => span.kind === "add")) return undefined;
239
- return { removeSpans, addSpans };
240
- }
241
-
242
- function buildInlineDiffs(entries: DiffEntry[]): InlineDiff[] | undefined {
243
- const inlineDiffs: InlineDiff[] = [];
244
- let remainingPairs = MAX_INLINE_DIFF_PAIRS;
245
-
246
- for (let index = 0; index < entries.length;) {
247
- if (entries[index]?.kind !== "remove") {
248
- index++;
249
- continue;
250
- }
251
-
252
- const removeStart = index;
253
- while (entries[index]?.kind === "remove") index++;
254
- const addStart = index;
255
- while (entries[index]?.kind === "add") index++;
256
-
257
- const removeCount = addStart - removeStart;
258
- const addCount = index - addStart;
259
- if (removeCount === 0 || addCount === 0 || removeCount !== addCount) continue;
260
-
261
- for (let offset = 0; offset < removeCount; offset++) {
262
- if (remainingPairs <= 0) break;
263
- remainingPairs--;
264
-
265
- const removeIndex = removeStart + offset;
266
- const addIndex = addStart + offset;
267
- const removeEntry = entries[removeIndex];
268
- const addEntry = entries[addIndex];
269
- if (removeEntry?.kind !== "remove" || addEntry?.kind !== "add") continue;
270
-
271
- const spans = buildInlineSpans(removeEntry.text, addEntry.text);
272
- if (!spans) continue;
273
-
274
- inlineDiffs.push({
275
- removeLineIndex: removeIndex,
276
- addLineIndex: addIndex,
277
- removeSpans: spans.removeSpans,
278
- addSpans: spans.addSpans,
279
- });
280
- }
281
- }
282
-
283
- return inlineDiffs.length ? inlineDiffs : undefined;
284
- }
285
-
286
- function finalizeDiffData(path: string, entries: DiffEntry[], blockRanges: DiffBlockRange[] | undefined): DiffData {
287
- const language = inferLanguage(path);
288
- const inlineDiffs = buildInlineDiffs(entries);
289
- return {
290
- version: 1,
291
- entries,
292
- stats: buildStats(entries),
293
- ...(language ? { language } : {}),
294
- ...(blockRanges?.length ? { blockRanges: [...blockRanges] } : {}),
295
- ...(inlineDiffs ? { inlineDiffs } : {}),
296
- };
297
- }
298
-
299
- export function buildDiffData(input: BuildDiffDataInput): DiffData {
300
- const entries = parseCompactDiffEntries(input.diff, input.oldContent, input.newContent) ?? parseFullDiffEntries(input.diff);
301
- return finalizeDiffData(input.path, entries, input.blockRanges);
302
- }
@@ -1,58 +0,0 @@
1
- import * as Diff from "diff";
2
-
3
- export type EditClassification = "no-op" | "whitespace-only" | "semantic" | "mixed";
4
-
5
- export interface EditClassifyResult {
6
- classification: EditClassification;
7
- }
8
-
9
- export function classifyEdit(oldContent: string, newContent: string): EditClassifyResult {
10
- if (oldContent === newContent) {
11
- return { classification: "no-op" };
12
- }
13
-
14
- const oldLines = oldContent.split("\n");
15
- const newLines = newContent.split("\n");
16
-
17
- // When line counts match, do pairwise comparison
18
- if (oldLines.length === newLines.length) {
19
- let hasWhitespaceChange = false;
20
- let hasSemanticChange = false;
21
- for (let i = 0; i < oldLines.length; i++) {
22
- if (oldLines[i] === newLines[i]) continue;
23
- if (oldLines[i].trim() === newLines[i].trim()) {
24
- hasWhitespaceChange = true;
25
- } else {
26
- hasSemanticChange = true;
27
- }
28
- }
29
- if (hasSemanticChange && hasWhitespaceChange) return { classification: "mixed" };
30
- if (hasWhitespaceChange) return { classification: "whitespace-only" };
31
- return { classification: "semantic" };
32
- }
33
-
34
- // When line counts differ, use diff library to find changes
35
- const parts = Diff.diffLines(oldContent, newContent);
36
- let hasWhitespaceChange = false;
37
- let hasSemanticChange = false;
38
-
39
- for (const part of parts) {
40
- if (!part.added && !part.removed) continue;
41
-
42
- const lines = part.value.split("\n");
43
- if (lines[lines.length - 1] === "") lines.pop();
44
-
45
- for (const line of lines) {
46
- if (line.trim() === "") {
47
- hasWhitespaceChange = true;
48
- } else {
49
- hasSemanticChange = true;
50
- }
51
- }
52
- }
53
-
54
- if (hasSemanticChange && hasWhitespaceChange) return { classification: "mixed" };
55
- if (hasWhitespaceChange) return { classification: "whitespace-only" };
56
- return { classification: "semantic" };
57
- }
58
-