@plannotator/pi-extension 0.23.0 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/generated/resolve-file.ts +99 -20
- package/package.json +1 -1
- package/plannotator.html +3 -3
- package/review-editor.html +2 -2
- package/server/serverAnnotate.ts +3 -2
- package/server/serverPlan.ts +3 -2
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { homedir } from "os";
|
|
14
14
|
import { isAbsolute, join, resolve, win32 } from "path";
|
|
15
15
|
import { existsSync, readdirSync, type Dirent } from "fs";
|
|
16
|
+
import { readdir } from "node:fs/promises";
|
|
16
17
|
|
|
17
18
|
const MARKDOWN_PATH_REGEX = /\.(mdx?|txt)$/i;
|
|
18
19
|
|
|
@@ -46,6 +47,23 @@ const CODE_IGNORED_DIRS = [
|
|
|
46
47
|
".pytest_cache/",
|
|
47
48
|
];
|
|
48
49
|
|
|
50
|
+
const DEFAULT_FILE_BROWSER_MAX_FILES = 5_000;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Return the shared file-traversal budget used by resolution, cache warming,
|
|
54
|
+
* and file-browser discovery. Invalid or non-positive overrides fall back to
|
|
55
|
+
* the default.
|
|
56
|
+
*/
|
|
57
|
+
export function getFileBrowserMaxFiles(): number {
|
|
58
|
+
const value = Number.parseInt(
|
|
59
|
+
process.env.PLANNOTATOR_FILE_BROWSER_MAX_FILES ?? "",
|
|
60
|
+
10,
|
|
61
|
+
);
|
|
62
|
+
return Number.isFinite(value) && value > 0
|
|
63
|
+
? value
|
|
64
|
+
: DEFAULT_FILE_BROWSER_MAX_FILES;
|
|
65
|
+
}
|
|
66
|
+
|
|
49
67
|
export type ResolveResult =
|
|
50
68
|
| { kind: "found"; path: string }
|
|
51
69
|
| { kind: "not_found"; input: string }
|
|
@@ -195,6 +213,11 @@ function fileExists(filePath: string): boolean {
|
|
|
195
213
|
}
|
|
196
214
|
}
|
|
197
215
|
|
|
216
|
+
type FileWalkState = {
|
|
217
|
+
visitedFiles: number;
|
|
218
|
+
readonly limit: number;
|
|
219
|
+
};
|
|
220
|
+
|
|
198
221
|
/** Recursively walk a directory collecting files matching `fileMatcher`, skipping ignored dirs. */
|
|
199
222
|
function walkFiles(
|
|
200
223
|
dir: string,
|
|
@@ -202,28 +225,48 @@ function walkFiles(
|
|
|
202
225
|
results: string[],
|
|
203
226
|
ignoredDirs: string[],
|
|
204
227
|
fileMatcher: (name: string) => boolean,
|
|
228
|
+
state: FileWalkState,
|
|
205
229
|
): void {
|
|
230
|
+
if (state.visitedFiles >= state.limit) return;
|
|
206
231
|
const entries = readdirSync(dir, { withFileTypes: true }) as Dirent[];
|
|
207
232
|
for (const entry of entries) {
|
|
233
|
+
if (state.visitedFiles >= state.limit) return;
|
|
208
234
|
if (entry.isDirectory()) {
|
|
209
235
|
if (ignoredDirs.some((d) => d === entry.name + "/")) continue;
|
|
210
236
|
try {
|
|
211
|
-
walkFiles(
|
|
237
|
+
walkFiles(
|
|
238
|
+
join(dir, entry.name),
|
|
239
|
+
root,
|
|
240
|
+
results,
|
|
241
|
+
ignoredDirs,
|
|
242
|
+
fileMatcher,
|
|
243
|
+
state,
|
|
244
|
+
);
|
|
212
245
|
} catch {
|
|
213
246
|
/* skip dirs we can't read */
|
|
214
247
|
}
|
|
215
|
-
} else if (entry.isFile()
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
248
|
+
} else if (entry.isFile()) {
|
|
249
|
+
state.visitedFiles += 1;
|
|
250
|
+
if (fileMatcher(entry.name)) {
|
|
251
|
+
const relative = join(dir, entry.name)
|
|
252
|
+
.slice(root.length + 1)
|
|
253
|
+
.replace(/\\/g, "/");
|
|
254
|
+
results.push(relative);
|
|
255
|
+
}
|
|
220
256
|
}
|
|
221
257
|
}
|
|
222
258
|
}
|
|
223
259
|
|
|
224
260
|
function walkMarkdownFiles(dir: string, root: string, results: string[], ignoredDirs: string[]): void {
|
|
225
261
|
try {
|
|
226
|
-
walkFiles(
|
|
262
|
+
walkFiles(
|
|
263
|
+
dir,
|
|
264
|
+
root,
|
|
265
|
+
results,
|
|
266
|
+
ignoredDirs,
|
|
267
|
+
(name) => /\.(mdx?|txt)$/i.test(name),
|
|
268
|
+
{ visitedFiles: 0, limit: getFileBrowserMaxFiles() },
|
|
269
|
+
);
|
|
227
270
|
} catch {
|
|
228
271
|
/* fail soft for markdown — preserves existing behavior */
|
|
229
272
|
}
|
|
@@ -241,18 +284,46 @@ function fileListCacheKey(projectRoot: string, kind: string): string {
|
|
|
241
284
|
return `${projectRoot}|${kind}`;
|
|
242
285
|
}
|
|
243
286
|
|
|
244
|
-
function
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
287
|
+
async function walkCodeFiles(
|
|
288
|
+
dir: string,
|
|
289
|
+
root: string,
|
|
290
|
+
results: string[],
|
|
291
|
+
state: FileWalkState,
|
|
292
|
+
): Promise<void> {
|
|
293
|
+
if (state.visitedFiles >= state.limit) return;
|
|
294
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
295
|
+
for (const entry of entries) {
|
|
296
|
+
if (state.visitedFiles >= state.limit) return;
|
|
297
|
+
if (entry.isDirectory()) {
|
|
298
|
+
if (CODE_IGNORED_DIRS.some((d) => d === entry.name + "/")) continue;
|
|
299
|
+
try {
|
|
300
|
+
await walkCodeFiles(join(dir, entry.name), root, results, state);
|
|
301
|
+
} catch {
|
|
302
|
+
/* skip dirs we can't read */
|
|
303
|
+
}
|
|
304
|
+
} else if (entry.isFile()) {
|
|
305
|
+
state.visitedFiles += 1;
|
|
306
|
+
if (CODE_FILE_BASENAME_REGEX.test(entry.name)) {
|
|
307
|
+
const relative = join(dir, entry.name)
|
|
308
|
+
.slice(root.length + 1)
|
|
309
|
+
.replace(/\\/g, "/");
|
|
310
|
+
results.push(relative);
|
|
311
|
+
}
|
|
254
312
|
}
|
|
255
|
-
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async function startCodeWalk(projectRoot: string): Promise<string[] | null> {
|
|
317
|
+
try {
|
|
318
|
+
const results: string[] = [];
|
|
319
|
+
await walkCodeFiles(projectRoot, projectRoot, results, {
|
|
320
|
+
visitedFiles: 0,
|
|
321
|
+
limit: getFileBrowserMaxFiles(),
|
|
322
|
+
});
|
|
323
|
+
return results;
|
|
324
|
+
} catch {
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
256
327
|
}
|
|
257
328
|
|
|
258
329
|
/**
|
|
@@ -489,7 +560,13 @@ export function hasMarkdownFiles(
|
|
|
489
560
|
excludedDirs: string[] = IGNORED_DIRS,
|
|
490
561
|
extensions: RegExp = /\.mdx?$/i,
|
|
491
562
|
): boolean {
|
|
563
|
+
const state: FileWalkState = {
|
|
564
|
+
visitedFiles: 0,
|
|
565
|
+
limit: getFileBrowserMaxFiles(),
|
|
566
|
+
};
|
|
567
|
+
|
|
492
568
|
function walk(dir: string): boolean {
|
|
569
|
+
if (state.visitedFiles >= state.limit) return false;
|
|
493
570
|
let entries;
|
|
494
571
|
try {
|
|
495
572
|
entries = readdirSync(dir, { withFileTypes: true });
|
|
@@ -497,11 +574,13 @@ export function hasMarkdownFiles(
|
|
|
497
574
|
return false;
|
|
498
575
|
}
|
|
499
576
|
for (const entry of entries) {
|
|
577
|
+
if (state.visitedFiles >= state.limit) return false;
|
|
500
578
|
if (entry.isDirectory()) {
|
|
501
579
|
if (excludedDirs.some((d) => d === entry.name + "/")) continue;
|
|
502
580
|
if (walk(join(dir, entry.name))) return true;
|
|
503
|
-
} else if (entry.isFile()
|
|
504
|
-
|
|
581
|
+
} else if (entry.isFile()) {
|
|
582
|
+
state.visitedFiles += 1;
|
|
583
|
+
if (extensions.test(entry.name)) return true;
|
|
505
584
|
}
|
|
506
585
|
}
|
|
507
586
|
return false;
|
package/package.json
CHANGED