60 lines
3.0 KiB
JavaScript
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}`);
|
|
}
|