/** * DevLoop · REST Routes (P1) * * POST /api/devloop/projects * body: { company_id, name?, requirement, mode?, target_path?, mock? } * → 创建项目 + 跑 PM / Architect 两个 Agent (mock 模式产出 PRD/SDD) * → 返 { pid, root_path, pm: {...}, architect: {...} } * * GET /api/devloop/projects/:pid/state * → 返 { pid, project, units, prd_rev, sdd_rev, prd_site, sdd_site, timeline_tail } * * GET /api/devloop/projects (额外:列项目) * → 返 { projects: [...] } * * 规格: docs/prd/newfunction/_spec/plan-migration.md · P1 验收 */ const express = require('express'); const router = express.Router(); const loopController = require('../src/core/devloop/loop-controller'); const pathResolver = require('../services/devloop/path-resolver'); const h = (fn) => async (req, res) => { try { await fn(req, res); } catch (e) { console.error(`[DevLoop] ${req.method} ${req.path}:`, e.message); res.status(e.statusCode || 500).json({ error: e.message }); } }; // POST /api/devloop/projects router.post('/projects', h(async (req, res) => { const { company_id, requirement, mode, target_path, mock, name } = req.body || {}; if (!company_id) { return res.status(400).json({ error: 'missing field: company_id' }); } if (!requirement || typeof requirement !== 'string') { return res.status(400).json({ error: 'missing field: requirement' }); } // 1. 创建项目 (sandbox 默认) const proj = await loopController.createProject({ company_id, mode: mode || 'sandbox', target_path, idea: name ? `${name}: ${requirement}` : requirement, }); // 2. PM → Architect (mock 模式) const phase = await loopController.runDocsPhase(proj.pid, requirement, { mock: mock !== false, }); res.json({ ok: true, pid: proj.pid, root_path: proj.root_path, mode: proj.mode, pm: { files_written: phase.pm.files_written.length, commit: phase.pm.commit?.sha || null, mock: phase.pm.mock, }, architect: { files_written: phase.architect.files_written.length, commit: phase.architect.commit?.sha || null, mock: phase.architect.mock, }, }); })); // GET /api/devloop/projects router.get('/projects', h(async (_req, res) => { const projects = pathResolver.listProjects(); res.json({ projects }); })); // GET /api/devloop/projects/:pid/state router.get('/projects/:pid/state', h(async (req, res) => { try { const state = loopController.getProjectState(req.params.pid); res.json(state); } catch (e) { if (/Unknown project/.test(e.message)) { return res.status(404).json({ error: 'unknown project: ' + req.params.pid }); } throw e; } })); module.exports = router;