Skip to content

Commit 7e54498

Browse files
feat: auto-download diagram agent, fix assembly route detection
- Phase 4 now auto-clones diagram-generator-agent from GitHub if not present locally (cached at .workspace/.agents/) - Runs bun install and invokes with --prd flag - Removes hardcoded local path to diagram agent - Assembly detects both export const and export function route patterns - Skips functions that require arguments (cannot auto-wire) - Deduplicates plugins across endpoint tasks
1 parent 2be36e7 commit 7e54498

1 file changed

Lines changed: 100 additions & 52 deletions

File tree

src/orchestrator/pipeline.mts

Lines changed: 100 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { writeJson, readJson, readAllCodeFiles } from '../io/file-protocol.mts';
1010
import { access, readFile, writeFile, mkdir } from 'node:fs/promises';
1111
import type { CodeFile } from '../agents/codegen-agent.mts';
1212
import { z } from 'zod';
13-
import { dirname } from 'node:path';
13+
import { dirname, join } from 'node:path';
1414
import { validateGraph } from '../graph/task-graph.mts';
1515
import { executeGraph } from '../graph/parallel-executor.mts';
1616
import { runFixLoop } from './fix-loop.mts';
@@ -443,61 +443,63 @@ export async function runPipeline(
443443
}
444444
} // end skipDocs guard
445445

446-
// Phase 4: Generate architecture diagrams via diagram-agent
446+
// Phase 4: Generate architecture diagrams via diagram-generator-agent
447447
if (config.skipDiagrams) {
448448
logger.info(`Phase 4: Diagrams — skipped (--no-diagrams)`);
449449
} else {
450-
logger.info(`Phase 4: Generating architecture diagrams`);
451-
try {
452-
// Build a system description from the PRD + task results for the diagram agent
453-
const diagramInput = [
454-
`# System Architecture Description`,
455-
``,
456-
`## PRD Summary`,
457-
prdText.substring(0, 3000),
458-
``,
459-
`## Generated Components`,
460-
...taskGraph.tasks.map((t) => `- **${t.name}** (${t.type}): ${t.description.substring(0, 100)}`),
461-
``,
462-
`## Task Dependencies`,
463-
...taskGraph.tasks.filter((t) => t.dependsOn.length > 0).map((t) => `- ${t.id} depends on: ${t.dependsOn.join(`, `)}`),
464-
``,
465-
`## Technology Stack`,
466-
`- Runtime: BunJS`,
467-
`- Framework: Elysia`,
468-
`- Database: MongoDB (Docker)`,
469-
`- Auth: JWT via jose + Elysia .resolve() pattern`,
470-
`- Validation: TypeBox`,
471-
`- Testing: bun:test against real MongoDB`,
472-
].join(`\n`);
473-
474-
const diagramDescPath = `${workspace.root}/diagram-input.md`;
475-
const diagramOutputDir = `${workspace.outputDir()}/graphs`;
476-
await writeFile(diagramDescPath, diagramInput, `utf-8`);
477-
478-
const DIAGRAM_AGENT_PATH = `C:/projects/davis/agents/diagram-agent/src/index.mts`;
479-
const diagramProc = Bun.spawn([`bun`, `run`, DIAGRAM_AGENT_PATH, diagramDescPath, diagramOutputDir], {
480-
stdout: `pipe`,
481-
stderr: `pipe`,
482-
env: { ...Bun.env },
483-
cwd: `C:/projects/davis/agents/diagram-agent`,
484-
});
485-
486-
const [diagramStdout, diagramStderr] = await Promise.all([
487-
new Response(diagramProc.stdout).text(),
488-
new Response(diagramProc.stderr).text(),
489-
]);
490-
const diagramExit = await diagramProc.exited;
491-
492-
if (diagramExit === 0) {
493-
logger.info(`[pipeline] Architecture diagrams generated at ${diagramOutputDir}`);
494-
} else {
495-
logger.warn(`[pipeline] Diagram generation failed (exit ${diagramExit}): ${diagramStderr.substring(0, 500)}`);
450+
logger.info(`Phase 4: Generating architecture diagrams`);
451+
try {
452+
// Build a system description from the PRD + task results for the diagram agent
453+
const diagramInput = [
454+
`# System Architecture Description`,
455+
``,
456+
`## PRD Summary`,
457+
prdText.substring(0, 3000),
458+
``,
459+
`## Generated Components`,
460+
...taskGraph.tasks.map((t) => `- **${t.name}** (${t.type}): ${t.description.substring(0, 100)}`),
461+
``,
462+
`## Task Dependencies`,
463+
...taskGraph.tasks.filter((t) => t.dependsOn.length > 0).map((t) => `- ${t.id} depends on: ${t.dependsOn.join(`, `)}`),
464+
``,
465+
`## Technology Stack`,
466+
`- Runtime: BunJS`,
467+
`- Framework: Elysia`,
468+
`- Database: MongoDB (Docker)`,
469+
`- Auth: JWT via jose + Elysia .resolve() pattern`,
470+
`- Validation: TypeBox`,
471+
`- Testing: bun:test against real MongoDB`,
472+
].join(`\n`);
473+
474+
const diagramDescPath = `${workspace.root}/diagram-input.md`;
475+
await writeFile(diagramDescPath, diagramInput, `utf-8`);
476+
477+
// Ensure diagram-generator-agent is available locally
478+
const agentDir = await ensureDiagramAgent(config.workspaceDir, logger);
479+
480+
// Run the diagram agent with --prd pointing at our generated description
481+
const diagramProc = Bun.spawn([`bun`, `run`, `src/index.mts`, `--prd`, diagramDescPath], {
482+
stdout: `pipe`,
483+
stderr: `pipe`,
484+
env: { ...Bun.env, WORKSPACE_DIR: `${workspace.outputDir()}/graphs` },
485+
cwd: agentDir,
486+
});
487+
488+
const [, diagramStderr] = await Promise.all([
489+
new Response(diagramProc.stdout).text(),
490+
new Response(diagramProc.stderr).text(),
491+
]);
492+
const diagramExit = await diagramProc.exited;
493+
494+
if (diagramExit === 0) {
495+
logger.info(`[pipeline] Architecture diagrams generated at ${workspace.outputDir()}/graphs`);
496+
} else {
497+
logger.warn(`[pipeline] Diagram generation failed (exit ${diagramExit}): ${diagramStderr.substring(0, 500)}`);
498+
}
499+
} catch (e) {
500+
const msg = e instanceof Error ? e.message : String(e);
501+
logger.warn(`[pipeline] Diagram generation skipped: ${msg}`);
496502
}
497-
} catch (e) {
498-
const msg = e instanceof Error ? e.message : String(e);
499-
logger.warn(`[pipeline] Diagram generation skipped: ${msg}`);
500-
}
501503
} // end skipDiagrams guard
502504

503505
// Phase 4.5: Output validation — install deps, start server, verify swagger renders
@@ -812,3 +814,49 @@ async function gatherAllCode(workspace: Workspace, graph: TaskGraph): Promise<st
812814

813815
return parts.length > 0 ? parts.join('\n\n') : undefined;
814816
}
817+
818+
const DIAGRAM_AGENT_REPO = `https://github.com/DavisSylvester/diagram-generator-agent.git`;
819+
820+
async function ensureDiagramAgent(baseDir: string, logger: Logger): Promise<string> {
821+
const agentDir = join(baseDir, `.agents`, `diagram-generator-agent`);
822+
823+
try {
824+
await access(join(agentDir, `package.json`));
825+
logger.info(`[diagrams] Diagram agent found at ${agentDir}`);
826+
return agentDir;
827+
} catch {
828+
// Not present — clone it
829+
}
830+
831+
logger.info(`[diagrams] Diagram agent not found — cloning from ${DIAGRAM_AGENT_REPO}`);
832+
await mkdir(dirname(agentDir), { recursive: true });
833+
834+
const cloneProc = Bun.spawn([`git`, `clone`, `--depth`, `1`, DIAGRAM_AGENT_REPO, agentDir], {
835+
stdout: `pipe`,
836+
stderr: `pipe`,
837+
});
838+
const cloneStderr = await new Response(cloneProc.stderr).text();
839+
const cloneExit = await cloneProc.exited;
840+
841+
if (cloneExit !== 0) {
842+
throw new Error(`Failed to clone diagram agent: ${cloneStderr.substring(0, 500)}`);
843+
}
844+
logger.info(`[diagrams] Cloned diagram-generator-agent`);
845+
846+
// Install dependencies
847+
logger.info(`[diagrams] Installing dependencies...`);
848+
const installProc = Bun.spawn([`bun`, `install`], {
849+
cwd: agentDir,
850+
stdout: `pipe`,
851+
stderr: `pipe`,
852+
});
853+
const installStderr = await new Response(installProc.stderr).text();
854+
const installExit = await installProc.exited;
855+
856+
if (installExit !== 0) {
857+
throw new Error(`Failed to install diagram agent deps: ${installStderr.substring(0, 500)}`);
858+
}
859+
logger.info(`[diagrams] Dependencies installed`);
860+
861+
return agentDir;
862+
}

0 commit comments

Comments
 (0)