// Parse static imports without evaluating any repository module. This is an audit tool, not a test runner. // Usage: node --experimental-vm-modules agents/fethry/work/queue-332/import-run-list.mjs import { readdirSync, readFileSync } from 'node:fs'; import { dirname, join, relative, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { SourceTextModule } from 'node:vm'; const root = resolve(dirname(fileURLToPath(import.meta.url)), '../../../..'); const files = []; function walk(dir) { for (const entry of readdirSync(dir, { withFileTypes: true })) { if (['node_modules', '.git', 'dist'].includes(entry.name)) continue; const path = join(dir, entry.name); if (entry.isDirectory()) walk(path); else if (entry.isFile() && path.endsWith('.mjs')) files.push(path); } } for (const dir of ['packages', 'scripts', 'adapters']) walk(join(root, dir)); const graph = new Map(); for (const file of files) { const module = new SourceTextModule(readFileSync(file, 'utf8'), { identifier: file }); graph.set(file, module.dependencySpecifiers.filter(spec => spec.startsWith('.')).map(spec => resolve(dirname(file), spec))); } // The loader imports the configured harness via a computed path. Explicit runtime import edges: const loader = join(root, 'packages/plugins/src/loader.mjs'); graph.get(loader).push(...['claude-code', 'pi'].map(name => join(root, 'packages/plugins/harness', name, 'index.mjs'))); // Actual process entrypoints referenced by constants in fork/spawn calls. graph.get(join(root, 'packages/cli/src/host.mjs')).push(join(root, 'packages/bus/src/process.mjs')); graph.get(join(root, 'packages/seat/src/session.mjs')).push(join(root, 'packages/harness/src/runner.mjs')); const changed = new Set([ 'packages/seat/src/session.mjs', 'packages/business/src/launch.mjs', 'packages/plugins/harness/claude-code/index.mjs', 'packages/plugins/harness/pi/index.mjs', 'packages/harness/src/bundle.mjs', 'packages/cli/src/launcher.mjs', ].map(file => join(root, file))); function affected(file, seen = new Set()) { if (changed.has(file)) return true; if (seen.has(file)) return false; seen.add(file); return (graph.get(file) ?? []).some(dep => affected(dep, seen)); } function trace(file) { const pending = [[file]], seen = new Set(); while (pending.length) { const path = pending.shift(), current = path.at(-1); if (changed.has(current)) return path; if (seen.has(current)) continue; seen.add(current); for (const dependency of graph.get(current) ?? []) pending.push([...path, dependency]); } return null; } const excluded = 'packages/cli/tests/case1-accept.test.mjs'; for (const file of files.filter(file => file.endsWith('.test.mjs')).sort()) { if (!affected(file)) continue; const path = relative(root, file); if (process.argv.includes('--explain')) console.log(JSON.stringify({ test: path, excluded: path === excluded, imports: trace(file).map(part => relative(root, part)) })); else console.log(`${path === excluded ? 'EXCLUDED (mos-1612) ' : ''}${path}`); }