@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,94 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ STATUS_COLUMN_MAPPING,
4
+ STATUS_METADATA,
5
+ COLUMN_ORDER,
6
+ getColumnForStatus,
7
+ getStatusMetadata,
8
+ groupItemsByColumn,
9
+ getColumnStats,
10
+ } from '../status-grouping';
11
+
12
+ describe('status-grouping', () => {
13
+ describe('getColumnForStatus', () => {
14
+ it('should return correct column for each status', () => {
15
+ expect(getColumnForStatus('planned')).toBe('Backlog');
16
+ expect(getColumnForStatus('pending')).toBe('Backlog');
17
+ expect(getColumnForStatus('ready')).toBe('Ready');
18
+ expect(getColumnForStatus('implementing')).toBe('In Progress');
19
+ expect(getColumnForStatus('feedback')).toBe('In Progress');
20
+ expect(getColumnForStatus('implemented')).toBe('Review');
21
+ expect(getColumnForStatus('testing')).toBe('Review');
22
+ expect(getColumnForStatus('completed')).toBe('Done');
23
+ });
24
+
25
+ it('should return null for unknown status', () => {
26
+ expect(getColumnForStatus('unknown')).toBeNull();
27
+ });
28
+ });
29
+
30
+ describe('getStatusMetadata', () => {
31
+ it('should return metadata for valid status', () => {
32
+ const metadata = getStatusMetadata('ready');
33
+ expect(metadata).toHaveProperty('color');
34
+ expect(metadata).toHaveProperty('icon');
35
+ expect(metadata).toHaveProperty('label');
36
+ expect(metadata.color).toBe('blue');
37
+ expect(metadata.label).toBe('Ready');
38
+ });
39
+
40
+ it('should return null for unknown status', () => {
41
+ expect(getStatusMetadata('unknown')).toBeNull();
42
+ });
43
+ });
44
+
45
+ describe('groupItemsByColumn', () => {
46
+ it('should group items by column correctly', () => {
47
+ const workItems = [
48
+ { id: '1', status: 'planned' },
49
+ { id: '2', status: 'ready' },
50
+ { id: '3', status: 'implementing' },
51
+ { id: '4', status: 'completed' },
52
+ { id: '5', status: 'pending' },
53
+ ];
54
+
55
+ const grouped = groupItemsByColumn(workItems);
56
+
57
+ expect(grouped.Backlog).toHaveLength(2);
58
+ expect(grouped.Ready).toHaveLength(1);
59
+ expect(grouped['In Progress']).toHaveLength(1);
60
+ expect(grouped.Review).toHaveLength(0);
61
+ expect(grouped.Done).toHaveLength(1);
62
+ });
63
+
64
+ it('should initialize all columns as empty arrays', () => {
65
+ const grouped = groupItemsByColumn([]);
66
+
67
+ COLUMN_ORDER.forEach((column) => {
68
+ expect(grouped[column]).toEqual([]);
69
+ });
70
+ });
71
+ });
72
+
73
+ describe('getColumnStats', () => {
74
+ it('should calculate correct statistics', () => {
75
+ const workItems = [
76
+ { status: 'planned' },
77
+ { status: 'planned' },
78
+ { status: 'pending' },
79
+ ];
80
+
81
+ const stats = getColumnStats(workItems);
82
+
83
+ expect(stats.total).toBe(3);
84
+ expect(stats.byStatus.planned).toBe(2);
85
+ expect(stats.byStatus.pending).toBe(1);
86
+ });
87
+
88
+ it('should handle empty array', () => {
89
+ const stats = getColumnStats([]);
90
+ expect(stats.total).toBe(0);
91
+ expect(stats.byStatus).toEqual({});
92
+ });
93
+ });
94
+ });
@@ -0,0 +1,401 @@
1
+ /**
2
+ * API Client for AVC Kanban Board
3
+ * Communicates with the backend Express server
4
+ */
5
+
6
+ const API_BASE_URL = import.meta.env.VITE_API_URL || '/api';
7
+
8
+ /**
9
+ * Fetch wrapper with error handling
10
+ * @param {string} endpoint - API endpoint (relative to /api)
11
+ * @param {object} options - Fetch options
12
+ * @returns {Promise<any>} Response data
13
+ */
14
+ async function apiFetch(endpoint, options = {}) {
15
+ const url = `${API_BASE_URL}${endpoint}`;
16
+
17
+ try {
18
+ const response = await fetch(url, {
19
+ ...options,
20
+ headers: {
21
+ 'Content-Type': 'application/json',
22
+ ...options.headers,
23
+ },
24
+ });
25
+
26
+ if (!response.ok) {
27
+ const error = await response.json().catch(() => ({}));
28
+ throw new Error(error.error || `HTTP ${response.status}: ${response.statusText}`);
29
+ }
30
+
31
+ return await response.json();
32
+ } catch (error) {
33
+ console.error(`API Error [${endpoint}]:`, error);
34
+ throw error;
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Get health check status
40
+ * @returns {Promise<object>} Health status
41
+ */
42
+ export async function getHealth() {
43
+ return apiFetch('/health');
44
+ }
45
+
46
+ /**
47
+ * Get statistics (counts by status/type)
48
+ * @returns {Promise<object>} Statistics
49
+ */
50
+ export async function getStats() {
51
+ return apiFetch('/stats');
52
+ }
53
+
54
+ /**
55
+ * Get all work items
56
+ * @param {object} filters - Filter options
57
+ * @param {string} filters.type - Filter by type (comma-separated: epic,story,task,subtask)
58
+ * @param {string} filters.status - Filter by status (comma-separated)
59
+ * @param {string} filters.search - Search query
60
+ * @returns {Promise<object>} Work items data
61
+ */
62
+ export async function getWorkItems(filters = {}) {
63
+ const params = new URLSearchParams();
64
+
65
+ if (filters.type) {
66
+ params.append('type', filters.type);
67
+ }
68
+ if (filters.status) {
69
+ params.append('status', filters.status);
70
+ }
71
+ if (filters.search) {
72
+ params.append('search', filters.search);
73
+ }
74
+
75
+ const query = params.toString();
76
+ const endpoint = query ? `/work-items?${query}` : '/work-items';
77
+
78
+ return apiFetch(endpoint);
79
+ }
80
+
81
+ /**
82
+ * Get work items grouped by column
83
+ * @returns {Promise<object>} Grouped work items
84
+ */
85
+ export async function getWorkItemsGrouped() {
86
+ return apiFetch('/work-items/grouped');
87
+ }
88
+
89
+ /**
90
+ * Get single work item with full details
91
+ * @param {string} id - Work item ID
92
+ * @returns {Promise<object>} Work item details
93
+ */
94
+ export async function getWorkItem(id) {
95
+ return apiFetch(`/work-items/${id}`);
96
+ }
97
+
98
+ /**
99
+ * Get rendered documentation (doc.md) as HTML
100
+ * @param {string} id - Work item ID
101
+ * @returns {Promise<string>} HTML content or empty string if not found
102
+ */
103
+ export async function getWorkItemDoc(id) {
104
+ const url = `${API_BASE_URL}/work-items/${id}/doc`;
105
+ const response = await fetch(url);
106
+
107
+ if (!response.ok) {
108
+ return ''; // Return empty string on error (404, etc.)
109
+ }
110
+
111
+ return await response.text();
112
+ }
113
+
114
+ /**
115
+ * Get raw markdown source of doc.md
116
+ * @param {string} id - Work item ID
117
+ * @returns {Promise<string>} Raw markdown or empty string if not found
118
+ */
119
+ export async function getWorkItemDocRaw(id) {
120
+ const url = `${API_BASE_URL}/work-items/${id}/doc/raw`;
121
+ const response = await fetch(url);
122
+ if (!response.ok) return '';
123
+ return await response.text();
124
+ }
125
+
126
+ /**
127
+ * Save updated markdown to doc.md
128
+ * @param {string} id - Work item ID
129
+ * @param {string} content - Markdown content
130
+ */
131
+ export async function updateWorkItemDoc(id, content) {
132
+ return apiFetch(`/work-items/${id}/doc`, {
133
+ method: 'PUT',
134
+ body: JSON.stringify({ content }),
135
+ });
136
+ }
137
+
138
+ // ── Board settings ───────────────────────────────────────────────────────────
139
+
140
+ /**
141
+ * Get board title from avc.json (falls back to 'AVC Kanban Board')
142
+ */
143
+ export async function getBoardTitle() {
144
+ const data = await apiFetch('/settings/title');
145
+ return data.title || 'AVC Kanban Board';
146
+ }
147
+
148
+ export async function getDocsUrl() {
149
+ const data = await apiFetch('/settings/docs-url');
150
+ return data.url || 'http://localhost:4173';
151
+ }
152
+
153
+ /**
154
+ * Save board title to avc.json
155
+ */
156
+ export async function updateBoardTitle(title) {
157
+ return apiFetch('/settings/title', {
158
+ method: 'PUT',
159
+ body: JSON.stringify({ title }),
160
+ });
161
+ }
162
+
163
+ // ── Project-level (root) files ──────────────────────────────────────────────
164
+
165
+ export async function getProjectDoc() {
166
+ const url = `${API_BASE_URL}/project/doc`;
167
+ const response = await fetch(url);
168
+ if (!response.ok) return '';
169
+ return response.text();
170
+ }
171
+
172
+ export async function getProjectDocRaw() {
173
+ const url = `${API_BASE_URL}/project/doc/raw`;
174
+ const response = await fetch(url);
175
+ if (!response.ok) return '';
176
+ return response.text();
177
+ }
178
+
179
+ export async function updateProjectDoc(content) {
180
+ return apiFetch('/project/doc', { method: 'PUT', body: JSON.stringify({ content }) });
181
+ }
182
+
183
+ export async function getProjectStatus() {
184
+ return apiFetch('/project/status');
185
+ }
186
+
187
+ // ── Settings API ─────────────────────────────────────────────────────────────
188
+
189
+ export async function getSettings() {
190
+ return apiFetch('/settings');
191
+ }
192
+
193
+ export async function saveApiKeys(keys) {
194
+ return apiFetch('/settings/api-keys', { method: 'PUT', body: JSON.stringify(keys) });
195
+ }
196
+
197
+ export async function saveCeremonies(ceremonies, missionGenerator) {
198
+ return apiFetch('/settings/ceremonies', { method: 'PUT', body: JSON.stringify({ ceremonies, missionGenerator }) });
199
+ }
200
+
201
+ export async function saveGeneralSettings(data) {
202
+ return apiFetch('/settings/general', { method: 'PUT', body: JSON.stringify(data) });
203
+ }
204
+
205
+ export async function saveModelPricing(models) {
206
+ return apiFetch('/settings/models', { method: 'PUT', body: JSON.stringify({ models }) });
207
+ }
208
+
209
+ export async function saveCostThresholds(thresholds) {
210
+ return apiFetch('/settings/cost-thresholds', { method: 'PUT', body: JSON.stringify({ thresholds }) });
211
+ }
212
+
213
+ // ── Ceremony API ─────────────────────────────────────────────────────────────
214
+
215
+ export async function getCeremonyStatus() {
216
+ return apiFetch('/ceremony/status');
217
+ }
218
+
219
+ export async function analyzeDatabase(mission, scope, strategy) {
220
+ return apiFetch('/ceremony/analyze/database', {
221
+ method: 'POST',
222
+ body: JSON.stringify({ mission, scope, strategy }),
223
+ });
224
+ }
225
+
226
+ export async function analyzeArchitecture(mission, scope, dbContext, strategy) {
227
+ return apiFetch('/ceremony/analyze/architecture', {
228
+ method: 'POST',
229
+ body: JSON.stringify({ mission, scope, dbContext, strategy }),
230
+ });
231
+ }
232
+
233
+ export async function prefillAnswers(mission, scope, arch, dbContext, strategy) {
234
+ return apiFetch('/ceremony/analyze/prefill', {
235
+ method: 'POST',
236
+ body: JSON.stringify({ mission, scope, arch, dbContext, strategy }),
237
+ });
238
+ }
239
+
240
+ export async function runCeremony(requirements) {
241
+ return apiFetch('/ceremony/run', {
242
+ method: 'POST',
243
+ body: JSON.stringify({ requirements }),
244
+ });
245
+ }
246
+
247
+ export async function runSprintPlanning() {
248
+ return apiFetch('/ceremony/sprint-planning/run', { method: 'POST' });
249
+ }
250
+
251
+ export const confirmSprintPlanningSelection = (selectedEpicIds, selectedStoryIds) =>
252
+ apiFetch('/ceremony/sprint-planning/confirm-selection', {
253
+ method: 'POST',
254
+ body: JSON.stringify({ selectedEpicIds, selectedStoryIds }),
255
+ });
256
+
257
+ export const pauseCeremony = () => apiFetch('/ceremony/pause', { method: 'POST' });
258
+ export const resumeCeremony = () => apiFetch('/ceremony/resume', { method: 'POST' });
259
+ export const cancelCeremony = () => apiFetch('/ceremony/cancel', { method: 'POST' });
260
+ export const resetCeremony = () => apiFetch('/ceremony/reset', { method: 'POST' });
261
+ export const continuePastCostLimit = () => apiFetch('/ceremony/cost-limit-continue', { method: 'POST' });
262
+
263
+ export async function getModels() {
264
+ return apiFetch('/ceremony/models');
265
+ }
266
+
267
+ export async function generateMission(description, modelId, provider, validatorModelId, validatorProvider) {
268
+ return apiFetch('/ceremony/generate-mission', {
269
+ method: 'POST',
270
+ body: JSON.stringify({ description, modelId, provider, validatorModelId, validatorProvider }),
271
+ });
272
+ }
273
+
274
+ export async function refineMission(missionStatement, initialScope, refinementRequest, modelId, provider, validatorModelId, validatorProvider) {
275
+ return apiFetch('/ceremony/refine-mission', {
276
+ method: 'POST',
277
+ body: JSON.stringify({ missionStatement, initialScope, refinementRequest, modelId, provider, validatorModelId, validatorProvider }),
278
+ });
279
+ }
280
+
281
+ export async function generateArchitecture(description, modelId, provider) {
282
+ return apiFetch('/ceremony/generate-architecture', {
283
+ method: 'POST',
284
+ body: JSON.stringify({ description, modelId, provider }),
285
+ });
286
+ }
287
+
288
+ export async function refineArchitecture(currentArch, refinementRequest, modelId, provider) {
289
+ return apiFetch('/ceremony/refine-architecture', {
290
+ method: 'POST',
291
+ body: JSON.stringify({ currentArch, refinementRequest, modelId, provider }),
292
+ });
293
+ }
294
+
295
+ // ── Sponsor-call draft (resume support) ──────────────────────────────────────
296
+
297
+ export async function getSponsorCallDraft() {
298
+ try {
299
+ return await apiFetch('/ceremony/sponsor-call/draft');
300
+ } catch (_) {
301
+ return null;
302
+ }
303
+ }
304
+
305
+ export async function saveSponsorCallDraft(data) {
306
+ return apiFetch('/ceremony/sponsor-call/draft', {
307
+ method: 'PUT',
308
+ body: JSON.stringify(data),
309
+ });
310
+ }
311
+
312
+ export async function deleteSponsorCallDraft() {
313
+ try {
314
+ return await apiFetch('/ceremony/sponsor-call/draft', { method: 'DELETE' });
315
+ } catch (_) {}
316
+ }
317
+
318
+ // ── Agents API ───────────────────────────────────────────────────────────────
319
+
320
+ export async function getAgentList() {
321
+ return apiFetch('/settings/agents');
322
+ }
323
+
324
+ export async function getAgentContent(name) {
325
+ return apiFetch(`/settings/agents/${encodeURIComponent(name)}`);
326
+ }
327
+
328
+ export async function saveAgentContent(name, content) {
329
+ return apiFetch(`/settings/agents/${encodeURIComponent(name)}`, {
330
+ method: 'PUT',
331
+ body: JSON.stringify({ content }),
332
+ });
333
+ }
334
+
335
+ export async function resetAgent(name) {
336
+ return apiFetch(`/settings/agents/${encodeURIComponent(name)}`, { method: 'DELETE' });
337
+ }
338
+
339
+ // ── Processes API ─────────────────────────────────────────────────────────────
340
+
341
+ export async function getProcesses() {
342
+ return apiFetch('/processes');
343
+ }
344
+
345
+ export async function killProcess(id) {
346
+ return apiFetch(`/processes/${encodeURIComponent(id)}`, { method: 'DELETE' });
347
+ }
348
+
349
+ export async function clearCompletedProcesses() {
350
+ return apiFetch('/processes', { method: 'DELETE' });
351
+ }
352
+
353
+ // ── Work Item Refine API ──────────────────────────────────────────────────────
354
+
355
+ /**
356
+ * Start an async refinement job for an epic or story.
357
+ * @param {string} id - Work item ID
358
+ * @param {object} options - { refinementRequest, selectedIssues, modelId, provider, validatorModelId, validatorProvider }
359
+ * @returns {Promise<{ jobId: string }>}
360
+ */
361
+ export async function refineWorkItem(id, options) {
362
+ return apiFetch(`/work-items/${id}/refine`, {
363
+ method: 'POST',
364
+ body: JSON.stringify(options),
365
+ });
366
+ }
367
+
368
+ /**
369
+ * Apply accepted refinement changes to work.json on disk.
370
+ * @param {string} id - Work item ID
371
+ * @param {object} proposedItem - The proposed (refined) item
372
+ * @param {Array} storyChanges - [{ type: 'update'|'new', storyId?, proposedStory }]
373
+ */
374
+ export async function applyWorkItemChanges(id, proposedItem, storyChanges = []) {
375
+ return apiFetch(`/work-items/${id}`, {
376
+ method: 'PUT',
377
+ body: JSON.stringify({ proposedItem, storyChanges }),
378
+ });
379
+ }
380
+
381
+ // ── Costs API ─────────────────────────────────────────────────────────────────
382
+
383
+ /**
384
+ * Get current month cost summary for the header chip
385
+ * @returns {Promise<{ totalCost: number, totalTokens: number, apiCalls: number }>}
386
+ */
387
+ export async function getCostSummary() {
388
+ return apiFetch('/costs/summary');
389
+ }
390
+
391
+ /**
392
+ * Get cost history for chart and ceremony breakdown
393
+ * @param {number|{ from: string, to: string }} range - Days count or custom date range
394
+ * @returns {Promise<{ daily: Array, ceremonies: Array }>}
395
+ */
396
+ export async function getCostHistory(range = 30) {
397
+ if (typeof range === 'number') {
398
+ return apiFetch(`/costs/history?days=${range}`);
399
+ }
400
+ return apiFetch(`/costs/history?from=${range.from}&to=${range.to}`);
401
+ }
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Status Column Grouping Logic (Frontend)
3
+ * Mirrors backend logic for consistency
4
+ */
5
+
6
+ /**
7
+ * Maps status values to their display column
8
+ */
9
+ export const STATUS_COLUMN_MAPPING = {
10
+ Backlog: ['planned', 'pending'],
11
+ Ready: ['ready'],
12
+ 'In Progress': ['implementing', 'feedback'],
13
+ Review: ['implemented', 'testing'],
14
+ Done: ['completed'],
15
+ };
16
+
17
+ /**
18
+ * Metadata for each status value
19
+ */
20
+ export const STATUS_METADATA = {
21
+ planned: { color: 'gray', icon: 'πŸ“‹', label: 'Planned' },
22
+ pending: { color: 'slate', icon: '⏸️', label: 'Pending' },
23
+ ready: { color: 'blue', icon: 'πŸš€', label: 'Ready' },
24
+ implementing: { color: 'yellow', icon: 'βš™οΈ', label: 'Implementing' },
25
+ feedback: { color: 'amber', icon: 'πŸ’¬', label: 'Feedback' },
26
+ implemented: { color: 'purple', icon: 'βœ…', label: 'Implemented' },
27
+ testing: { color: 'violet', icon: 'πŸ§ͺ', label: 'Testing' },
28
+ completed: { color: 'green', icon: 'πŸŽ‰', label: 'Completed' },
29
+ blocked: { color: 'red', icon: '🚫', label: 'Blocked' },
30
+ };
31
+
32
+ /**
33
+ * Canonical order for columns on the board
34
+ */
35
+ export const COLUMN_ORDER = ['Backlog', 'Ready', 'In Progress', 'Review', 'Done'];
36
+
37
+ /**
38
+ * Column metadata
39
+ */
40
+ export const COLUMN_METADATA = {
41
+ Backlog: {
42
+ color: 'slate',
43
+ bgColor: 'bg-slate-100',
44
+ borderColor: 'border-slate-300',
45
+ },
46
+ Ready: {
47
+ color: 'blue',
48
+ bgColor: 'bg-blue-100',
49
+ borderColor: 'border-blue-300',
50
+ },
51
+ 'In Progress': {
52
+ color: 'yellow',
53
+ bgColor: 'bg-yellow-100',
54
+ borderColor: 'border-yellow-300',
55
+ },
56
+ Review: {
57
+ color: 'purple',
58
+ bgColor: 'bg-purple-100',
59
+ borderColor: 'border-purple-300',
60
+ },
61
+ Done: {
62
+ color: 'green',
63
+ bgColor: 'bg-green-100',
64
+ borderColor: 'border-green-300',
65
+ },
66
+ };
67
+
68
+ /**
69
+ * Get the column name for a given status
70
+ * @param {string} status - Work item status
71
+ * @returns {string|null} Column name or null if not found
72
+ */
73
+ export function getColumnForStatus(status) {
74
+ for (const [column, statuses] of Object.entries(STATUS_COLUMN_MAPPING)) {
75
+ if (statuses.includes(status)) {
76
+ return column;
77
+ }
78
+ }
79
+ return null;
80
+ }
81
+
82
+ /**
83
+ * Get metadata for a status
84
+ * @param {string} status - Work item status
85
+ * @returns {object|null} Status metadata or null if not found
86
+ */
87
+ export function getStatusMetadata(status) {
88
+ return STATUS_METADATA[status] || null;
89
+ }
90
+
91
+ /**
92
+ * Get metadata for a column
93
+ * @param {string} column - Column name
94
+ * @returns {object|null} Column metadata or null if not found
95
+ */
96
+ export function getColumnMetadata(column) {
97
+ return COLUMN_METADATA[column] || null;
98
+ }
99
+
100
+ /**
101
+ * Group work items by column
102
+ * @param {Array} workItems - Array of work items
103
+ * @returns {object} Object with column names as keys, arrays of work items as values
104
+ */
105
+ export function groupItemsByColumn(workItems) {
106
+ const grouped = {};
107
+
108
+ // Initialize all columns as empty arrays
109
+ COLUMN_ORDER.forEach((column) => {
110
+ grouped[column] = [];
111
+ });
112
+
113
+ // Group items by their column
114
+ workItems.forEach((item) => {
115
+ const column = getColumnForStatus(item.status);
116
+ if (column && grouped[column]) {
117
+ grouped[column].push(item);
118
+ }
119
+ });
120
+
121
+ return grouped;
122
+ }
123
+
124
+ /**
125
+ * Get statistics for a column
126
+ * @param {Array} workItems - Work items in the column
127
+ * @returns {object} Statistics object with total count and breakdown by status
128
+ */
129
+ export function getColumnStats(workItems) {
130
+ const stats = {
131
+ total: workItems.length,
132
+ byStatus: {},
133
+ };
134
+
135
+ workItems.forEach((item) => {
136
+ const status = item.status;
137
+ if (!stats.byStatus[status]) {
138
+ stats.byStatus[status] = 0;
139
+ }
140
+ stats.byStatus[status]++;
141
+ });
142
+
143
+ return stats;
144
+ }
@@ -0,0 +1,11 @@
1
+ import { clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ /**
5
+ * Utility function to merge Tailwind CSS classes
6
+ * @param {...any} inputs - Class names to merge
7
+ * @returns {string} Merged class names
8
+ */
9
+ export function cn(...inputs) {
10
+ return twMerge(clsx(inputs));
11
+ }
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom/client';
3
+ import App from './App.jsx';
4
+ import './styles/globals.css';
5
+
6
+ ReactDOM.createRoot(document.getElementById('root')).render(
7
+ <React.StrictMode>
8
+ <App />
9
+ </React.StrictMode>
10
+ );