@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,254 @@
1
+ import { useState } from 'react';
2
+ import { useSprintPlanningStore } from '../../store/sprintPlanningStore';
3
+ import { confirmSprintPlanningSelection, cancelCeremony } from '../../lib/api';
4
+
5
+ // ── Inner component: only mounted when hierarchy is non-null ──────────────────
6
+ // Using a wrapper+inner pattern so useState can be lazily initialised from
7
+ // a non-null hierarchy prop, avoiding "hooks before conditional return" issues.
8
+
9
+ function SelectionContent({ hierarchy, onConfirm, onCancel }) {
10
+ const [epicSel, setEpicSel] = useState(() => {
11
+ const m = {};
12
+ for (const e of hierarchy.epics) m[e.id] = true;
13
+ return m;
14
+ });
15
+ const [storySel, setStorySel] = useState(() => {
16
+ const m = {};
17
+ for (const e of hierarchy.epics)
18
+ for (const s of (e.stories || []))
19
+ m[s.id] = true;
20
+ return m;
21
+ });
22
+
23
+ const selectedEpicCount = Object.values(epicSel).filter(Boolean).length;
24
+ const totalEpicCount = hierarchy.epics.length;
25
+ const selectedStoryCount = Object.values(storySel).filter(Boolean).length;
26
+ const totalStoryCount = hierarchy.epics.reduce((n, e) => n + (e.stories?.length || 0), 0);
27
+ const canConfirm = selectedEpicCount > 0;
28
+
29
+ const toggleEpic = (epicId, checked) => {
30
+ setEpicSel(prev => ({ ...prev, [epicId]: checked }));
31
+ const epic = hierarchy.epics.find(e => e.id === epicId);
32
+ if (epic) {
33
+ const update = {};
34
+ for (const s of (epic.stories || [])) update[s.id] = checked;
35
+ setStorySel(prev => ({ ...prev, ...update }));
36
+ }
37
+ };
38
+
39
+ const toggleStory = (epicId, storyId, checked) => {
40
+ const next = { ...storySel, [storyId]: checked };
41
+ setStorySel(next);
42
+ const epic = hierarchy.epics.find(e => e.id === epicId);
43
+ if (epic) {
44
+ const anyChecked = (epic.stories || []).some(s => next[s.id]);
45
+ setEpicSel(prev => ({ ...prev, [epicId]: anyChecked }));
46
+ }
47
+ };
48
+
49
+ const selectAll = () => {
50
+ const em = {}, sm = {};
51
+ for (const e of hierarchy.epics) {
52
+ em[e.id] = true;
53
+ for (const s of (e.stories || [])) sm[s.id] = true;
54
+ }
55
+ setEpicSel(em); setStorySel(sm);
56
+ };
57
+
58
+ const deselectAll = () => {
59
+ const em = {}, sm = {};
60
+ for (const e of hierarchy.epics) {
61
+ em[e.id] = false;
62
+ for (const s of (e.stories || [])) sm[s.id] = false;
63
+ }
64
+ setEpicSel(em); setStorySel(sm);
65
+ };
66
+
67
+ const handleConfirm = () => {
68
+ const selectedEpicIds = Object.entries(epicSel).filter(([, v]) => v).map(([k]) => k);
69
+ const selectedStoryIds = Object.entries(storySel).filter(([, v]) => v).map(([k]) => k);
70
+ onConfirm(selectedEpicIds, selectedStoryIds);
71
+ };
72
+
73
+ return (
74
+ <>
75
+ {/* Header */}
76
+ <div className="px-6 pt-5 pb-4 border-b border-slate-200 flex-shrink-0">
77
+ <h2 className="text-base font-semibold text-slate-900">Review Decomposed Work</h2>
78
+ <p className="text-sm text-slate-500 mt-1">
79
+ The AI has decomposed your scope into epics and stories. Select which items to carry
80
+ forward into validation — deselected items will be skipped entirely.
81
+ </p>
82
+ </div>
83
+
84
+ {/* Scrollable checklist */}
85
+ <div className="flex-1 overflow-y-auto px-6 py-4 space-y-4 min-h-0">
86
+ {/* Counts + select-all controls */}
87
+ <div className="flex items-center justify-between">
88
+ <p className="text-xs text-slate-500">
89
+ <span className="font-medium text-slate-700">{selectedEpicCount}/{totalEpicCount}</span> epics ·{' '}
90
+ <span className="font-medium text-slate-700">{selectedStoryCount}/{totalStoryCount}</span> stories selected
91
+ </p>
92
+ <div className="flex items-center gap-1.5">
93
+ <button onClick={selectAll} className="text-xs px-2 py-0.5 rounded border border-slate-200 text-slate-600 hover:bg-slate-50 transition-colors">
94
+ Select all
95
+ </button>
96
+ <button onClick={deselectAll} className="text-xs px-2 py-0.5 rounded border border-slate-200 text-slate-600 hover:bg-slate-50 transition-colors">
97
+ Deselect all
98
+ </button>
99
+ </div>
100
+ </div>
101
+
102
+ {/* Two-level checklist */}
103
+ <div className="space-y-2">
104
+ {hierarchy.epics.map(epic => {
105
+ const epicChecked = epicSel[epic.id] ?? false;
106
+ const stories = epic.stories || [];
107
+ return (
108
+ <div key={epic.id} className="rounded-lg border border-slate-200 overflow-hidden">
109
+ {/* Epic row */}
110
+ <label className="flex items-start gap-3 px-4 py-3 bg-slate-50 cursor-pointer hover:bg-slate-100 select-none">
111
+ <input
112
+ type="checkbox"
113
+ checked={epicChecked}
114
+ onChange={e => toggleEpic(epic.id, e.target.checked)}
115
+ className="mt-0.5 flex-shrink-0 accent-slate-900"
116
+ />
117
+ <div className="min-w-0 flex-1">
118
+ <p className="text-sm font-semibold text-slate-900 leading-snug">{epic.name}</p>
119
+ {epic.description && (
120
+ <p className="text-xs text-slate-500 mt-0.5 line-clamp-2">{epic.description}</p>
121
+ )}
122
+ <p className="text-xs text-slate-400 mt-0.5">
123
+ {stories.length} {stories.length === 1 ? 'story' : 'stories'}
124
+ {epic.domain ? <span className="ml-1 text-slate-300">· {epic.domain}</span> : null}
125
+ </p>
126
+ </div>
127
+ </label>
128
+
129
+ {/* Story rows */}
130
+ {stories.length > 0 && (
131
+ <div className="divide-y divide-slate-100">
132
+ {stories.map(story => (
133
+ <label
134
+ key={story.id}
135
+ className="flex items-start gap-3 px-4 py-2 pl-10 cursor-pointer hover:bg-slate-50 select-none"
136
+ >
137
+ <input
138
+ type="checkbox"
139
+ checked={storySel[story.id] ?? false}
140
+ onChange={e => toggleStory(epic.id, story.id, e.target.checked)}
141
+ className="mt-0.5 flex-shrink-0 accent-slate-600"
142
+ />
143
+ <div className="min-w-0">
144
+ <p className="text-xs font-medium text-slate-800">{story.name}</p>
145
+ {story.userType && (
146
+ <p className="text-xs text-slate-400 mt-0.5">{story.userType}</p>
147
+ )}
148
+ </div>
149
+ </label>
150
+ ))}
151
+ </div>
152
+ )}
153
+ </div>
154
+ );
155
+ })}
156
+ </div>
157
+ </div>
158
+
159
+ {/* Footer */}
160
+ <div className="flex items-center justify-between px-6 py-4 border-t border-slate-200 flex-shrink-0">
161
+ <button
162
+ onClick={onCancel}
163
+ className="px-4 py-2 text-sm rounded-lg border border-red-200 text-red-600 hover:bg-red-50 transition-colors"
164
+ >
165
+ ✕ Cancel Run
166
+ </button>
167
+ <button
168
+ onClick={handleConfirm}
169
+ disabled={!canConfirm}
170
+ className="px-6 py-2 text-sm font-medium bg-slate-900 text-white rounded-lg hover:bg-slate-700 disabled:opacity-40 disabled:cursor-not-allowed transition-colors"
171
+ >
172
+ Confirm Selection →
173
+ </button>
174
+ </div>
175
+ </>
176
+ );
177
+ }
178
+
179
+ // ── Outer wrapper: guards on status + hierarchy ───────────────────────────────
180
+
181
+ export function EpicStorySelectionModal() {
182
+ const {
183
+ status,
184
+ decomposedHierarchy,
185
+ setStatus,
186
+ setStep,
187
+ setDecomposedHierarchy,
188
+ setError,
189
+ } = useSprintPlanningStore();
190
+
191
+ const [showCancelConfirm, setShowCancelConfirm] = useState(false);
192
+
193
+ // Only render when the worker is waiting for selection
194
+ if (status !== 'awaiting-selection' || !decomposedHierarchy) return null;
195
+
196
+ const handleConfirm = async (selectedEpicIds, selectedStoryIds) => {
197
+ setDecomposedHierarchy(null);
198
+ setStatus('running');
199
+ setStep(2);
200
+ try {
201
+ await confirmSprintPlanningSelection(selectedEpicIds, selectedStoryIds);
202
+ } catch (err) {
203
+ setStatus('error');
204
+ setError(err.message);
205
+ }
206
+ };
207
+
208
+ const handleConfirmCancel = async () => {
209
+ setShowCancelConfirm(false);
210
+ try { await cancelCeremony(); } catch (_) {}
211
+ };
212
+
213
+ return (
214
+ <div className="fixed inset-0 z-[80] flex items-center justify-center">
215
+ {/* Non-dismissible backdrop — user must make a selection or cancel */}
216
+ <div className="absolute inset-0 bg-black/60" />
217
+
218
+ {/* Modal */}
219
+ <div className="relative bg-white rounded-2xl shadow-2xl w-full max-w-2xl mx-4 max-h-[90vh] flex flex-col">
220
+ {/* Cancel confirmation overlay */}
221
+ {showCancelConfirm && (
222
+ <div className="absolute inset-0 z-10 flex items-center justify-center bg-white/90 rounded-2xl">
223
+ <div className="bg-white border border-slate-200 rounded-xl shadow-lg p-6 max-w-sm mx-4 text-center space-y-4">
224
+ <p className="text-base font-semibold text-slate-900">Cancel sprint planning?</p>
225
+ <p className="text-sm text-slate-500">
226
+ Any epics and stories created in this run will be permanently deleted.
227
+ </p>
228
+ <div className="flex gap-3 justify-center pt-1">
229
+ <button
230
+ onClick={() => setShowCancelConfirm(false)}
231
+ className="px-4 py-2 text-sm rounded-lg border border-slate-200 text-slate-700 hover:bg-slate-50"
232
+ >
233
+ Go Back
234
+ </button>
235
+ <button
236
+ onClick={handleConfirmCancel}
237
+ className="px-4 py-2 text-sm rounded-lg bg-red-600 text-white hover:bg-red-700"
238
+ >
239
+ Cancel Run
240
+ </button>
241
+ </div>
242
+ </div>
243
+ </div>
244
+ )}
245
+
246
+ <SelectionContent
247
+ hierarchy={decomposedHierarchy}
248
+ onConfirm={handleConfirm}
249
+ onCancel={() => setShowCancelConfirm(true)}
250
+ />
251
+ </div>
252
+ </div>
253
+ );
254
+ }