@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,516 @@
1
+ import express from 'express';
2
+ import http from 'http';
3
+ import cors from 'cors';
4
+ import path from 'path';
5
+ import fs from 'fs/promises';
6
+ import { fileURLToPath } from 'url';
7
+ import chokidar from 'chokidar';
8
+ import { FileSystemScanner } from './services/FileSystemScanner.js';
9
+ import { WorkItemReader } from './services/WorkItemReader.js';
10
+ import { HierarchyBuilder } from './services/HierarchyBuilder.js';
11
+ import { FileWatcher } from './services/FileWatcher.js';
12
+ import { createWorkItemsRouter } from './routes/work-items.js';
13
+ import { createCeremonyRouter } from './routes/ceremony.js';
14
+ import { createProcessesRouter } from './routes/processes.js';
15
+ import { createSettingsRouter } from './routes/settings.js';
16
+ import { createCostsRouter } from './routes/costs.js';
17
+ import { setupWebSocket } from './routes/websocket.js';
18
+ import { renderMarkdown } from './utils/markdown.js';
19
+ import { CeremonyService } from './services/CeremonyService.js';
20
+ import { ProcessRegistry } from './services/ProcessRegistry.js';
21
+ import { WorkItemRefineService } from './services/WorkItemRefineService.js';
22
+
23
+ /**
24
+ * KanbanServer
25
+ * Express server for AVC Kanban Board
26
+ */
27
+ export class KanbanServer {
28
+ /**
29
+ * @param {string} projectRoot - Absolute path to project root directory
30
+ * @param {object} options - Server options
31
+ */
32
+ constructor(projectRoot, options = {}) {
33
+ this.projectRoot = projectRoot;
34
+ this.port = options.port || 4174;
35
+ this.host = options.host || 'localhost';
36
+
37
+ // Path to pre-built React client
38
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
39
+ this.clientDistPath = path.join(__dirname, '..', 'client', 'dist');
40
+
41
+ // Services
42
+ this.scanner = new FileSystemScanner(projectRoot);
43
+ this.reader = new WorkItemReader(projectRoot);
44
+ this.hierarchyBuilder = new HierarchyBuilder();
45
+ this.fileWatcher = new FileWatcher(projectRoot);
46
+
47
+ // Data store
48
+ this.hierarchy = null;
49
+
50
+ // Ceremony service + process registry
51
+ this.ceremonyService = new CeremonyService(projectRoot);
52
+ this.processRegistry = new ProcessRegistry();
53
+
54
+ // Work item refine service (websocket injected after start())
55
+ this.refineService = new WorkItemRefineService(projectRoot);
56
+
57
+ // Express app
58
+ this.app = express();
59
+ this.server = null;
60
+ this.websocket = null;
61
+
62
+ // Setup middleware
63
+ this.setupMiddleware();
64
+
65
+ // Setup routes
66
+ this.setupRoutes();
67
+ }
68
+
69
+ /**
70
+ * Setup Express middleware
71
+ */
72
+ setupMiddleware() {
73
+ // CORS for frontend development server
74
+ this.app.use(
75
+ cors({
76
+ origin: [`http://localhost:${this.port}`, 'http://localhost:5173', 'http://127.0.0.1:5173'],
77
+ credentials: true,
78
+ })
79
+ );
80
+
81
+ // JSON body parser
82
+ this.app.use(express.json());
83
+
84
+ // Serve pre-built React client
85
+ this.app.use(express.static(this.clientDistPath));
86
+
87
+ // Request logging
88
+ this.app.use((req, res, next) => {
89
+ console.log(`${req.method} ${req.path}`);
90
+ next();
91
+ });
92
+ }
93
+
94
+ /**
95
+ * Setup API routes
96
+ */
97
+ setupRoutes() {
98
+ // Health check
99
+ this.app.get('/api/health', (req, res) => {
100
+ res.json({
101
+ status: 'ok',
102
+ timestamp: Date.now(),
103
+ projectRoot: this.projectRoot,
104
+ });
105
+ });
106
+
107
+ // Statistics
108
+ this.app.get('/api/stats', (req, res) => {
109
+ if (!this.hierarchy) {
110
+ return res.status(503).json({ error: 'Data not loaded yet' });
111
+ }
112
+
113
+ const { items } = this.hierarchy;
114
+ const allItems = Array.from(items.values());
115
+
116
+ const stats = {
117
+ total: allItems.length,
118
+ byType: {},
119
+ byStatus: {},
120
+ };
121
+
122
+ allItems.forEach((item) => {
123
+ // Count by type
124
+ const type = item._type;
125
+ stats.byType[type] = (stats.byType[type] || 0) + 1;
126
+
127
+ // Count by status
128
+ const status = item.status;
129
+ stats.byStatus[status] = (stats.byStatus[status] || 0) + 1;
130
+ });
131
+
132
+ res.json(stats);
133
+ });
134
+
135
+ // Manual reload endpoint
136
+ this.app.post('/api/reload', async (req, res) => {
137
+ try {
138
+ await this.reloadWorkItems();
139
+ const count = this.hierarchy ? this.hierarchy.items.size : 0;
140
+ res.json({ status: 'ok', workItemCount: count });
141
+ if (this.websocket) {
142
+ this.websocket.broadcastRefresh();
143
+ }
144
+ } catch (error) {
145
+ console.error('Error during manual reload:', error);
146
+ res.status(500).json({ error: 'Reload failed', message: error.message });
147
+ }
148
+ });
149
+
150
+ // Project-level doc.md and context.md (root .avc/project/ files)
151
+ const projectPath = path.join(this.projectRoot, '.avc', 'project');
152
+
153
+ const readProjectFile = async (filename) => {
154
+ try {
155
+ return await fs.readFile(path.join(projectPath, filename), 'utf8');
156
+ } catch {
157
+ return null;
158
+ }
159
+ };
160
+
161
+ this.app.get('/api/project/doc', async (req, res) => {
162
+ const md = await readProjectFile('doc.md');
163
+ if (!md) return res.status(404).json({ error: 'Project doc.md not found' });
164
+ res.send(renderMarkdown(md));
165
+ });
166
+
167
+ this.app.get('/api/project/doc/raw', async (req, res) => {
168
+ const md = await readProjectFile('doc.md');
169
+ if (!md) return res.status(404).json({ error: 'Project doc.md not found' });
170
+ res.type('text/plain').send(md);
171
+ });
172
+
173
+ this.app.put('/api/project/doc', async (req, res) => {
174
+ const { content } = req.body;
175
+ if (typeof content !== 'string') return res.status(400).json({ error: 'content must be a string' });
176
+ await fs.writeFile(path.join(projectPath, 'doc.md'), content, 'utf8');
177
+ res.json({ status: 'ok' });
178
+ });
179
+
180
+ this.app.get('/api/project/status', async (req, res) => {
181
+ const docExists = await fs.access(path.join(projectPath, 'doc.md')).then(() => true).catch(() => false);
182
+ res.json({ docExists });
183
+ });
184
+
185
+ // Settings router (GET /api/settings + PUT sub-routes)
186
+ const settingsRouter = createSettingsRouter(this.projectRoot);
187
+ this.app.use('/api/settings', settingsRouter);
188
+
189
+ // Board title setting (read/write from avc.json)
190
+ const avcJsonPath = path.join(this.projectRoot, '.avc', 'avc.json');
191
+ const DEFAULT_TITLE = 'AVC Kanban Board';
192
+
193
+ const readAvcConfig = async () => {
194
+ try {
195
+ return JSON.parse(await fs.readFile(avcJsonPath, 'utf8'));
196
+ } catch {
197
+ return {};
198
+ }
199
+ };
200
+
201
+ this.app.get('/api/settings/title', async (req, res) => {
202
+ const config = await readAvcConfig();
203
+ res.json({ title: config?.settings?.kanban?.title || DEFAULT_TITLE });
204
+ });
205
+
206
+ this.app.get('/api/settings/docs-url', async (req, res) => {
207
+ const config = await readAvcConfig();
208
+ const docsPort = config?.settings?.documentation?.port || 4173;
209
+ res.json({ url: `http://localhost:${docsPort}` });
210
+ });
211
+
212
+ this.app.put('/api/settings/title', async (req, res) => {
213
+ const { title } = req.body;
214
+ if (typeof title !== 'string' || !title.trim()) {
215
+ return res.status(400).json({ error: 'title must be a non-empty string' });
216
+ }
217
+ try {
218
+ const config = await readAvcConfig();
219
+ if (!config.settings) config.settings = {};
220
+ if (!config.settings.kanban) config.settings.kanban = {};
221
+ config.settings.kanban.title = title.trim();
222
+ await fs.writeFile(avcJsonPath, JSON.stringify(config, null, 2), 'utf8');
223
+ res.json({ title: title.trim() });
224
+ } catch (err) {
225
+ res.status(500).json({ error: err.message });
226
+ }
227
+ });
228
+
229
+ // Work items routes
230
+ const workItemsRouter = createWorkItemsRouter(this, this.refineService);
231
+ this.app.use('/api/work-items', workItemsRouter);
232
+
233
+ // Ceremony routes
234
+ const ceremonyRouter = createCeremonyRouter(this.ceremonyService, this.processRegistry);
235
+ this.app.use('/api/ceremony', ceremonyRouter);
236
+
237
+ // Process monitor routes
238
+ const processesRouter = createProcessesRouter(this.processRegistry);
239
+ this.app.use('/api/processes', processesRouter);
240
+
241
+ // Costs routes
242
+ const costsRouter = createCostsRouter(this.projectRoot);
243
+ this.app.use('/api/costs', costsRouter);
244
+
245
+ // SPA fallback — serve index.html for any non-API GET
246
+ this.app.get('*', (req, res) => {
247
+ res.sendFile(path.join(this.clientDistPath, 'index.html'));
248
+ });
249
+
250
+ // 404 handler
251
+ this.app.use((req, res) => {
252
+ res.status(404).json({ error: 'Not found' });
253
+ });
254
+
255
+ // Error handler
256
+ this.app.use((err, req, res, next) => {
257
+ console.error('Server error:', err);
258
+ res.status(500).json({ error: 'Internal server error' });
259
+ });
260
+ }
261
+
262
+ /**
263
+ * Load work items from file system
264
+ */
265
+ async loadWorkItems() {
266
+ console.log('Loading work items...');
267
+
268
+ try {
269
+ // Scan for work.json files
270
+ const workFiles = await this.scanner.scan();
271
+ console.log(`Found ${workFiles.length} work items`);
272
+
273
+ if (workFiles.length === 0) {
274
+ console.warn('No work items found in .avc/project/');
275
+ this.hierarchy = { items: new Map(), roots: [] };
276
+ return;
277
+ }
278
+
279
+ // Read all work items
280
+ const workItems = await this.reader.readAllWorkItems(workFiles);
281
+ console.log(`Successfully read ${workItems.length} work items`);
282
+
283
+ // Build hierarchy
284
+ this.hierarchy = this.hierarchyBuilder.buildHierarchy(workItems);
285
+ console.log(`Built hierarchy with ${this.hierarchy.roots.length} root epics`);
286
+ } catch (error) {
287
+ console.error('Error loading work items:', error);
288
+ throw error;
289
+ }
290
+ }
291
+
292
+ /**
293
+ * Setup file watcher for real-time updates
294
+ */
295
+ setupFileWatcher() {
296
+ console.log('Setting up file watcher...');
297
+
298
+ this.fileWatcher.on('ready', () => {
299
+ console.log('File watcher ready');
300
+ });
301
+
302
+ this.fileWatcher.on('added', async (filePath) => {
303
+ console.log(`Work item added: ${filePath}`);
304
+ await this.reloadWorkItems();
305
+ if (this.websocket) {
306
+ this.websocket.broadcastRefresh();
307
+ }
308
+ });
309
+
310
+ this.fileWatcher.on('changed', async (filePath) => {
311
+ console.log(`Work item changed: ${filePath}`);
312
+ await this.reloadWorkItems();
313
+ if (this.websocket) {
314
+ this.websocket.broadcastRefresh();
315
+ }
316
+ });
317
+
318
+ this.fileWatcher.on('deleted', async (filePath) => {
319
+ console.log(`Work item deleted: ${filePath}`);
320
+ await this.reloadWorkItems();
321
+ if (this.websocket) {
322
+ this.websocket.broadcastRefresh();
323
+ }
324
+ });
325
+
326
+ this.fileWatcher.on('error', (error) => {
327
+ console.error('File watcher error:', error);
328
+ });
329
+
330
+ this.fileWatcher.start();
331
+
332
+ // Watch doc.md for changes → sync to .avc/documentation/index.md
333
+ // so vitepress dev picks up the change and hot-reloads the browser
334
+ const docMdPath = path.join(this.projectRoot, '.avc', 'project', 'doc.md');
335
+ const docsIndexPath = path.join(this.projectRoot, '.avc', 'documentation', 'index.md');
336
+
337
+ const docWatcher = chokidar.watch(docMdPath, {
338
+ persistent: true,
339
+ ignoreInitial: true,
340
+ usePolling: true,
341
+ interval: 2000,
342
+ awaitWriteFinish: { stabilityThreshold: 500, pollInterval: 100 },
343
+ });
344
+
345
+ docWatcher.on('change', async () => {
346
+ try {
347
+ const docsDir = path.join(this.projectRoot, '.avc', 'documentation');
348
+ const docsDirExists = await fs.access(docsDir).then(() => true).catch(() => false);
349
+ if (!docsDirExists) return; // documentation not set up yet
350
+ const content = await fs.readFile(docMdPath, 'utf8');
351
+ await fs.writeFile(docsIndexPath, content, 'utf8');
352
+ console.log('[doc-watcher] Synced doc.md → documentation/index.md');
353
+ } catch (err) {
354
+ console.error('[doc-watcher] Sync failed:', err.message);
355
+ }
356
+ });
357
+
358
+ docWatcher.on('error', (err) => {
359
+ console.error('[doc-watcher] Error:', err.message);
360
+ });
361
+
362
+ // Watch token-history.json → broadcast cost:update so the dashboard
363
+ // chip refreshes immediately after any API call writes token usage.
364
+ const tokenHistoryPath = path.join(this.projectRoot, '.avc', 'token-history.json');
365
+ const tokenWatcher = chokidar.watch(tokenHistoryPath, {
366
+ persistent: true,
367
+ ignoreInitial: true,
368
+ usePolling: true,
369
+ interval: 1000,
370
+ awaitWriteFinish: { stabilityThreshold: 300, pollInterval: 100 },
371
+ });
372
+
373
+ tokenWatcher.on('change', () => {
374
+ if (this.websocket) {
375
+ this.websocket.broadcastCostUpdate();
376
+ }
377
+ });
378
+
379
+ tokenWatcher.on('error', (err) => {
380
+ console.error('[token-watcher] Error:', err.message);
381
+ });
382
+ }
383
+
384
+ /**
385
+ * Reload work items from file system
386
+ */
387
+ async reloadWorkItems() {
388
+ try {
389
+ await this.loadWorkItems();
390
+ } catch (error) {
391
+ console.error('Error reloading work items:', error);
392
+ }
393
+ }
394
+
395
+ /**
396
+ * Get current hierarchy
397
+ * @returns {object} Current hierarchy
398
+ */
399
+ getHierarchy() {
400
+ return this.hierarchy || { items: new Map(), roots: [] };
401
+ }
402
+
403
+ /**
404
+ * Get full details for a work item
405
+ * @param {object} item - Work item
406
+ * @returns {Promise<object>} Full details
407
+ */
408
+ async getFullDetails(item) {
409
+ return await this.reader.getFullDetails(item);
410
+ }
411
+
412
+ /**
413
+ * Start the server
414
+ */
415
+ async start() {
416
+ try {
417
+ // Load initial data
418
+ await this.loadWorkItems();
419
+
420
+ // Create HTTP server
421
+ this.server = http.createServer(this.app);
422
+
423
+ // Setup WebSocket (pass processRegistry for init message to new clients)
424
+ this.websocket = setupWebSocket(this.server, this, this.processRegistry, this.ceremonyService);
425
+
426
+ // Wire ceremony service to WebSocket for broadcasting
427
+ this.ceremonyService.setWebSocket(this.websocket);
428
+ this.ceremonyService.setReloadCallback(() => this.reloadWorkItems());
429
+
430
+ // Wire refine service to WebSocket
431
+ this.refineService.websocket = this.websocket;
432
+
433
+ // Wire ProcessRegistry events → WebSocket broadcasts
434
+ this.processRegistry.on('created', (record) => {
435
+ this.websocket?.broadcastProcessStarted(record);
436
+ });
437
+ this.processRegistry.on('status', (processId, status, record) => {
438
+ this.websocket?.broadcastProcessStatus(processId, status, {
439
+ result: record.result,
440
+ error: record.error,
441
+ endedAt: record.endedAt,
442
+ });
443
+ });
444
+
445
+ // Setup file watcher
446
+ this.setupFileWatcher();
447
+
448
+ // Start listening
449
+ await new Promise((resolve, reject) => {
450
+ this.server.listen(this.port, this.host, (error) => {
451
+ if (error) {
452
+ reject(error);
453
+ } else {
454
+ console.log(`\nKanban server listening on http://${this.host}:${this.port}`);
455
+ console.log(`WebSocket available at ws://${this.host}:${this.port}/ws`);
456
+ console.log(`API endpoints:`);
457
+ console.log(` GET /api/health`);
458
+ console.log(` GET /api/stats`);
459
+ console.log(` GET /api/work-items`);
460
+ console.log(` GET /api/work-items/grouped`);
461
+ console.log(` GET /api/work-items/:id`);
462
+ console.log(` GET /api/work-items/:id/doc\n`);
463
+ resolve();
464
+ }
465
+ });
466
+ });
467
+ } catch (error) {
468
+ console.error('Error starting server:', error);
469
+ throw error;
470
+ }
471
+ }
472
+
473
+ /**
474
+ * Stop the server
475
+ */
476
+ async stop() {
477
+ console.log('Stopping kanban server...');
478
+
479
+ // Stop file watcher
480
+ if (this.fileWatcher) {
481
+ await this.fileWatcher.stop();
482
+ }
483
+
484
+ // Close WebSocket connections
485
+ if (this.websocket && this.websocket.wss) {
486
+ this.websocket.wss.clients.forEach((client) => {
487
+ client.close();
488
+ });
489
+ this.websocket.wss.close();
490
+ }
491
+
492
+ // Close HTTP server
493
+ if (this.server) {
494
+ await new Promise((resolve) => {
495
+ this.server.close(() => {
496
+ console.log('Kanban server stopped');
497
+ resolve();
498
+ });
499
+ });
500
+ }
501
+ }
502
+
503
+ /**
504
+ * Get server status
505
+ * @returns {object} Server status
506
+ */
507
+ getStatus() {
508
+ return {
509
+ running: this.server !== null,
510
+ port: this.port,
511
+ host: this.host,
512
+ workItemCount: this.hierarchy ? this.hierarchy.items.size : 0,
513
+ websocketClients: this.websocket ? this.websocket.getClientCount() : 0,
514
+ };
515
+ }
516
+ }