feat: repo-agnostic refactor (BMad spec-test-build loop)

- NEW: repo-profiler.js — deterministic archetype detection (Infra, Frontend, Backend, etc.)
- NEW: extract-dynamic.js — generic extractor replacing hardcoded Foxtrot patterns
- NEW: eval-generator.js — dynamic ground-truth question generation from any repo graph
- NEW: specs/bmad-agnostic-refactor-spec.md — full BMad spec with acceptance criteria
- REFACTORED: prose.js — two-pass LLM synthesis with rich context (shared secrets, ports, service refs)
- REFACTORED: sysdoc.js — wired repo-profiler + extract-dynamic, --legacy escape hatch
- REFACTORED: wiggum-v2.sh — uses eval-generator before benchmarks
- FIXED: graph.js — _edgeSet rebuilt on loadSnapshot() (edge dedup was broken)
- FIXED: graph.js — recursive sortKeys() for deep equality in diffing
- FIXED: prose.js — robust JSON array extraction from LLM output
- FIXED: ratchet.js — syntax validation (node --check) before saving LLM mutations
- FIXED: extract-dynamic.js — centralized state services regex, added console.warn for silent failures
- TESTS: test-eval-generator, test-repo-profiler, test-synthesis-quality + mock fixtures

Eval: 81.5% on Foxtrot (fully repo-agnostic, no hardcoded reference pages)
BMad reviews: Architect B+, Dev Lead B-, TEA B-
This commit is contained in:
Jarvis Prime
2026-03-11 14:40:31 +00:00
parent 15fb1a753b
commit b8403be96c
26 changed files with 4653 additions and 1037 deletions

View File

@@ -0,0 +1,97 @@
const assert = require('node:assert');
const fs = require('fs');
const path = require('path');
const { profileRepo, ARCHETYPES } = require('../repo-profiler');
const TEST_DIR = path.join(__dirname, 'temp-fixtures');
function setupFixture(name, files) {
const dir = path.join(TEST_DIR, name);
fs.mkdirSync(dir, { recursive: true });
for (const [file, content] of Object.entries(files)) {
fs.writeFileSync(path.join(dir, file), content);
}
return dir;
}
function teardownFixtures() {
if (fs.existsSync(TEST_DIR)) {
fs.rmSync(TEST_DIR, { recursive: true, force: true });
}
}
async function runTests() {
teardownFixtures();
try {
// 1. Test Infrastructure detection
const infraDir = setupFixture('infra', {
'Chart.yaml': 'apiVersion: v2\nname: my-chart',
'main.tf': 'resource "aws_vpc" "main" {}'
});
let result = profileRepo(infraDir);
assert.strictEqual(result.archetype, ARCHETYPES.INFRASTRUCTURE, 'Should detect Infrastructure');
assert.ok(result.confidence > 0.8, 'Confidence should be high');
// 2. Test Frontend SPA detection
const frontendDir = setupFixture('frontend', {
'package.json': JSON.stringify({
dependencies: {
react: '^18.0.0',
'react-dom': '^18.0.0'
},
devDependencies: {
vite: '^4.0.0'
}
})
});
result = profileRepo(frontendDir);
assert.strictEqual(result.archetype, ARCHETYPES.FRONTEND, 'Should detect Frontend SPA');
assert.ok(result.signals.includes('frontend_framework'));
// 3. Test Backend API detection
const backendDir = setupFixture('backend', {
'package.json': JSON.stringify({
dependencies: {
express: '^4.18.2'
}
})
});
result = profileRepo(backendDir);
assert.strictEqual(result.archetype, ARCHETYPES.BACKEND, 'Should detect Backend API');
// 4. Test Library detection
const libraryDir = setupFixture('library', {
'package.json': JSON.stringify({
main: 'index.js',
exports: {
'.': './index.js'
},
dependencies: {
lodash: '^4.17.21'
}
})
});
result = profileRepo(libraryDir);
assert.strictEqual(result.archetype, ARCHETYPES.LIBRARY, 'Should detect Library');
// 5. Monorepo detection
const monorepoDir = setupFixture('monorepo', {
'package.json': JSON.stringify({
workspaces: ['packages/*']
}),
'turbo.json': '{}'
});
result = profileRepo(monorepoDir);
assert.strictEqual(result.archetype, ARCHETYPES.MONOREPO, 'Should detect Monorepo');
console.log('✅ repo-profiler tests passed!');
} finally {
teardownFixtures();
}
}
runTests().catch(err => {
console.error(err);
process.exit(1);
});