|
1 | 1 | import { readdir, access } from 'node:fs/promises'; |
2 | 2 | import { join } from 'node:path'; |
3 | 3 |
|
4 | | -const IGNORE = new Set(['__pycache__', '.pytest_cache', '.claude', '.playwright-mcp']); |
| 4 | +const IGNORE = new Set(['__pycache__', '.pytest_cache', '.claude', '.playwright-mcp', 'node_modules']); |
5 | 5 |
|
6 | 6 | export interface RawProject { |
7 | 7 | path: string; |
8 | 8 | name: string; |
9 | 9 | } |
10 | 10 |
|
11 | | -export async function scanRepos(baseDir: string): Promise<RawProject[]> { |
12 | | - const out: RawProject[] = []; |
| 11 | +async function isRepo(dir: string): Promise<boolean> { |
| 12 | + try { |
| 13 | + await access(join(dir, '.git')); |
| 14 | + return true; |
| 15 | + } catch { |
| 16 | + return false; |
| 17 | + } |
| 18 | +} |
| 19 | + |
| 20 | +async function walk(dir: string, depth: number, maxDepth: number, out: RawProject[]): Promise<void> { |
13 | 21 | let entries; |
14 | 22 | try { |
15 | | - entries = await readdir(baseDir, { withFileTypes: true }); |
| 23 | + entries = await readdir(dir, { withFileTypes: true }); |
16 | 24 | } catch { |
17 | | - return out; |
| 25 | + return; |
18 | 26 | } |
19 | 27 | await Promise.all( |
20 | 28 | entries |
21 | 29 | .filter((e) => e.isDirectory() && !e.name.startsWith('.') && !IGNORE.has(e.name)) |
22 | 30 | .map(async (entry) => { |
23 | | - const full = join(baseDir, entry.name); |
24 | | - try { |
25 | | - await access(join(full, '.git')); |
26 | | - out.push({ path: full, name: entry.name }); |
27 | | - } catch { /* no .git */ } |
| 31 | + const full = join(dir, entry.name); |
| 32 | + if (await isRepo(full)) { |
| 33 | + out.push({ path: full, name: entry.name }); // a repo — include it, don't descend into it |
| 34 | + } else if (depth < maxDepth) { |
| 35 | + await walk(full, depth + 1, maxDepth, out); // not a repo — look one level deeper (org/repo layouts) |
| 36 | + } |
28 | 37 | }), |
29 | 38 | ); |
| 39 | +} |
| 40 | + |
| 41 | +/** Find git repos under baseDir, scanning up to maxDepth levels (default 2 = org/repo). */ |
| 42 | +export async function scanRepos(baseDir: string, maxDepth = 2): Promise<RawProject[]> { |
| 43 | + const out: RawProject[] = []; |
| 44 | + await walk(baseDir, 1, maxDepth, out); |
30 | 45 | return out; |
31 | 46 | } |
0 commit comments