Files

60 lines
3.0 KiB
JavaScript

// 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}`);
}