@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,347 @@
1
+ import express from 'express';
2
+ import fs from 'fs/promises';
3
+ import fsSync from 'fs';
4
+ import path from 'path';
5
+ import { renderMarkdown, extractDescriptionFromDoc } from '../utils/markdown.js';
6
+ import { groupItemsByColumn, getColumnStats } from '../utils/status-grouping.js';
7
+
8
+ /**
9
+ * Create work items router
10
+ * @param {object} dataStore - Data store with work items
11
+ * @returns {express.Router}
12
+ */
13
+ export function createWorkItemsRouter(dataStore, refineService) {
14
+ const router = express.Router();
15
+
16
+ /**
17
+ * GET /api/work-items
18
+ * Get all work items as hierarchical JSON
19
+ */
20
+ router.get('/', (req, res) => {
21
+ try {
22
+ const { items, roots } = dataStore.getHierarchy();
23
+
24
+ // Convert Map to array
25
+ const allItems = Array.from(items.values());
26
+
27
+ // Apply filters if provided
28
+ let filtered = allItems;
29
+
30
+ // Filter by type
31
+ if (req.query.type) {
32
+ const types = req.query.type.split(',');
33
+ filtered = filtered.filter((item) => types.includes(item._type));
34
+ }
35
+
36
+ // Filter by status
37
+ if (req.query.status) {
38
+ const statuses = req.query.status.split(',');
39
+ filtered = filtered.filter((item) => statuses.includes(item.status));
40
+ }
41
+
42
+ // Search query
43
+ if (req.query.search) {
44
+ const search = req.query.search.toLowerCase();
45
+ filtered = filtered.filter(
46
+ (item) =>
47
+ item.name.toLowerCase().includes(search) ||
48
+ item.id.toLowerCase().includes(search) ||
49
+ (item.description && item.description.toLowerCase().includes(search))
50
+ );
51
+ }
52
+
53
+ // Clean items (remove circular references for JSON serialization)
54
+ const cleanItems = filtered.map((item) => cleanWorkItem(item));
55
+
56
+ res.json({
57
+ items: cleanItems,
58
+ total: cleanItems.length,
59
+ roots: roots.map((r) => r.id),
60
+ });
61
+ } catch (error) {
62
+ console.error('Error getting work items:', error);
63
+ res.status(500).json({ error: 'Failed to get work items' });
64
+ }
65
+ });
66
+
67
+ /**
68
+ * GET /api/work-items/grouped
69
+ * Get work items grouped by column
70
+ */
71
+ router.get('/grouped', (req, res) => {
72
+ try {
73
+ const { items } = dataStore.getHierarchy();
74
+ const allItems = Array.from(items.values());
75
+
76
+ // Group by column
77
+ const grouped = groupItemsByColumn(allItems);
78
+
79
+ // Add statistics for each column
80
+ const result = {};
81
+ for (const [column, columnItems] of Object.entries(grouped)) {
82
+ result[column] = {
83
+ items: columnItems.map((item) => cleanWorkItem(item)),
84
+ stats: getColumnStats(columnItems),
85
+ };
86
+ }
87
+
88
+ res.json(result);
89
+ } catch (error) {
90
+ console.error('Error getting grouped work items:', error);
91
+ res.status(500).json({ error: 'Failed to get grouped work items' });
92
+ }
93
+ });
94
+
95
+ /**
96
+ * GET /api/work-items/:id
97
+ * Get single work item with full details
98
+ */
99
+ router.get('/:id', async (req, res) => {
100
+ try {
101
+ const { items } = dataStore.getHierarchy();
102
+ const item = items.get(req.params.id);
103
+
104
+ if (!item) {
105
+ return res.status(404).json({ error: 'Work item not found' });
106
+ }
107
+
108
+ // Get full details (including doc.md and context.md)
109
+ const fullItem = await dataStore.getFullDetails(item);
110
+
111
+ res.json(cleanWorkItem(fullItem, true));
112
+ } catch (error) {
113
+ console.error(`Error getting work item ${req.params.id}:`, error);
114
+ res.status(500).json({ error: 'Failed to get work item' });
115
+ }
116
+ });
117
+
118
+ /**
119
+ * GET /api/work-items/:id/doc
120
+ * Get rendered documentation (doc.md) as HTML
121
+ */
122
+ router.get('/:id/doc', async (req, res) => {
123
+ try {
124
+ const { items } = dataStore.getHierarchy();
125
+ const item = items.get(req.params.id);
126
+
127
+ if (!item) {
128
+ return res.status(404).json({ error: 'Work item not found' });
129
+ }
130
+
131
+ const fullItem = await dataStore.getFullDetails(item);
132
+
133
+ if (!fullItem.documentation) {
134
+ return res.status(404).json({ error: 'Documentation not found' });
135
+ }
136
+
137
+ const html = renderMarkdown(fullItem.documentation);
138
+ res.send(html);
139
+ } catch (error) {
140
+ console.error(`Error getting documentation for ${req.params.id}:`, error);
141
+ res.status(500).json({ error: 'Failed to get documentation' });
142
+ }
143
+ });
144
+
145
+ /**
146
+ * GET /api/work-items/:id/doc/raw
147
+ * Get raw markdown source of doc.md
148
+ */
149
+ router.get('/:id/doc/raw', async (req, res) => {
150
+ try {
151
+ const { items } = dataStore.getHierarchy();
152
+ const item = items.get(req.params.id);
153
+ if (!item) return res.status(404).json({ error: 'Work item not found' });
154
+
155
+ const fullItem = await dataStore.getFullDetails(item);
156
+ if (!fullItem.documentation) return res.status(404).json({ error: 'Documentation not found' });
157
+
158
+ res.type('text/plain').send(fullItem.documentation);
159
+ } catch (error) {
160
+ console.error(`Error getting raw doc for ${req.params.id}:`, error);
161
+ res.status(500).json({ error: 'Failed to get documentation' });
162
+ }
163
+ });
164
+
165
+ /**
166
+ * POST /api/work-items/:id/refine
167
+ * Start an async refinement job for an epic or story.
168
+ * Body: { refinementRequest?, selectedIssues?, modelId, provider, validatorModelId, validatorProvider }
169
+ * Returns: { jobId }
170
+ */
171
+ router.post('/:id/refine', async (req, res) => {
172
+ try {
173
+ if (!refineService) {
174
+ return res.status(503).json({ error: 'Refine service not available' });
175
+ }
176
+ const { items } = dataStore.getHierarchy();
177
+ const item = items.get(req.params.id);
178
+ if (!item) return res.status(404).json({ error: 'Work item not found' });
179
+
180
+ const { refinementRequest, selectedIssues, modelId, provider, validatorModelId, validatorProvider } =
181
+ req.body;
182
+
183
+ if (!modelId || !provider || !validatorModelId || !validatorProvider) {
184
+ return res.status(400).json({
185
+ error: 'modelId, provider, validatorModelId and validatorProvider are required',
186
+ });
187
+ }
188
+
189
+ const fullItem = await dataStore.getFullDetails(item);
190
+ const cleanItem = cleanWorkItem(fullItem, true);
191
+
192
+ const jobId = await refineService.startRefine(req.params.id, cleanItem, {
193
+ refinementRequest: refinementRequest || '',
194
+ selectedIssues: selectedIssues || [],
195
+ modelId,
196
+ provider,
197
+ validatorModelId,
198
+ validatorProvider,
199
+ itemDirPath: item._dirPath,
200
+ });
201
+
202
+ res.json({ jobId });
203
+ } catch (err) {
204
+ console.error(`Error starting refine for ${req.params.id}:`, err);
205
+ res.status(500).json({ error: err.message });
206
+ }
207
+ });
208
+
209
+ /**
210
+ * PUT /api/work-items/:id
211
+ * Apply accepted refinement changes to work.json on disk.
212
+ * Body: { proposedItem, storyChanges? }
213
+ */
214
+ router.put('/:id', async (req, res) => {
215
+ try {
216
+ if (!refineService) {
217
+ return res.status(503).json({ error: 'Refine service not available' });
218
+ }
219
+ const { proposedItem, storyChanges } = req.body;
220
+ if (!proposedItem) return res.status(400).json({ error: 'proposedItem is required' });
221
+
222
+ // Build a dirPath map from the in-memory data store so applyChanges doesn't
223
+ // need to walk the filesystem (which can silently fail).
224
+ const { items } = dataStore.getHierarchy();
225
+ const item = items.get(req.params.id);
226
+ if (!item) return res.status(404).json({ error: 'Work item not found' });
227
+
228
+ const dirPathMap = new Map([[item.id, item._dirPath]]);
229
+ for (const change of (storyChanges || [])) {
230
+ if (change.storyId) {
231
+ const storyItem = items.get(change.storyId);
232
+ if (storyItem) dirPathMap.set(change.storyId, storyItem._dirPath);
233
+ }
234
+ }
235
+
236
+ await refineService.applyChanges(req.params.id, proposedItem, storyChanges || [], dirPathMap);
237
+ res.json({ status: 'ok' });
238
+ } catch (err) {
239
+ console.error(`Error applying changes for ${req.params.id}:`, err);
240
+ res.status(500).json({ error: err.message });
241
+ }
242
+ });
243
+
244
+ /**
245
+ * PUT /api/work-items/:id/doc
246
+ * Save updated markdown content to doc.md
247
+ */
248
+ router.put('/:id/doc', async (req, res) => {
249
+ try {
250
+ const { items } = dataStore.getHierarchy();
251
+ const item = items.get(req.params.id);
252
+ if (!item) return res.status(404).json({ error: 'Work item not found' });
253
+
254
+ const markdown = req.body.content;
255
+ if (typeof markdown !== 'string') {
256
+ return res.status(400).json({ error: 'content must be a string' });
257
+ }
258
+
259
+ const docPath = path.join(item._dirPath, 'doc.md');
260
+ await fs.writeFile(docPath, markdown, 'utf8');
261
+
262
+ // Sync work.json description cache so kanban cards stay in sync
263
+ const newDescription = extractDescriptionFromDoc(markdown);
264
+ if (newDescription) {
265
+ const workJsonPath = path.join(item._dirPath, 'work.json');
266
+ if (fsSync.existsSync(workJsonPath)) {
267
+ const workJson = JSON.parse(fsSync.readFileSync(workJsonPath, 'utf8'));
268
+ workJson.description = newDescription;
269
+ fsSync.writeFileSync(workJsonPath, JSON.stringify(workJson, null, 2), 'utf8');
270
+ }
271
+ }
272
+
273
+ res.json({ status: 'ok' });
274
+ } catch (error) {
275
+ console.error(`Error saving doc for ${req.params.id}:`, error);
276
+ res.status(500).json({ error: 'Failed to save documentation' });
277
+ }
278
+ });
279
+
280
+ return router;
281
+ }
282
+
283
+ /**
284
+ * Clean work item for JSON serialization
285
+ * Removes circular references and internal fields
286
+ * @param {object} item - Work item
287
+ * @param {boolean} includeFullDetails - Include documentation and context
288
+ * @returns {object} Cleaned work item
289
+ */
290
+ function cleanWorkItem(item, includeFullDetails = false) {
291
+ const cleaned = {
292
+ id: item.id,
293
+ name: item.name,
294
+ type: item._type,
295
+ status: item.status,
296
+ description: item.description,
297
+ dependencies: item.dependencies || [],
298
+ metadata: item.metadata,
299
+ created: item.created,
300
+ updated: item.updated,
301
+ // Type-specific fields used for diff display in RefineWorkItemPopup
302
+ features: item.features, // epics
303
+ acceptance: item.acceptance, // stories
304
+ userType: item.userType, // stories
305
+ domain: item.domain, // epics
306
+ };
307
+
308
+ // Add parent reference (ID only, not full object)
309
+ if (item._parentId) {
310
+ cleaned.parentId = item._parentId;
311
+ cleaned.parentName = item._parent?.name;
312
+ }
313
+
314
+ // Add children references (IDs only)
315
+ if (item._children && item._children.length > 0) {
316
+ cleaned.children = item._children.map((child) => ({
317
+ id: child.id,
318
+ name: child.name,
319
+ type: child._type,
320
+ status: child.status,
321
+ }));
322
+ }
323
+
324
+ // Add epic reference for nested items
325
+ if (item._type !== 'epic' && item._parent) {
326
+ let epic = item._parent;
327
+ while (epic._parent) {
328
+ epic = epic._parent;
329
+ }
330
+ if (epic._type === 'epic') {
331
+ cleaned.epicId = epic.id;
332
+ cleaned.epicName = epic.name;
333
+ }
334
+ }
335
+
336
+ // Include full details if requested
337
+ if (includeFullDetails) {
338
+ if (item.documentation) {
339
+ cleaned.documentation = item.documentation;
340
+ }
341
+ if (item.context) {
342
+ cleaned.context = item.context;
343
+ }
344
+ }
345
+
346
+ return cleaned;
347
+ }