@agile-vibe-coding/avc 0.1.0 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (290) hide show
  1. package/README.md +2 -0
  2. package/cli/agent-loader.js +21 -0
  3. package/cli/agents/agent-selector.md +129 -0
  4. package/cli/agents/architecture-recommender.md +418 -0
  5. package/cli/agents/database-deep-dive.md +470 -0
  6. package/cli/agents/database-recommender.md +634 -0
  7. package/cli/agents/doc-distributor.md +176 -0
  8. package/cli/agents/documentation-updater.md +203 -0
  9. package/cli/agents/epic-story-decomposer.md +280 -0
  10. package/cli/agents/feature-context-generator.md +91 -0
  11. package/cli/agents/gap-checker-epic.md +52 -0
  12. package/cli/agents/impact-checker-story.md +51 -0
  13. package/cli/agents/migration-guide-generator.md +305 -0
  14. package/cli/agents/mission-scope-generator.md +79 -0
  15. package/cli/agents/mission-scope-validator.md +112 -0
  16. package/cli/agents/project-context-extractor.md +107 -0
  17. package/cli/agents/project-documentation-creator.json +226 -0
  18. package/cli/agents/project-documentation-creator.md +595 -0
  19. package/cli/agents/question-prefiller.md +269 -0
  20. package/cli/agents/refiner-epic.md +39 -0
  21. package/cli/agents/refiner-story.md +42 -0
  22. package/cli/agents/solver-epic-api.json +15 -0
  23. package/cli/agents/solver-epic-api.md +39 -0
  24. package/cli/agents/solver-epic-backend.json +15 -0
  25. package/cli/agents/solver-epic-backend.md +39 -0
  26. package/cli/agents/solver-epic-cloud.json +15 -0
  27. package/cli/agents/solver-epic-cloud.md +39 -0
  28. package/cli/agents/solver-epic-data.json +15 -0
  29. package/cli/agents/solver-epic-data.md +39 -0
  30. package/cli/agents/solver-epic-database.json +15 -0
  31. package/cli/agents/solver-epic-database.md +39 -0
  32. package/cli/agents/solver-epic-developer.json +15 -0
  33. package/cli/agents/solver-epic-developer.md +39 -0
  34. package/cli/agents/solver-epic-devops.json +15 -0
  35. package/cli/agents/solver-epic-devops.md +39 -0
  36. package/cli/agents/solver-epic-frontend.json +15 -0
  37. package/cli/agents/solver-epic-frontend.md +39 -0
  38. package/cli/agents/solver-epic-mobile.json +15 -0
  39. package/cli/agents/solver-epic-mobile.md +39 -0
  40. package/cli/agents/solver-epic-qa.json +15 -0
  41. package/cli/agents/solver-epic-qa.md +39 -0
  42. package/cli/agents/solver-epic-security.json +15 -0
  43. package/cli/agents/solver-epic-security.md +39 -0
  44. package/cli/agents/solver-epic-solution-architect.json +15 -0
  45. package/cli/agents/solver-epic-solution-architect.md +39 -0
  46. package/cli/agents/solver-epic-test-architect.json +15 -0
  47. package/cli/agents/solver-epic-test-architect.md +39 -0
  48. package/cli/agents/solver-epic-ui.json +15 -0
  49. package/cli/agents/solver-epic-ui.md +39 -0
  50. package/cli/agents/solver-epic-ux.json +15 -0
  51. package/cli/agents/solver-epic-ux.md +39 -0
  52. package/cli/agents/solver-story-api.json +15 -0
  53. package/cli/agents/solver-story-api.md +39 -0
  54. package/cli/agents/solver-story-backend.json +15 -0
  55. package/cli/agents/solver-story-backend.md +39 -0
  56. package/cli/agents/solver-story-cloud.json +15 -0
  57. package/cli/agents/solver-story-cloud.md +39 -0
  58. package/cli/agents/solver-story-data.json +15 -0
  59. package/cli/agents/solver-story-data.md +39 -0
  60. package/cli/agents/solver-story-database.json +15 -0
  61. package/cli/agents/solver-story-database.md +39 -0
  62. package/cli/agents/solver-story-developer.json +15 -0
  63. package/cli/agents/solver-story-developer.md +39 -0
  64. package/cli/agents/solver-story-devops.json +15 -0
  65. package/cli/agents/solver-story-devops.md +39 -0
  66. package/cli/agents/solver-story-frontend.json +15 -0
  67. package/cli/agents/solver-story-frontend.md +39 -0
  68. package/cli/agents/solver-story-mobile.json +15 -0
  69. package/cli/agents/solver-story-mobile.md +39 -0
  70. package/cli/agents/solver-story-qa.json +15 -0
  71. package/cli/agents/solver-story-qa.md +39 -0
  72. package/cli/agents/solver-story-security.json +15 -0
  73. package/cli/agents/solver-story-security.md +39 -0
  74. package/cli/agents/solver-story-solution-architect.json +15 -0
  75. package/cli/agents/solver-story-solution-architect.md +39 -0
  76. package/cli/agents/solver-story-test-architect.json +15 -0
  77. package/cli/agents/solver-story-test-architect.md +39 -0
  78. package/cli/agents/solver-story-ui.json +15 -0
  79. package/cli/agents/solver-story-ui.md +39 -0
  80. package/cli/agents/solver-story-ux.json +15 -0
  81. package/cli/agents/solver-story-ux.md +39 -0
  82. package/cli/agents/story-doc-enricher.md +133 -0
  83. package/cli/agents/suggestion-business-analyst.md +88 -0
  84. package/cli/agents/suggestion-deployment-architect.md +263 -0
  85. package/cli/agents/suggestion-product-manager.md +129 -0
  86. package/cli/agents/suggestion-security-specialist.md +156 -0
  87. package/cli/agents/suggestion-technical-architect.md +269 -0
  88. package/cli/agents/suggestion-ux-researcher.md +93 -0
  89. package/cli/agents/task-subtask-decomposer.md +188 -0
  90. package/cli/agents/validator-documentation.json +152 -0
  91. package/cli/agents/validator-documentation.md +453 -0
  92. package/cli/agents/validator-epic-api.json +93 -0
  93. package/cli/agents/validator-epic-api.md +137 -0
  94. package/cli/agents/validator-epic-backend.json +93 -0
  95. package/cli/agents/validator-epic-backend.md +130 -0
  96. package/cli/agents/validator-epic-cloud.json +93 -0
  97. package/cli/agents/validator-epic-cloud.md +137 -0
  98. package/cli/agents/validator-epic-data.json +93 -0
  99. package/cli/agents/validator-epic-data.md +130 -0
  100. package/cli/agents/validator-epic-database.json +93 -0
  101. package/cli/agents/validator-epic-database.md +137 -0
  102. package/cli/agents/validator-epic-developer.json +74 -0
  103. package/cli/agents/validator-epic-developer.md +153 -0
  104. package/cli/agents/validator-epic-devops.json +74 -0
  105. package/cli/agents/validator-epic-devops.md +153 -0
  106. package/cli/agents/validator-epic-frontend.json +74 -0
  107. package/cli/agents/validator-epic-frontend.md +153 -0
  108. package/cli/agents/validator-epic-mobile.json +93 -0
  109. package/cli/agents/validator-epic-mobile.md +130 -0
  110. package/cli/agents/validator-epic-qa.json +93 -0
  111. package/cli/agents/validator-epic-qa.md +130 -0
  112. package/cli/agents/validator-epic-security.json +74 -0
  113. package/cli/agents/validator-epic-security.md +154 -0
  114. package/cli/agents/validator-epic-solution-architect.json +74 -0
  115. package/cli/agents/validator-epic-solution-architect.md +156 -0
  116. package/cli/agents/validator-epic-test-architect.json +93 -0
  117. package/cli/agents/validator-epic-test-architect.md +130 -0
  118. package/cli/agents/validator-epic-ui.json +93 -0
  119. package/cli/agents/validator-epic-ui.md +130 -0
  120. package/cli/agents/validator-epic-ux.json +93 -0
  121. package/cli/agents/validator-epic-ux.md +130 -0
  122. package/cli/agents/validator-selector.md +211 -0
  123. package/cli/agents/validator-story-api.json +104 -0
  124. package/cli/agents/validator-story-api.md +152 -0
  125. package/cli/agents/validator-story-backend.json +104 -0
  126. package/cli/agents/validator-story-backend.md +152 -0
  127. package/cli/agents/validator-story-cloud.json +104 -0
  128. package/cli/agents/validator-story-cloud.md +152 -0
  129. package/cli/agents/validator-story-data.json +104 -0
  130. package/cli/agents/validator-story-data.md +152 -0
  131. package/cli/agents/validator-story-database.json +104 -0
  132. package/cli/agents/validator-story-database.md +152 -0
  133. package/cli/agents/validator-story-developer.json +104 -0
  134. package/cli/agents/validator-story-developer.md +152 -0
  135. package/cli/agents/validator-story-devops.json +104 -0
  136. package/cli/agents/validator-story-devops.md +152 -0
  137. package/cli/agents/validator-story-frontend.json +104 -0
  138. package/cli/agents/validator-story-frontend.md +152 -0
  139. package/cli/agents/validator-story-mobile.json +104 -0
  140. package/cli/agents/validator-story-mobile.md +152 -0
  141. package/cli/agents/validator-story-qa.json +104 -0
  142. package/cli/agents/validator-story-qa.md +152 -0
  143. package/cli/agents/validator-story-security.json +104 -0
  144. package/cli/agents/validator-story-security.md +152 -0
  145. package/cli/agents/validator-story-solution-architect.json +104 -0
  146. package/cli/agents/validator-story-solution-architect.md +152 -0
  147. package/cli/agents/validator-story-test-architect.json +104 -0
  148. package/cli/agents/validator-story-test-architect.md +152 -0
  149. package/cli/agents/validator-story-ui.json +104 -0
  150. package/cli/agents/validator-story-ui.md +152 -0
  151. package/cli/agents/validator-story-ux.json +104 -0
  152. package/cli/agents/validator-story-ux.md +152 -0
  153. package/cli/ansi-colors.js +21 -0
  154. package/cli/build-docs.js +298 -0
  155. package/cli/ceremony-history.js +369 -0
  156. package/cli/command-logger.js +245 -0
  157. package/cli/components/static-output.js +63 -0
  158. package/cli/console-output-manager.js +94 -0
  159. package/cli/docs-sync.js +306 -0
  160. package/cli/epic-story-validator.js +1174 -0
  161. package/cli/evaluation-prompts.js +1008 -0
  162. package/cli/execution-context.js +195 -0
  163. package/cli/generate-summary-table.js +340 -0
  164. package/cli/index.js +3 -25
  165. package/cli/init-model-config.js +697 -0
  166. package/cli/init.js +1765 -100
  167. package/cli/kanban-server-manager.js +228 -0
  168. package/cli/llm-claude.js +109 -0
  169. package/cli/llm-gemini.js +115 -0
  170. package/cli/llm-mock.js +233 -0
  171. package/cli/llm-openai.js +233 -0
  172. package/cli/llm-provider.js +300 -0
  173. package/cli/llm-token-limits.js +102 -0
  174. package/cli/llm-verifier.js +454 -0
  175. package/cli/logger.js +32 -5
  176. package/cli/message-constants.js +58 -0
  177. package/cli/message-manager.js +334 -0
  178. package/cli/message-types.js +96 -0
  179. package/cli/messaging-api.js +297 -0
  180. package/cli/model-pricing.js +169 -0
  181. package/cli/model-query-engine.js +468 -0
  182. package/cli/model-recommendation-analyzer.js +495 -0
  183. package/cli/model-selector.js +269 -0
  184. package/cli/output-buffer.js +107 -0
  185. package/cli/process-manager.js +332 -0
  186. package/cli/repl-ink.js +5840 -504
  187. package/cli/repl-old.js +4 -4
  188. package/cli/seed-processor.js +792 -0
  189. package/cli/sprint-planning-processor.js +1813 -0
  190. package/cli/template-processor.js +2306 -108
  191. package/cli/templates/project.md +25 -8
  192. package/cli/templates/vitepress-config.mts.template +34 -0
  193. package/cli/token-tracker.js +520 -0
  194. package/cli/tools/generate-story-validators.js +317 -0
  195. package/cli/tools/generate-validators.js +669 -0
  196. package/cli/update-checker.js +19 -17
  197. package/cli/update-notifier.js +4 -4
  198. package/cli/validation-router.js +605 -0
  199. package/cli/verification-tracker.js +563 -0
  200. package/kanban/README.md +386 -0
  201. package/kanban/client/README.md +205 -0
  202. package/kanban/client/components.json +20 -0
  203. package/kanban/client/dist/assets/index-CiD8PS2e.js +306 -0
  204. package/kanban/client/dist/assets/index-nLh0m82Q.css +1 -0
  205. package/kanban/client/dist/index.html +16 -0
  206. package/kanban/client/dist/vite.svg +1 -0
  207. package/kanban/client/index.html +15 -0
  208. package/kanban/client/package-lock.json +9442 -0
  209. package/kanban/client/package.json +44 -0
  210. package/kanban/client/postcss.config.js +6 -0
  211. package/kanban/client/public/vite.svg +1 -0
  212. package/kanban/client/src/App.jsx +622 -0
  213. package/kanban/client/src/components/ProjectFileEditorPopup.jsx +117 -0
  214. package/kanban/client/src/components/ceremony/AskArchPopup.jsx +416 -0
  215. package/kanban/client/src/components/ceremony/AskModelPopup.jsx +616 -0
  216. package/kanban/client/src/components/ceremony/CeremonyWorkflowModal.jsx +946 -0
  217. package/kanban/client/src/components/ceremony/EpicStorySelectionModal.jsx +254 -0
  218. package/kanban/client/src/components/ceremony/SponsorCallModal.jsx +619 -0
  219. package/kanban/client/src/components/ceremony/SprintPlanningModal.jsx +704 -0
  220. package/kanban/client/src/components/ceremony/steps/ArchitectureStep.jsx +150 -0
  221. package/kanban/client/src/components/ceremony/steps/CompleteStep.jsx +154 -0
  222. package/kanban/client/src/components/ceremony/steps/DatabaseStep.jsx +202 -0
  223. package/kanban/client/src/components/ceremony/steps/DeploymentStep.jsx +123 -0
  224. package/kanban/client/src/components/ceremony/steps/MissionStep.jsx +106 -0
  225. package/kanban/client/src/components/ceremony/steps/ReviewAnswersStep.jsx +125 -0
  226. package/kanban/client/src/components/ceremony/steps/RunningStep.jsx +228 -0
  227. package/kanban/client/src/components/kanban/CardDetailModal.jsx +559 -0
  228. package/kanban/client/src/components/kanban/EpicSection.jsx +146 -0
  229. package/kanban/client/src/components/kanban/FilterToolbar.jsx +222 -0
  230. package/kanban/client/src/components/kanban/GroupingSelector.jsx +57 -0
  231. package/kanban/client/src/components/kanban/KanbanBoard.jsx +211 -0
  232. package/kanban/client/src/components/kanban/KanbanCard.jsx +138 -0
  233. package/kanban/client/src/components/kanban/KanbanColumn.jsx +90 -0
  234. package/kanban/client/src/components/kanban/RefineWorkItemPopup.jsx +789 -0
  235. package/kanban/client/src/components/layout/LoadingScreen.jsx +82 -0
  236. package/kanban/client/src/components/process/ProcessMonitorBar.jsx +80 -0
  237. package/kanban/client/src/components/settings/AgentEditorPopup.jsx +171 -0
  238. package/kanban/client/src/components/settings/AgentsTab.jsx +353 -0
  239. package/kanban/client/src/components/settings/ApiKeysTab.jsx +113 -0
  240. package/kanban/client/src/components/settings/CeremonyModelsTab.jsx +98 -0
  241. package/kanban/client/src/components/settings/CostThresholdsTab.jsx +94 -0
  242. package/kanban/client/src/components/settings/ModelPricingTab.jsx +204 -0
  243. package/kanban/client/src/components/settings/ServersTab.jsx +121 -0
  244. package/kanban/client/src/components/settings/SettingsModal.jsx +84 -0
  245. package/kanban/client/src/components/stats/CostModal.jsx +353 -0
  246. package/kanban/client/src/components/ui/badge.jsx +27 -0
  247. package/kanban/client/src/components/ui/dialog.jsx +121 -0
  248. package/kanban/client/src/components/ui/tabs.jsx +85 -0
  249. package/kanban/client/src/hooks/__tests__/useGrouping.test.js +232 -0
  250. package/kanban/client/src/hooks/useGrouping.js +118 -0
  251. package/kanban/client/src/hooks/useWebSocket.js +120 -0
  252. package/kanban/client/src/lib/__tests__/api.test.js +196 -0
  253. package/kanban/client/src/lib/__tests__/status-grouping.test.js +94 -0
  254. package/kanban/client/src/lib/api.js +401 -0
  255. package/kanban/client/src/lib/status-grouping.js +144 -0
  256. package/kanban/client/src/lib/utils.js +11 -0
  257. package/kanban/client/src/main.jsx +10 -0
  258. package/kanban/client/src/store/__tests__/kanbanStore.test.js +164 -0
  259. package/kanban/client/src/store/ceremonyStore.js +172 -0
  260. package/kanban/client/src/store/filterStore.js +201 -0
  261. package/kanban/client/src/store/kanbanStore.js +115 -0
  262. package/kanban/client/src/store/processStore.js +65 -0
  263. package/kanban/client/src/store/sprintPlanningStore.js +33 -0
  264. package/kanban/client/src/styles/globals.css +59 -0
  265. package/kanban/client/tailwind.config.js +77 -0
  266. package/kanban/client/vite.config.js +28 -0
  267. package/kanban/client/vitest.config.js +28 -0
  268. package/kanban/dev-start.sh +47 -0
  269. package/kanban/package.json +12 -0
  270. package/kanban/server/index.js +516 -0
  271. package/kanban/server/routes/ceremony.js +305 -0
  272. package/kanban/server/routes/costs.js +157 -0
  273. package/kanban/server/routes/processes.js +50 -0
  274. package/kanban/server/routes/settings.js +303 -0
  275. package/kanban/server/routes/websocket.js +276 -0
  276. package/kanban/server/routes/work-items.js +347 -0
  277. package/kanban/server/services/CeremonyService.js +1190 -0
  278. package/kanban/server/services/FileSystemScanner.js +95 -0
  279. package/kanban/server/services/FileWatcher.js +144 -0
  280. package/kanban/server/services/HierarchyBuilder.js +196 -0
  281. package/kanban/server/services/ProcessRegistry.js +122 -0
  282. package/kanban/server/services/WorkItemReader.js +123 -0
  283. package/kanban/server/services/WorkItemRefineService.js +510 -0
  284. package/kanban/server/start.js +49 -0
  285. package/kanban/server/utils/kanban-logger.js +132 -0
  286. package/kanban/server/utils/markdown.js +91 -0
  287. package/kanban/server/utils/status-grouping.js +107 -0
  288. package/kanban/server/workers/sponsor-call-worker.js +84 -0
  289. package/kanban/server/workers/sprint-planning-worker.js +130 -0
  290. package/package.json +34 -7
@@ -0,0 +1,305 @@
1
+ import express from 'express';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+
5
+ /**
6
+ * Ceremony Router
7
+ * Handles all /api/ceremony/* endpoints for the sponsor-call wizard.
8
+ * @param {CeremonyService} ceremonyService
9
+ */
10
+ export function createCeremonyRouter(ceremonyService, processRegistry) {
11
+ const router = express.Router();
12
+
13
+ // GET /api/ceremony/status — current ceremony state
14
+ router.get('/status', (req, res) => {
15
+ res.json(ceremonyService.getStatus());
16
+ });
17
+
18
+ // GET /api/ceremony/models — available LLM models with API key status
19
+ router.get('/models', async (req, res) => {
20
+ try {
21
+ const models = await ceremonyService.getAvailableModels();
22
+ res.json(models);
23
+ } catch (err) {
24
+ console.error('getAvailableModels error:', err);
25
+ res.status(500).json({ error: err.message });
26
+ }
27
+ });
28
+
29
+ // POST /api/ceremony/generate-mission
30
+ // Body: { description, modelId, provider, validatorModelId, validatorProvider }
31
+ router.post('/generate-mission', async (req, res) => {
32
+ const { description, modelId, provider, validatorModelId, validatorProvider } = req.body;
33
+ console.log('[ceremony] POST /generate-mission', {
34
+ descriptionLength: description?.length,
35
+ modelId,
36
+ provider,
37
+ validatorModelId,
38
+ validatorProvider,
39
+ });
40
+
41
+ if (!description?.trim() || !modelId || !provider || !validatorModelId || !validatorProvider) {
42
+ console.warn('[ceremony] generate-mission: missing required fields');
43
+ return res.status(400).json({
44
+ error: 'description, modelId, provider, validatorModelId and validatorProvider are required',
45
+ });
46
+ }
47
+
48
+ const start = Date.now();
49
+ try {
50
+ const result = await ceremonyService.generateMissionScope(
51
+ description, modelId, provider, validatorModelId, validatorProvider
52
+ );
53
+ console.log(`[ceremony] generate-mission completed in ${Date.now() - start}ms`, {
54
+ validationScore: result.validationScore,
55
+ iterations: result.iterations,
56
+ issueCount: result.issues?.length,
57
+ });
58
+ res.json(result);
59
+ } catch (err) {
60
+ console.error(`[ceremony] generate-mission failed in ${Date.now() - start}ms:`, err.message);
61
+ res.status(500).json({ error: err.message });
62
+ }
63
+ });
64
+
65
+ // POST /api/ceremony/refine-mission
66
+ // Body: { missionStatement, initialScope, refinementRequest, modelId, provider, validatorModelId, validatorProvider }
67
+ router.post('/refine-mission', async (req, res) => {
68
+ const { missionStatement, initialScope, refinementRequest, modelId, provider, validatorModelId, validatorProvider } = req.body;
69
+ if (!missionStatement?.trim() || !initialScope?.trim() || !refinementRequest?.trim() || !modelId || !provider || !validatorModelId || !validatorProvider) {
70
+ return res.status(400).json({ error: 'missionStatement, initialScope, refinementRequest, modelId, provider, validatorModelId and validatorProvider are required' });
71
+ }
72
+ const start = Date.now();
73
+ try {
74
+ const result = await ceremonyService.refineMissionScope(
75
+ missionStatement, initialScope, refinementRequest, modelId, provider, validatorModelId, validatorProvider
76
+ );
77
+ console.log(`[ceremony] refine-mission completed in ${Date.now() - start}ms`, { validationScore: result.validationScore, iterations: result.iterations });
78
+ res.json(result);
79
+ } catch (err) {
80
+ console.error('[ceremony] refine-mission failed:', err.message);
81
+ res.status(500).json({ error: err.message });
82
+ }
83
+ });
84
+
85
+ // POST /api/ceremony/generate-architecture
86
+ // Body: { description, modelId, provider }
87
+ router.post('/generate-architecture', async (req, res) => {
88
+ const { description, modelId, provider } = req.body;
89
+ if (!description?.trim() || !modelId || !provider) {
90
+ return res.status(400).json({ error: 'description, modelId and provider are required' });
91
+ }
92
+ try {
93
+ const arch = await ceremonyService.generateCustomArchitecture(description, modelId, provider);
94
+ res.json(arch);
95
+ } catch (err) {
96
+ console.error('[ceremony] generate-architecture failed:', err.message);
97
+ res.status(500).json({ error: err.message });
98
+ }
99
+ });
100
+
101
+ // POST /api/ceremony/refine-architecture
102
+ // Body: { currentArch, refinementRequest, modelId, provider }
103
+ router.post('/refine-architecture', async (req, res) => {
104
+ const { currentArch, refinementRequest, modelId, provider } = req.body;
105
+ if (!currentArch || !refinementRequest?.trim() || !modelId || !provider) {
106
+ return res.status(400).json({ error: 'currentArch, refinementRequest, modelId and provider are required' });
107
+ }
108
+ try {
109
+ const arch = await ceremonyService.refineCustomArchitecture(currentArch, refinementRequest, modelId, provider);
110
+ res.json(arch);
111
+ } catch (err) {
112
+ console.error('[ceremony] refine-architecture failed:', err.message);
113
+ res.status(500).json({ error: err.message });
114
+ }
115
+ });
116
+
117
+ // POST /api/ceremony/analyze/database
118
+ // Body: { mission, scope, strategy }
119
+ router.post('/analyze/database', async (req, res) => {
120
+ const { mission, scope, strategy } = req.body;
121
+ console.log('[ceremony] POST /analyze/database', {
122
+ missionLength: mission?.length,
123
+ scopeLines: scope?.split('\n').length,
124
+ strategy,
125
+ });
126
+ if (!mission || !scope) {
127
+ return res.status(400).json({ error: 'mission and scope are required' });
128
+ }
129
+ const start = Date.now();
130
+ try {
131
+ const result = await ceremonyService.analyzeDatabase(mission, scope, strategy || null);
132
+ console.log(`[ceremony] analyze/database completed in ${Date.now() - start}ms`);
133
+ res.json(result);
134
+ } catch (err) {
135
+ console.error(`[ceremony] analyze/database failed in ${Date.now() - start}ms:`, err.message);
136
+ res.status(500).json({ error: err.message });
137
+ }
138
+ });
139
+
140
+ // POST /api/ceremony/analyze/architecture
141
+ // Body: { mission, scope, dbContext, strategy }
142
+ router.post('/analyze/architecture', async (req, res) => {
143
+ const { mission, scope, dbContext, strategy } = req.body;
144
+ console.log('[ceremony] POST /analyze/architecture', {
145
+ missionLength: mission?.length,
146
+ dbContext: dbContext ? 'provided' : 'null',
147
+ strategy,
148
+ });
149
+ if (!mission || !scope) {
150
+ return res.status(400).json({ error: 'mission and scope are required' });
151
+ }
152
+ const start = Date.now();
153
+ try {
154
+ const result = await ceremonyService.analyzeArchitecture(
155
+ mission,
156
+ scope,
157
+ dbContext || null,
158
+ strategy || null
159
+ );
160
+ console.log(`[ceremony] analyze/architecture completed in ${Date.now() - start}ms`);
161
+ res.json(result);
162
+ } catch (err) {
163
+ console.error(`[ceremony] analyze/architecture failed in ${Date.now() - start}ms:`, err.message);
164
+ res.status(500).json({ error: err.message });
165
+ }
166
+ });
167
+
168
+ // POST /api/ceremony/analyze/prefill
169
+ // Body: { mission, scope, arch, dbContext, strategy }
170
+ router.post('/analyze/prefill', async (req, res) => {
171
+ const { mission, scope, arch, dbContext, strategy } = req.body;
172
+ console.log('[ceremony] POST /analyze/prefill', {
173
+ missionLength: mission?.length,
174
+ arch: arch ? 'provided' : 'null',
175
+ strategy,
176
+ });
177
+ if (!mission || !scope || !arch) {
178
+ return res.status(400).json({ error: 'mission, scope, and arch are required' });
179
+ }
180
+ const start = Date.now();
181
+ try {
182
+ const result = await ceremonyService.prefillAnswers(
183
+ mission,
184
+ scope,
185
+ arch,
186
+ dbContext || null,
187
+ strategy || null
188
+ );
189
+ console.log(`[ceremony] analyze/prefill completed in ${Date.now() - start}ms`);
190
+ res.json(result);
191
+ } catch (err) {
192
+ console.error(`[ceremony] analyze/prefill failed in ${Date.now() - start}ms:`, err.message);
193
+ res.status(500).json({ error: err.message });
194
+ }
195
+ });
196
+
197
+ // POST /api/ceremony/run
198
+ // Body: { requirements } — all 7 template variables
199
+ router.post('/run', async (req, res) => {
200
+ const { requirements } = req.body;
201
+ console.log('[ceremony] POST /run', {
202
+ requirementKeys: Object.keys(requirements || {}),
203
+ missionLength: requirements?.MISSION_STATEMENT?.length,
204
+ });
205
+ if (!requirements || !requirements.MISSION_STATEMENT) {
206
+ return res.status(400).json({ error: 'requirements.MISSION_STATEMENT is required' });
207
+ }
208
+ try {
209
+ const processId = await ceremonyService.runSponsorCallInProcess(processRegistry, requirements);
210
+ console.log('[ceremony] run started in process', processId);
211
+ res.json({ started: true, processId });
212
+ } catch (err) {
213
+ console.error('[ceremony] run error:', err.message);
214
+ res.status(500).json({ error: err.message });
215
+ }
216
+ });
217
+
218
+ // POST /api/ceremony/sprint-planning/run
219
+ router.post('/sprint-planning/run', async (req, res) => {
220
+ try {
221
+ const processId = await ceremonyService.runSprintPlanningInProcess(processRegistry);
222
+ console.log('[ceremony] sprint-planning/run started in process', processId);
223
+ res.json({ started: true, processId });
224
+ } catch (err) {
225
+ console.error('[ceremony] sprint-planning/run error:', err.message);
226
+ res.status(400).json({ error: err.message });
227
+ }
228
+ });
229
+
230
+ // POST /api/ceremony/sprint-planning/confirm-selection
231
+ // Body: { selectedEpicIds: string[], selectedStoryIds: string[] }
232
+ router.post('/sprint-planning/confirm-selection', (req, res) => {
233
+ const { selectedEpicIds, selectedStoryIds } = req.body;
234
+ if (!Array.isArray(selectedEpicIds) || !Array.isArray(selectedStoryIds)) {
235
+ return res.status(400).json({ error: 'selectedEpicIds and selectedStoryIds must be arrays' });
236
+ }
237
+ ceremonyService.confirmSprintPlanningSelection(selectedEpicIds, selectedStoryIds);
238
+ res.json({ ok: true });
239
+ });
240
+
241
+ // POST /api/ceremony/pause
242
+ router.post('/pause', (req, res) => {
243
+ ceremonyService.pause();
244
+ res.json({ ok: true });
245
+ });
246
+
247
+ // POST /api/ceremony/resume
248
+ router.post('/resume', (req, res) => {
249
+ ceremonyService.resume();
250
+ res.json({ ok: true });
251
+ });
252
+
253
+ // POST /api/ceremony/cancel
254
+ router.post('/cancel', (req, res) => {
255
+ ceremonyService.cancel();
256
+ res.json({ ok: true });
257
+ });
258
+
259
+ // POST /api/ceremony/cost-limit-continue
260
+ router.post('/cost-limit-continue', (req, res) => {
261
+ ceremonyService.continuePastCostLimit();
262
+ res.json({ ok: true });
263
+ });
264
+
265
+ // POST /api/ceremony/reset — force-stop any running ceremony and reset state immediately
266
+ router.post('/reset', (req, res) => {
267
+ ceremonyService.forceReset();
268
+ res.json({ ok: true });
269
+ });
270
+
271
+ // ── Sponsor-call draft (resume support) ────────────────────────────────────
272
+
273
+ const draftFilePath = () =>
274
+ path.join(ceremonyService.projectRoot, '.avc', 'sponsor-call-draft.json');
275
+
276
+ // GET /api/ceremony/sponsor-call/draft
277
+ router.get('/sponsor-call/draft', (req, res) => {
278
+ try {
279
+ const content = fs.readFileSync(draftFilePath(), 'utf8');
280
+ res.json(JSON.parse(content));
281
+ } catch (_) {
282
+ res.status(404).json({ draft: null });
283
+ }
284
+ });
285
+
286
+ // PUT /api/ceremony/sponsor-call/draft
287
+ router.put('/sponsor-call/draft', (req, res) => {
288
+ try {
289
+ const data = { ...req.body, savedAt: new Date().toISOString() };
290
+ fs.writeFileSync(draftFilePath(), JSON.stringify(data, null, 2));
291
+ res.json({ ok: true });
292
+ } catch (err) {
293
+ console.error('[ceremony] draft save error:', err.message);
294
+ res.status(500).json({ error: err.message });
295
+ }
296
+ });
297
+
298
+ // DELETE /api/ceremony/sponsor-call/draft
299
+ router.delete('/sponsor-call/draft', (req, res) => {
300
+ try { fs.unlinkSync(draftFilePath()); } catch (_) {}
301
+ res.json({ ok: true });
302
+ });
303
+
304
+ return router;
305
+ }
@@ -0,0 +1,157 @@
1
+ import express from 'express';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+
5
+ /**
6
+ * Costs Router
7
+ * Handles GET /api/costs/summary and /api/costs/history
8
+ * Reads token-history.json written by TokenTracker.
9
+ * @param {string} projectRoot - Absolute path to project root
10
+ */
11
+ export function createCostsRouter(projectRoot) {
12
+ const router = express.Router();
13
+ const historyPath = path.join(projectRoot, '.avc', 'token-history.json');
14
+
15
+ // Top-level ceremony names — each becomes a parent node in the hierarchy
16
+ const PARENT_CEREMONIES = ['sponsor-call', 'sprint-planning', 'seed'];
17
+
18
+ // Explicit stage → parent mapping for stages whose names don't carry a prefix
19
+ const STAGE_PARENT_MAP = {
20
+ 'mission-scope': 'sponsor-call',
21
+ 'mission-refine': 'sponsor-call',
22
+ 'analyze-database': 'sponsor-call',
23
+ 'analyze-architecture': 'sponsor-call',
24
+ 'prefill-answers': 'sponsor-call',
25
+ 'sprint-planning-decomposition': 'sprint-planning',
26
+ 'sprint-planning-validation': 'sprint-planning',
27
+ 'sprint-planning-solver': 'sprint-planning',
28
+ 'sprint-planning-doc-distribution':'sprint-planning',
29
+ 'sprint-planning-enrichment': 'sprint-planning',
30
+ };
31
+
32
+ function getParentCeremony(key) {
33
+ if (STAGE_PARENT_MAP[key]) return STAGE_PARENT_MAP[key];
34
+ for (const parent of PARENT_CEREMONIES) {
35
+ if (key !== parent && key.startsWith(`${parent}-`)) return parent;
36
+ }
37
+ if (PARENT_CEREMONIES.includes(key)) return key; // self
38
+ return null;
39
+ }
40
+
41
+ function readHistory() {
42
+ if (!fs.existsSync(historyPath)) return null;
43
+ try {
44
+ return JSON.parse(fs.readFileSync(historyPath, 'utf8'));
45
+ } catch {
46
+ return null;
47
+ }
48
+ }
49
+
50
+ function getCurrentMonthKey() {
51
+ return new Date().toISOString().substring(0, 7); // YYYY-MM
52
+ }
53
+
54
+ // GET /api/costs/summary — current month totals for header chip
55
+ router.get('/summary', (req, res) => {
56
+ const history = readHistory();
57
+ if (!history) return res.json({ totalCost: 0, totalTokens: 0, apiCalls: 0 });
58
+
59
+ const monthKey = getCurrentMonthKey();
60
+ const monthly = history.totals?.monthly?.[monthKey] ?? {};
61
+
62
+ res.json({
63
+ totalCost: monthly.cost?.total ?? 0,
64
+ totalTokens: (monthly.input ?? 0) + (monthly.output ?? 0),
65
+ apiCalls: monthly.executions ?? 0,
66
+ });
67
+ });
68
+
69
+ // GET /api/costs/history?days=30 (or ?from=YYYY-MM-DD&to=YYYY-MM-DD)
70
+ router.get('/history', (req, res) => {
71
+ const history = readHistory();
72
+ if (!history) return res.json({ daily: [], ceremonies: [] });
73
+
74
+ // Determine date range
75
+ let cutoff, endDate;
76
+ if (req.query.from && req.query.to) {
77
+ cutoff = new Date(req.query.from);
78
+ endDate = new Date(req.query.to);
79
+ endDate.setDate(endDate.getDate() + 1); // inclusive end
80
+ } else {
81
+ const days = Math.max(1, Math.min(365, parseInt(req.query.days ?? '30', 10)));
82
+ cutoff = new Date();
83
+ cutoff.setDate(cutoff.getDate() - days);
84
+ endDate = new Date();
85
+ endDate.setDate(endDate.getDate() + 1);
86
+ }
87
+
88
+ // Filter daily totals
89
+ const dailyData = history.totals?.daily ?? {};
90
+ const daily = Object.entries(dailyData)
91
+ .filter(([date]) => {
92
+ const d = new Date(date);
93
+ return d >= cutoff && d < endDate;
94
+ })
95
+ .sort(([a], [b]) => a.localeCompare(b))
96
+ .map(([date, data]) => ({
97
+ date,
98
+ cost: data.cost?.total ?? 0,
99
+ tokens: data.total ?? 0,
100
+ executions: data.executions ?? 0,
101
+ }));
102
+
103
+ // Build parent node skeletons
104
+ const SKIP_KEYS = new Set(['version', 'lastUpdated', 'totals']);
105
+ const parentNodes = {};
106
+ for (const p of PARENT_CEREMONIES) {
107
+ parentNodes[p] = { name: p, calls: 0, tokens: 0, cost: 0, stages: [] };
108
+ }
109
+ const orphans = []; // keys that don't map to a known parent
110
+
111
+ for (const [key, value] of Object.entries(history)) {
112
+ if (SKIP_KEYS.has(key)) continue;
113
+ if (!value || typeof value !== 'object') continue;
114
+
115
+ let totalInput = 0, totalOutput = 0, totalCost = 0, totalExec = 0;
116
+ const dailyForKey = value.daily ?? {};
117
+ for (const [date, data] of Object.entries(dailyForKey)) {
118
+ const d = new Date(date);
119
+ if (d >= cutoff && d < endDate) {
120
+ totalInput += data.input ?? 0;
121
+ totalOutput += data.output ?? 0;
122
+ totalCost += data.cost?.total ?? 0;
123
+ totalExec += data.executions ?? 0;
124
+ }
125
+ }
126
+
127
+ if (totalExec === 0 && totalInput === 0 && totalOutput === 0) continue;
128
+
129
+ const entry = { name: key, calls: totalExec, tokens: totalInput + totalOutput, cost: totalCost };
130
+ const parent = getParentCeremony(key);
131
+
132
+ if (parent && parentNodes[parent]) {
133
+ // Don't add a ceremony as a stage of itself — only add sub-stages
134
+ if (key !== parent) {
135
+ parentNodes[parent].stages.push(entry);
136
+ }
137
+ parentNodes[parent].calls += totalExec;
138
+ parentNodes[parent].tokens += totalInput + totalOutput;
139
+ parentNodes[parent].cost += totalCost;
140
+ } else {
141
+ orphans.push({ ...entry, stages: [] });
142
+ }
143
+ }
144
+
145
+ // Sort stages within each parent by cost desc
146
+ const ceremonies = [
147
+ ...Object.values(parentNodes).filter(c => c.cost > 0 || c.tokens > 0),
148
+ ...orphans,
149
+ ]
150
+ .map(c => ({ ...c, stages: (c.stages || []).sort((a, b) => b.cost - a.cost) }))
151
+ .sort((a, b) => b.cost - a.cost);
152
+
153
+ res.json({ daily, ceremonies });
154
+ });
155
+
156
+ return router;
157
+ }
@@ -0,0 +1,50 @@
1
+ import express from 'express';
2
+
3
+ /**
4
+ * Processes Router
5
+ * REST endpoints for monitoring and controlling forked ceremony/CLI processes.
6
+ * @param {ProcessRegistry} registry
7
+ */
8
+ export function createProcessesRouter(registry) {
9
+ const router = express.Router();
10
+
11
+ // GET /api/processes — list all process DTOs (no logs)
12
+ router.get('/', (req, res) => {
13
+ res.json(registry.list());
14
+ });
15
+
16
+ // GET /api/processes/:id — single process DTO
17
+ router.get('/:id', (req, res) => {
18
+ const dto = registry.getDTO(req.params.id);
19
+ if (!dto) return res.status(404).json({ error: 'Process not found' });
20
+ res.json(dto);
21
+ });
22
+
23
+ // DELETE /api/processes — clear all completed/error/cancelled records
24
+ router.delete('/', (req, res) => {
25
+ registry.clearCompleted();
26
+ res.json({ ok: true });
27
+ });
28
+
29
+ // DELETE /api/processes/:id — kill running process or clear finished record
30
+ router.delete('/:id', (req, res) => {
31
+ const record = registry.get(req.params.id);
32
+ if (!record) return res.status(404).json({ error: 'Process not found' });
33
+ registry.kill(req.params.id);
34
+ res.json({ ok: true });
35
+ });
36
+
37
+ // POST /api/processes/:id/pause
38
+ router.post('/:id/pause', (req, res) => {
39
+ const ok = registry.pause(req.params.id);
40
+ res.json({ ok });
41
+ });
42
+
43
+ // POST /api/processes/:id/resume
44
+ router.post('/:id/resume', (req, res) => {
45
+ const ok = registry.resume(req.params.id);
46
+ res.json({ ok });
47
+ });
48
+
49
+ return router;
50
+ }