diff --git a/packages/dotdog/__tests__/cli.test.ts b/packages/dotdog/__tests__/cli.test.ts index c6bb04c..53feba7 100644 --- a/packages/dotdog/__tests__/cli.test.ts +++ b/packages/dotdog/__tests__/cli.test.ts @@ -170,4 +170,71 @@ describe('CLI', () => { rmSync(dir, { recursive: true, force: true }); } }); + + test('serve reads compiled .doghouse graph artifacts', async () => { + const dir = mkdtempSync(join(tmpdir(), 'dotdog-test-serve-compiled-')); + try { + mkdirSync(join(dir, '.doghouse', 'semantic'), { recursive: true }); + writeFileSync(join(dir, 'package.json'), JSON.stringify({ name: 'compiled-graph-app', version: '1.0.0' }, null, 2)); + writeFileSync(join(dir, 'railway.json'), JSON.stringify({ startCommand: 'bun start' }, null, 2)); + writeFileSync(join(dir, '.doghouse', 'semantic', 'deployment.dog'), [ + '## Deployment', + '', + '### Entity: Deployment', + '', + 'A generic deployment capability.', + '', + '```yaml', + 'entity: Deployment', + 'type: external', + '```', + '', + '### Entity: RailwayService', + '', + 'A generic Railway deployment service.', + '', + '```yaml', + 'entity: RailwayService', + 'type: external', + '```', + '', + '### Relationship: Deployment → RailwayService', + '', + '```yaml', + 'relationship: Deployment → RailwayService', + 'source: Deployment', + 'target: RailwayService', + 'verb: includes', + '```', + ].join('\n')); + + await $`cd ${dir} && ${BUN} ${ROOT}/packages/dotdog/src/cli.ts map . --project compiled-graph-app`.quiet(); + await $`cd ${dir} && ${BUN} ${ROOT}/packages/dotdog/src/cli.ts compile`.quiet(); + expect(existsSync(join(dir, '.doghouse', 'compiled', 'repo.dag'))).toBe(true); + + const proc = Bun.spawn([BUN, join(ROOT, 'packages/dotdog/src/cli.ts'), 'serve'], { + stdin: 'pipe', stdout: 'pipe', stderr: 'pipe', + cwd: dir, + }); + proc.stdin.write(JSON.stringify({jsonrpc:'2.0',id:1,method:'initialize',params:{}})+'\n'); + await new Promise(r => setTimeout(r, 1000)); + proc.stdin.write(JSON.stringify({jsonrpc:'2.0',id:2,method:'tools/call',params:{name:'getEntity',arguments:{name:'Deployment'}}})+'\n'); + proc.stdin.write(JSON.stringify({jsonrpc:'2.0',id:3,method:'tools/call',params:{name:'traverse',arguments:{from:'Deployment',depth:1}}})+'\n'); + proc.stdin.end(); + const out = await new Response(proc.stdout).text(); + proc.kill(); + + const lines = out.split('\n').filter(l => l.trim()); + expect(lines.length).toBeGreaterThanOrEqual(3); + const entity = JSON.parse(JSON.parse(lines[1]).result.content[0].text); + expect(entity.name).toBe('Deployment'); + expect(entity.type).toBe('external'); + expect(entity.edges.some((edge: any) => edge[0] === 'RailwayService' && edge[1] === 'includes')).toBe(true); + const graph = JSON.parse(JSON.parse(lines[2]).result.content[0].text); + expect(graph.nodes.some((node: any) => node.name === 'Deployment')).toBe(true); + expect(graph.nodes.some((node: any) => node.edges.some((edge: string) => edge.startsWith('RailwayService:includes')))).toBe(true); + } finally { + rmSync(dir, { recursive: true, force: true }); + } + }); }); diff --git a/packages/dotdog/__tests__/commands.test.ts b/packages/dotdog/__tests__/commands.test.ts index 75644f7..ef9ed52 100644 --- a/packages/dotdog/__tests__/commands.test.ts +++ b/packages/dotdog/__tests__/commands.test.ts @@ -130,8 +130,8 @@ describe('untested commands', () => { setupTempProject(dir, 'testproj'); const out = await $`cd ${dir} && ${BUN} ${ROOT}/packages/dotdog/src/cli.ts map . --project repo-world-test`.text(); expect(out).toContain('repo.dag'); - expect(existsSync(join(dir, '.dotdog', 'generated', 'repo-map.dog'))).toBe(true); - expect(existsSync(join(dir, '.dotdog', 'generated', 'repo.dag'))).toBe(true); + expect(existsSync(join(dir, '.doghouse', 'generated', 'repo-map.dog'))).toBe(true); + expect(existsSync(join(dir, '.doghouse', 'generated', 'repo.dag'))).toBe(true); } finally { rmSync(dir, { recursive: true, force: true }); } @@ -142,7 +142,7 @@ describe('untested commands', () => { try { setupTempProject(dir, 'testproj'); await $`cd ${dir} && ${BUN} ${ROOT}/packages/dotdog/src/cli.ts map . --project repo-world-test`.quiet(); - const dag = join(dir, '.dotdog', 'generated', 'repo.dag'); + const dag = join(dir, '.doghouse', 'generated', 'repo.dag'); const out = await $`cd ${dir} && ${BUN} ${ROOT}/packages/dotdog/src/cli.ts query repository --dag ${dag}`.text(); expect(out.toLowerCase()).toContain('repository'); } finally { diff --git a/packages/dotdog/__tests__/regression.test.ts b/packages/dotdog/__tests__/regression.test.ts index 90fd63f..77074eb 100644 --- a/packages/dotdog/__tests__/regression.test.ts +++ b/packages/dotdog/__tests__/regression.test.ts @@ -71,10 +71,10 @@ describe('regression', () => { test('semantic deployment nodes survive map and compile remap', async () => { const dir = mkdtempSync(join(tmpdir(), 'dotdog-test-layers-')); try { - mkdirSync(join(dir, '.dotdog', 'semantic'), { recursive: true }); + mkdirSync(join(dir, '.doghouse', 'semantic'), { recursive: true }); writeFileSync(join(dir, 'package.json'), JSON.stringify({ name: 'example-web-app', version: '1.0.0' }, null, 2)); writeFileSync(join(dir, 'railway.json'), JSON.stringify({ startCommand: 'npm start' }, null, 2)); - writeFileSync(join(dir, '.dotdog', 'semantic', 'deployment.dog'), [ + writeFileSync(join(dir, '.doghouse', 'semantic', 'deployment.dog'), [ '## Deployment', '', '### Entity: Deployment', @@ -119,7 +119,7 @@ describe('regression', () => { await $`cd ${dir} && ${BUN} ${ROOT}/packages/dotdog/src/cli.ts map`.quiet(); await $`cd ${dir} && ${BUN} ${ROOT}/packages/dotdog/src/cli.ts compile`.quiet(); - const compiled = JSON.parse(readFileSync(join(dir, '.dotdog', 'compiled', 'repo.dag'), 'utf-8')); + const compiled = JSON.parse(readFileSync(join(dir, '.doghouse', 'compiled', 'repo.dag'), 'utf-8')); const labels = compiled.nodes.map((node: any) => node.label); expect(labels).toContain('Deployment'); expect(labels).toContain('RailwayService'); diff --git a/packages/dotdog/src/cli.ts b/packages/dotdog/src/cli.ts index d315978..175cc1b 100644 --- a/packages/dotdog/src/cli.ts +++ b/packages/dotdog/src/cli.ts @@ -1708,7 +1708,7 @@ program .option('--json', 'print write result as JSON') .action((dir = '.', opts) => { const projectName = opts.project || safeProjectName(resolve(dir)); - const specDir = resolve(dir, '.dotdog', 'generated'); + const specDir = resolve(dir, '.doghouse', 'generated'); const result = writeRepoMap(resolve(dir), projectName, specDir); if (opts.json) { console.log(JSON.stringify(result, null, 2)); @@ -1721,7 +1721,7 @@ program program .command('query ') .description('Query a repo.dag world model') - .option('--dag ', 'path to repo.dag', '.dotdog/compiled/repo.dag') + .option('--dag ', 'path to repo.dag', '.doghouse/compiled/repo.dag') .option('-l, --limit ', 'max results', '10') .action((term, opts) => { const world = loadWorldModel(resolve(opts.dag)); @@ -1732,7 +1732,7 @@ program program .command('trace ') .description('Trace repo.dag relationships for a node') - .option('--dag ', 'path to repo.dag', '.dotdog/compiled/repo.dag') + .option('--dag ', 'path to repo.dag', '.doghouse/compiled/repo.dag') .option('-d, --depth ', 'trace depth', '2') .action((node, opts) => { const world = loadWorldModel(resolve(opts.dag)); diff --git a/packages/dotdog/src/dag/layers.ts b/packages/dotdog/src/dag/layers.ts index 31f5ab2..75a8c25 100644 --- a/packages/dotdog/src/dag/layers.ts +++ b/packages/dotdog/src/dag/layers.ts @@ -163,10 +163,10 @@ function loadDogLayer(dir: string, originType: 'semantic' | 'overlay', labelToId } export function compileDotdogLayers(root: string, project: string): LayerCompileResult | null { - const dotdogDir = join(root, '.dotdog'); - const generatedFile = join(dotdogDir, 'generated', 'repo.dag'); - const semanticDir = join(dotdogDir, 'semantic'); - const overlayDir = join(dotdogDir, 'overlays'); + const doghouseDir = join(root, '.doghouse'); + const generatedFile = join(doghouseDir, 'generated', 'repo.dag'); + const semanticDir = join(doghouseDir, 'semantic'); + const overlayDir = join(doghouseDir, 'overlays'); if (!existsSync(generatedFile) && !existsSync(semanticDir) && !existsSync(overlayDir)) return null; const base = readGeneratedWorld(generatedFile, project, root); @@ -198,7 +198,7 @@ export function compileDotdogLayers(root: string, project: string): LayerCompile unknowns: [...base.unknowns, ...semantic.unknowns, ...overlay.unknowns], }; - const compiledDir = join(dotdogDir, 'compiled'); + const compiledDir = join(doghouseDir, 'compiled'); mkdirSync(compiledDir, { recursive: true }); const outFile = join(compiledDir, 'repo.dag'); writeFileSync(outFile, serializeWorldModel(compiled)); diff --git a/packages/dotdog/src/serve.ts b/packages/dotdog/src/serve.ts index d35c021..95ffe97 100644 --- a/packages/dotdog/src/serve.ts +++ b/packages/dotdog/src/serve.ts @@ -78,12 +78,12 @@ export function serve(dir: string = '.'): void { const dagPaths: Map = new Map(); function loadDags(): string[] { - const compiledDag = join(root, '.dotdog', 'compiled', 'repo.dag'); + const compiledDag = join(root, '.doghouse', 'compiled', 'repo.dag'); if (existsSync(compiledDag)) { const dag = JSON.parse(readFileSync(compiledDag, 'utf-8')); const p = project(dag) || 'repo'; dagCache.set(p, dag); - dagPaths.set(p, join(root, '.dotdog')); + dagPaths.set(p, join(root, '.doghouse')); } const dirs = [join(root,'projects'),join(root,'specs'),root];