@agile-vibe-coding/avc 0.1.1 โ†’ 0.3.1

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 (239) hide show
  1. package/cli/agent-loader.js +21 -0
  2. package/cli/agents/agent-selector.md +152 -0
  3. package/cli/agents/architecture-recommender.md +418 -0
  4. package/cli/agents/code-implementer.md +117 -0
  5. package/cli/agents/code-validator.md +80 -0
  6. package/cli/agents/context-reviewer-epic.md +101 -0
  7. package/cli/agents/context-reviewer-story.md +92 -0
  8. package/cli/agents/context-writer-epic.md +145 -0
  9. package/cli/agents/context-writer-story.md +111 -0
  10. package/cli/agents/database-deep-dive.md +470 -0
  11. package/cli/agents/database-recommender.md +634 -0
  12. package/cli/agents/doc-distributor.md +176 -0
  13. package/cli/agents/doc-writer-epic.md +42 -0
  14. package/cli/agents/doc-writer-story.md +43 -0
  15. package/cli/agents/documentation-updater.md +203 -0
  16. package/cli/agents/duplicate-detector.md +110 -0
  17. package/cli/agents/epic-story-decomposer.md +559 -0
  18. package/cli/agents/feature-context-generator.md +91 -0
  19. package/cli/agents/gap-checker-epic.md +52 -0
  20. package/cli/agents/impact-checker-story.md +51 -0
  21. package/cli/agents/migration-guide-generator.md +305 -0
  22. package/cli/agents/mission-scope-generator.md +143 -0
  23. package/cli/agents/mission-scope-validator.md +146 -0
  24. package/cli/agents/project-context-extractor.md +122 -0
  25. package/cli/agents/project-documentation-creator.json +226 -0
  26. package/cli/agents/project-documentation-creator.md +595 -0
  27. package/cli/agents/question-prefiller.md +269 -0
  28. package/cli/agents/refiner-epic.md +39 -0
  29. package/cli/agents/refiner-story.md +42 -0
  30. package/cli/agents/scaffolding-generator.md +99 -0
  31. package/cli/agents/seed-validator.md +71 -0
  32. package/cli/agents/story-doc-enricher.md +133 -0
  33. package/cli/agents/story-scope-reviewer.md +147 -0
  34. package/cli/agents/story-splitter.md +83 -0
  35. package/cli/agents/suggestion-business-analyst.md +88 -0
  36. package/cli/agents/suggestion-deployment-architect.md +263 -0
  37. package/cli/agents/suggestion-product-manager.md +129 -0
  38. package/cli/agents/suggestion-security-specialist.md +156 -0
  39. package/cli/agents/suggestion-technical-architect.md +269 -0
  40. package/cli/agents/suggestion-ux-researcher.md +93 -0
  41. package/cli/agents/task-subtask-decomposer.md +188 -0
  42. package/cli/agents/validator-documentation.json +183 -0
  43. package/cli/agents/validator-documentation.md +455 -0
  44. package/cli/agents/validator-selector.md +211 -0
  45. package/cli/ansi-colors.js +21 -0
  46. package/cli/api-reference-tool.js +368 -0
  47. package/cli/build-docs.js +29 -8
  48. package/cli/ceremony-history.js +369 -0
  49. package/cli/checks/catalog.json +76 -0
  50. package/cli/checks/code/quality.json +26 -0
  51. package/cli/checks/code/testing.json +14 -0
  52. package/cli/checks/code/traceability.json +26 -0
  53. package/cli/checks/cross-refs/epic.json +171 -0
  54. package/cli/checks/cross-refs/story.json +149 -0
  55. package/cli/checks/epic/api.json +114 -0
  56. package/cli/checks/epic/backend.json +126 -0
  57. package/cli/checks/epic/cloud.json +126 -0
  58. package/cli/checks/epic/data.json +102 -0
  59. package/cli/checks/epic/database.json +114 -0
  60. package/cli/checks/epic/developer.json +182 -0
  61. package/cli/checks/epic/devops.json +174 -0
  62. package/cli/checks/epic/frontend.json +162 -0
  63. package/cli/checks/epic/mobile.json +102 -0
  64. package/cli/checks/epic/qa.json +90 -0
  65. package/cli/checks/epic/security.json +184 -0
  66. package/cli/checks/epic/solution-architect.json +192 -0
  67. package/cli/checks/epic/test-architect.json +90 -0
  68. package/cli/checks/epic/ui.json +102 -0
  69. package/cli/checks/epic/ux.json +90 -0
  70. package/cli/checks/fixes/epic-fix-template.md +10 -0
  71. package/cli/checks/fixes/story-fix-template.md +10 -0
  72. package/cli/checks/story/api.json +186 -0
  73. package/cli/checks/story/backend.json +102 -0
  74. package/cli/checks/story/cloud.json +102 -0
  75. package/cli/checks/story/data.json +210 -0
  76. package/cli/checks/story/database.json +102 -0
  77. package/cli/checks/story/developer.json +168 -0
  78. package/cli/checks/story/devops.json +102 -0
  79. package/cli/checks/story/frontend.json +174 -0
  80. package/cli/checks/story/mobile.json +102 -0
  81. package/cli/checks/story/qa.json +210 -0
  82. package/cli/checks/story/security.json +198 -0
  83. package/cli/checks/story/solution-architect.json +230 -0
  84. package/cli/checks/story/test-architect.json +210 -0
  85. package/cli/checks/story/ui.json +102 -0
  86. package/cli/checks/story/ux.json +102 -0
  87. package/cli/coding-order.js +401 -0
  88. package/cli/command-logger.js +49 -12
  89. package/cli/components/static-output.js +63 -0
  90. package/cli/console-output-manager.js +94 -0
  91. package/cli/dependency-checker.js +72 -0
  92. package/cli/docs-sync.js +306 -0
  93. package/cli/epic-story-validator.js +659 -0
  94. package/cli/evaluation-prompts.js +1008 -0
  95. package/cli/execution-context.js +195 -0
  96. package/cli/generate-summary-table.js +340 -0
  97. package/cli/init-model-config.js +704 -0
  98. package/cli/init.js +1737 -278
  99. package/cli/kanban-server-manager.js +227 -0
  100. package/cli/llm-claude.js +150 -1
  101. package/cli/llm-gemini.js +109 -0
  102. package/cli/llm-local.js +493 -0
  103. package/cli/llm-mock.js +233 -0
  104. package/cli/llm-openai.js +454 -0
  105. package/cli/llm-provider.js +379 -3
  106. package/cli/llm-token-limits.js +211 -0
  107. package/cli/llm-verifier.js +662 -0
  108. package/cli/llm-xiaomi.js +143 -0
  109. package/cli/message-constants.js +49 -0
  110. package/cli/message-manager.js +334 -0
  111. package/cli/message-types.js +96 -0
  112. package/cli/messaging-api.js +291 -0
  113. package/cli/micro-check-fixer.js +335 -0
  114. package/cli/micro-check-runner.js +449 -0
  115. package/cli/micro-check-scorer.js +148 -0
  116. package/cli/micro-check-validator.js +538 -0
  117. package/cli/model-pricing.js +192 -0
  118. package/cli/model-query-engine.js +468 -0
  119. package/cli/model-recommendation-analyzer.js +495 -0
  120. package/cli/model-selector.js +270 -0
  121. package/cli/output-buffer.js +107 -0
  122. package/cli/process-manager.js +73 -2
  123. package/cli/prompt-logger.js +57 -0
  124. package/cli/repl-ink.js +4625 -1094
  125. package/cli/repl-old.js +3 -4
  126. package/cli/seed-processor.js +962 -0
  127. package/cli/sprint-planning-processor.js +4162 -0
  128. package/cli/template-processor.js +2149 -105
  129. package/cli/templates/project.md +25 -8
  130. package/cli/templates/vitepress-config.mts.template +5 -4
  131. package/cli/token-tracker.js +547 -0
  132. package/cli/tools/generate-story-validators.js +317 -0
  133. package/cli/tools/generate-validators.js +669 -0
  134. package/cli/update-checker.js +19 -17
  135. package/cli/update-notifier.js +4 -4
  136. package/cli/validation-router.js +667 -0
  137. package/cli/verification-tracker.js +563 -0
  138. package/cli/worktree-runner.js +654 -0
  139. package/kanban/README.md +386 -0
  140. package/kanban/client/README.md +205 -0
  141. package/kanban/client/components.json +20 -0
  142. package/kanban/client/dist/assets/index-D_KC5EQT.css +1 -0
  143. package/kanban/client/dist/assets/index-DjY5zqW7.js +351 -0
  144. package/kanban/client/dist/index.html +16 -0
  145. package/kanban/client/dist/vite.svg +1 -0
  146. package/kanban/client/index.html +15 -0
  147. package/kanban/client/package-lock.json +9442 -0
  148. package/kanban/client/package.json +44 -0
  149. package/kanban/client/postcss.config.js +6 -0
  150. package/kanban/client/public/vite.svg +1 -0
  151. package/kanban/client/src/App.jsx +651 -0
  152. package/kanban/client/src/components/ProjectFileEditorPopup.jsx +117 -0
  153. package/kanban/client/src/components/ceremony/AskArchPopup.jsx +420 -0
  154. package/kanban/client/src/components/ceremony/AskModelPopup.jsx +629 -0
  155. package/kanban/client/src/components/ceremony/CeremonyWorkflowModal.jsx +1133 -0
  156. package/kanban/client/src/components/ceremony/EpicStorySelectionModal.jsx +254 -0
  157. package/kanban/client/src/components/ceremony/ProviderSwitcherButton.jsx +290 -0
  158. package/kanban/client/src/components/ceremony/SponsorCallModal.jsx +686 -0
  159. package/kanban/client/src/components/ceremony/SprintPlanningModal.jsx +838 -0
  160. package/kanban/client/src/components/ceremony/steps/ArchitectureStep.jsx +150 -0
  161. package/kanban/client/src/components/ceremony/steps/CompleteStep.jsx +136 -0
  162. package/kanban/client/src/components/ceremony/steps/DatabaseStep.jsx +202 -0
  163. package/kanban/client/src/components/ceremony/steps/DeploymentStep.jsx +123 -0
  164. package/kanban/client/src/components/ceremony/steps/MissionStep.jsx +106 -0
  165. package/kanban/client/src/components/ceremony/steps/ReviewAnswersStep.jsx +329 -0
  166. package/kanban/client/src/components/ceremony/steps/RunningStep.jsx +249 -0
  167. package/kanban/client/src/components/kanban/CardDetailModal.jsx +646 -0
  168. package/kanban/client/src/components/kanban/EpicSection.jsx +146 -0
  169. package/kanban/client/src/components/kanban/FilterToolbar.jsx +222 -0
  170. package/kanban/client/src/components/kanban/GroupingSelector.jsx +63 -0
  171. package/kanban/client/src/components/kanban/KanbanBoard.jsx +211 -0
  172. package/kanban/client/src/components/kanban/KanbanCard.jsx +147 -0
  173. package/kanban/client/src/components/kanban/KanbanColumn.jsx +90 -0
  174. package/kanban/client/src/components/kanban/RefineWorkItemPopup.jsx +784 -0
  175. package/kanban/client/src/components/kanban/RunButton.jsx +162 -0
  176. package/kanban/client/src/components/kanban/SeedButton.jsx +176 -0
  177. package/kanban/client/src/components/layout/LoadingScreen.jsx +82 -0
  178. package/kanban/client/src/components/process/ProcessMonitorBar.jsx +80 -0
  179. package/kanban/client/src/components/settings/AgentEditorPopup.jsx +171 -0
  180. package/kanban/client/src/components/settings/AgentsTab.jsx +381 -0
  181. package/kanban/client/src/components/settings/ApiKeysTab.jsx +142 -0
  182. package/kanban/client/src/components/settings/CeremonyModelsTab.jsx +105 -0
  183. package/kanban/client/src/components/settings/CheckEditorPopup.jsx +507 -0
  184. package/kanban/client/src/components/settings/CostThresholdsTab.jsx +95 -0
  185. package/kanban/client/src/components/settings/ModelPricingTab.jsx +269 -0
  186. package/kanban/client/src/components/settings/OpenAIAuthSection.jsx +412 -0
  187. package/kanban/client/src/components/settings/ServersTab.jsx +121 -0
  188. package/kanban/client/src/components/settings/SettingsModal.jsx +84 -0
  189. package/kanban/client/src/components/stats/CostModal.jsx +384 -0
  190. package/kanban/client/src/components/ui/badge.jsx +27 -0
  191. package/kanban/client/src/components/ui/dialog.jsx +121 -0
  192. package/kanban/client/src/components/ui/tabs.jsx +85 -0
  193. package/kanban/client/src/hooks/__tests__/useGrouping.test.js +232 -0
  194. package/kanban/client/src/hooks/useGrouping.js +177 -0
  195. package/kanban/client/src/hooks/useWebSocket.js +120 -0
  196. package/kanban/client/src/lib/__tests__/api.test.js +196 -0
  197. package/kanban/client/src/lib/__tests__/status-grouping.test.js +94 -0
  198. package/kanban/client/src/lib/api.js +515 -0
  199. package/kanban/client/src/lib/status-grouping.js +154 -0
  200. package/kanban/client/src/lib/utils.js +11 -0
  201. package/kanban/client/src/main.jsx +10 -0
  202. package/kanban/client/src/store/__tests__/kanbanStore.test.js +164 -0
  203. package/kanban/client/src/store/ceremonyStore.js +172 -0
  204. package/kanban/client/src/store/filterStore.js +201 -0
  205. package/kanban/client/src/store/kanbanStore.js +123 -0
  206. package/kanban/client/src/store/processStore.js +65 -0
  207. package/kanban/client/src/store/sprintPlanningStore.js +33 -0
  208. package/kanban/client/src/styles/globals.css +59 -0
  209. package/kanban/client/tailwind.config.js +77 -0
  210. package/kanban/client/vite.config.js +28 -0
  211. package/kanban/client/vitest.config.js +28 -0
  212. package/kanban/dev-start.sh +47 -0
  213. package/kanban/package.json +12 -0
  214. package/kanban/server/index.js +537 -0
  215. package/kanban/server/routes/ceremony.js +454 -0
  216. package/kanban/server/routes/costs.js +163 -0
  217. package/kanban/server/routes/openai-oauth.js +366 -0
  218. package/kanban/server/routes/processes.js +50 -0
  219. package/kanban/server/routes/settings.js +736 -0
  220. package/kanban/server/routes/websocket.js +281 -0
  221. package/kanban/server/routes/work-items.js +487 -0
  222. package/kanban/server/services/CeremonyService.js +1441 -0
  223. package/kanban/server/services/FileSystemScanner.js +95 -0
  224. package/kanban/server/services/FileWatcher.js +144 -0
  225. package/kanban/server/services/HierarchyBuilder.js +196 -0
  226. package/kanban/server/services/ProcessRegistry.js +122 -0
  227. package/kanban/server/services/TaskRunnerService.js +261 -0
  228. package/kanban/server/services/WorkItemReader.js +123 -0
  229. package/kanban/server/services/WorkItemRefineService.js +510 -0
  230. package/kanban/server/start.js +49 -0
  231. package/kanban/server/utils/kanban-logger.js +132 -0
  232. package/kanban/server/utils/markdown.js +91 -0
  233. package/kanban/server/utils/status-grouping.js +107 -0
  234. package/kanban/server/workers/run-task-worker.js +121 -0
  235. package/kanban/server/workers/seed-worker.js +94 -0
  236. package/kanban/server/workers/sponsor-call-worker.js +92 -0
  237. package/kanban/server/workers/sprint-planning-worker.js +212 -0
  238. package/package.json +19 -7
  239. package/cli/agents/documentation.md +0 -302
@@ -0,0 +1,515 @@
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(resumeFrom = null) {
248
+ return apiFetch('/ceremony/sprint-planning/run', {
249
+ method: 'POST',
250
+ ...(resumeFrom ? { body: JSON.stringify({ resumeFrom }) } : {}),
251
+ });
252
+ }
253
+
254
+ export async function getSprintPlanningResumable() {
255
+ return apiFetch('/ceremony/sprint-planning/resumable');
256
+ }
257
+
258
+ export const confirmSprintPlanningSelection = (selectedEpicIds, selectedStoryIds) =>
259
+ apiFetch('/ceremony/sprint-planning/confirm-selection', {
260
+ method: 'POST',
261
+ body: JSON.stringify({ selectedEpicIds, selectedStoryIds }),
262
+ });
263
+
264
+ export const pauseCeremony = () => apiFetch('/ceremony/pause', { method: 'POST' });
265
+ export const resumeCeremony = () => apiFetch('/ceremony/resume', { method: 'POST' });
266
+ export const cancelCeremony = (keepItems = false) => apiFetch('/ceremony/cancel', { method: 'POST', body: JSON.stringify({ keepItems }) });
267
+ export const resetCeremony = () => apiFetch('/ceremony/reset', { method: 'POST' });
268
+ export const continuePastCostLimit = () => apiFetch('/ceremony/cost-limit-continue', { method: 'POST' });
269
+ export const continueAfterQuota = (newProvider = null, newModel = null) =>
270
+ apiFetch('/ceremony/quota-continue', { method: 'POST', body: JSON.stringify({ newProvider, newModel }) });
271
+
272
+ export async function getModels() {
273
+ const [models, local] = await Promise.all([
274
+ apiFetch('/ceremony/models'),
275
+ apiFetch('/settings/local-models').catch(() => ({ servers: [] })),
276
+ ]);
277
+ // Merge discovered local models into the list
278
+ const localModels = (local.servers || []).flatMap((srv) =>
279
+ srv.models.map((lm) => ({
280
+ modelId: lm.id,
281
+ displayName: `${lm.id} (${srv.label})`,
282
+ provider: 'local',
283
+ hasApiKey: true,
284
+ }))
285
+ );
286
+ return [...models, ...localModels];
287
+ }
288
+
289
+ export async function getLocalModels() {
290
+ return apiFetch('/settings/local-models');
291
+ }
292
+
293
+ export async function generateMission(description, modelId, provider, validatorModelId, validatorProvider) {
294
+ return apiFetch('/ceremony/generate-mission', {
295
+ method: 'POST',
296
+ body: JSON.stringify({ description, modelId, provider, validatorModelId, validatorProvider }),
297
+ });
298
+ }
299
+
300
+ export async function refineMission(missionStatement, initialScope, refinementRequest, modelId, provider, validatorModelId, validatorProvider) {
301
+ return apiFetch('/ceremony/refine-mission', {
302
+ method: 'POST',
303
+ body: JSON.stringify({ missionStatement, initialScope, refinementRequest, modelId, provider, validatorModelId, validatorProvider }),
304
+ });
305
+ }
306
+
307
+ export async function generateArchitecture(description, modelId, provider) {
308
+ return apiFetch('/ceremony/generate-architecture', {
309
+ method: 'POST',
310
+ body: JSON.stringify({ description, modelId, provider }),
311
+ });
312
+ }
313
+
314
+ export async function refineArchitecture(currentArch, refinementRequest, modelId, provider) {
315
+ return apiFetch('/ceremony/refine-architecture', {
316
+ method: 'POST',
317
+ body: JSON.stringify({ currentArch, refinementRequest, modelId, provider }),
318
+ });
319
+ }
320
+
321
+ export async function refineField(fieldKey, fieldLabel, currentValue, refinementRequest, context, modelId, provider) {
322
+ return apiFetch('/ceremony/refine-field', {
323
+ method: 'POST',
324
+ body: JSON.stringify({ fieldKey, fieldLabel, currentValue, refinementRequest, context, modelId, provider }),
325
+ });
326
+ }
327
+
328
+ // โ”€โ”€ Sponsor-call draft (resume support) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
329
+
330
+ export async function getSponsorCallDraft() {
331
+ try {
332
+ return await apiFetch('/ceremony/sponsor-call/draft');
333
+ } catch (_) {
334
+ return null;
335
+ }
336
+ }
337
+
338
+ export async function saveSponsorCallDraft(data) {
339
+ return apiFetch('/ceremony/sponsor-call/draft', {
340
+ method: 'PUT',
341
+ body: JSON.stringify(data),
342
+ });
343
+ }
344
+
345
+ export async function deleteSponsorCallDraft() {
346
+ try {
347
+ return await apiFetch('/ceremony/sponsor-call/draft', { method: 'DELETE' });
348
+ } catch (_) {}
349
+ }
350
+
351
+ // โ”€โ”€ Agents API โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
352
+
353
+ export async function getAgentList() {
354
+ return apiFetch('/settings/agents');
355
+ }
356
+
357
+ export async function getAgentContent(name) {
358
+ return apiFetch(`/settings/agents/${encodeURIComponent(name)}`);
359
+ }
360
+
361
+ export async function saveAgentContent(name, content) {
362
+ return apiFetch(`/settings/agents/${encodeURIComponent(name)}`, {
363
+ method: 'PUT',
364
+ body: JSON.stringify({ content }),
365
+ });
366
+ }
367
+
368
+ export async function resetAgent(name) {
369
+ return apiFetch(`/settings/agents/${encodeURIComponent(name)}`, { method: 'DELETE' });
370
+ }
371
+
372
+ // โ”€โ”€ Micro-Check Definitions API โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
373
+
374
+ export async function getCheckList() {
375
+ return apiFetch('/settings/checks');
376
+ }
377
+
378
+ export async function getCheckContent(scope, perspective) {
379
+ return apiFetch(`/settings/checks/${encodeURIComponent(scope)}/${encodeURIComponent(perspective)}`);
380
+ }
381
+
382
+ export async function saveCheckContent(scope, perspective, content) {
383
+ return apiFetch(`/settings/checks/${encodeURIComponent(scope)}/${encodeURIComponent(perspective)}`, {
384
+ method: 'PUT',
385
+ body: JSON.stringify({ content }),
386
+ });
387
+ }
388
+
389
+ export async function resetCheck(scope, perspective) {
390
+ return apiFetch(`/settings/checks/${encodeURIComponent(scope)}/${encodeURIComponent(perspective)}`, { method: 'DELETE' });
391
+ }
392
+
393
+ // โ”€โ”€ Processes API โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
394
+
395
+ export async function getProcesses() {
396
+ return apiFetch('/processes');
397
+ }
398
+
399
+ export async function killProcess(id) {
400
+ return apiFetch(`/processes/${encodeURIComponent(id)}`, { method: 'DELETE' });
401
+ }
402
+
403
+ export async function clearCompletedProcesses() {
404
+ return apiFetch('/processes', { method: 'DELETE' });
405
+ }
406
+
407
+ // โ”€โ”€ Work Item Refine API โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
408
+
409
+ /**
410
+ * Start an async refinement job for an epic or story.
411
+ * @param {string} id - Work item ID
412
+ * @param {object} options - { refinementRequest, selectedIssues, modelId, provider, validatorModelId, validatorProvider }
413
+ * @returns {Promise<{ jobId: string }>}
414
+ */
415
+ export async function refineWorkItem(id, options) {
416
+ return apiFetch(`/work-items/${id}/refine`, {
417
+ method: 'POST',
418
+ body: JSON.stringify(options),
419
+ });
420
+ }
421
+
422
+ /**
423
+ * Apply accepted refinement changes to work.json on disk.
424
+ * @param {string} id - Work item ID
425
+ * @param {object} proposedItem - The proposed (refined) item
426
+ * @param {Array} storyChanges - [{ type: 'update'|'new', storyId?, proposedStory }]
427
+ */
428
+ export async function applyWorkItemChanges(id, proposedItem, storyChanges = []) {
429
+ return apiFetch(`/work-items/${id}`, {
430
+ method: 'PUT',
431
+ body: JSON.stringify({ proposedItem, storyChanges }),
432
+ });
433
+ }
434
+
435
+ // โ”€โ”€ OpenAI OAuth API โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
436
+
437
+ export async function connectOpenAIOAuth() {
438
+ return apiFetch('/settings/openai-oauth/login', { method: 'POST' });
439
+ }
440
+
441
+ export async function disconnectOpenAIOAuth() {
442
+ return apiFetch('/settings/openai-oauth/logout', { method: 'POST' });
443
+ }
444
+
445
+ export async function getOpenAIOAuthStatus() {
446
+ return apiFetch('/settings/openai-oauth/status');
447
+ }
448
+
449
+ export async function testOpenAIOAuth() {
450
+ return apiFetch('/settings/openai-oauth/test', { method: 'POST' });
451
+ }
452
+
453
+ export async function setOpenAIOAuthFallback(enabled) {
454
+ return apiFetch('/settings/openai-oauth/fallback', { method: 'POST', body: JSON.stringify({ enabled }) });
455
+ }
456
+
457
+ // โ”€โ”€ Costs API โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
458
+
459
+ /**
460
+ * Get current month cost summary for the header chip
461
+ * @returns {Promise<{ totalCost: number, totalTokens: number, apiCalls: number }>}
462
+ */
463
+ export async function getCostSummary() {
464
+ return apiFetch('/costs/summary');
465
+ }
466
+
467
+ /**
468
+ * Get cost history for chart and ceremony breakdown
469
+ * @param {number|{ from: string, to: string }} range - Days count or custom date range
470
+ * @returns {Promise<{ daily: Array, ceremonies: Array }>}
471
+ */
472
+ export async function getCostHistory(range = 30) {
473
+ if (typeof range === 'number') {
474
+ return apiFetch(`/costs/history?days=${range}`);
475
+ }
476
+ return apiFetch(`/costs/history?from=${range.from}&to=${range.to}`);
477
+ }
478
+
479
+ // ---- Task Runner: Seed + Run ----
480
+
481
+ export async function updateWorkItemStatus(id, status) {
482
+ return apiFetch(`/work-items/${id}/status`, {
483
+ method: 'PUT',
484
+ body: JSON.stringify({ status }),
485
+ });
486
+ }
487
+
488
+ export async function getDependencyStatus(id) {
489
+ return apiFetch(`/work-items/${id}/dependency-status`);
490
+ }
491
+
492
+ export async function startSeedCeremony(storyId) {
493
+ return apiFetch('/ceremony/seed/run', {
494
+ method: 'POST',
495
+ body: JSON.stringify({ storyId }),
496
+ });
497
+ }
498
+
499
+ export async function startRunTask(taskId) {
500
+ return apiFetch('/ceremony/run-task/run', {
501
+ method: 'POST',
502
+ body: JSON.stringify({ taskId }),
503
+ });
504
+ }
505
+
506
+ export async function getTaskRunnerStatus() {
507
+ return apiFetch('/ceremony/task-runner/status');
508
+ }
509
+
510
+ export async function cancelTaskRun(processId) {
511
+ return apiFetch('/ceremony/task-runner/cancel', {
512
+ method: 'POST',
513
+ body: JSON.stringify({ processId }),
514
+ });
515
+ }
@@ -0,0 +1,154 @@
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
+ // Sort items within each column by coding order (if available), then by ID
122
+ for (const column of COLUMN_ORDER) {
123
+ grouped[column].sort((a, b) => {
124
+ const orderA = a.metadata?.codingOrder ?? Infinity;
125
+ const orderB = b.metadata?.codingOrder ?? Infinity;
126
+ if (orderA !== orderB) return orderA - orderB;
127
+ return (a.id || '').localeCompare(b.id || '');
128
+ });
129
+ }
130
+
131
+ return grouped;
132
+ }
133
+
134
+ /**
135
+ * Get statistics for a column
136
+ * @param {Array} workItems - Work items in the column
137
+ * @returns {object} Statistics object with total count and breakdown by status
138
+ */
139
+ export function getColumnStats(workItems) {
140
+ const stats = {
141
+ total: workItems.length,
142
+ byStatus: {},
143
+ };
144
+
145
+ workItems.forEach((item) => {
146
+ const status = item.status;
147
+ if (!stats.byStatus[status]) {
148
+ stats.byStatus[status] = 0;
149
+ }
150
+ stats.byStatus[status]++;
151
+ });
152
+
153
+ return stats;
154
+ }
@@ -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
+ );