@agile-vibe-coding/avc 0.1.1 → 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 (289) hide show
  1. package/cli/agent-loader.js +21 -0
  2. package/cli/agents/agent-selector.md +129 -0
  3. package/cli/agents/architecture-recommender.md +418 -0
  4. package/cli/agents/database-deep-dive.md +470 -0
  5. package/cli/agents/database-recommender.md +634 -0
  6. package/cli/agents/doc-distributor.md +176 -0
  7. package/cli/agents/documentation-updater.md +203 -0
  8. package/cli/agents/epic-story-decomposer.md +280 -0
  9. package/cli/agents/feature-context-generator.md +91 -0
  10. package/cli/agents/gap-checker-epic.md +52 -0
  11. package/cli/agents/impact-checker-story.md +51 -0
  12. package/cli/agents/migration-guide-generator.md +305 -0
  13. package/cli/agents/mission-scope-generator.md +79 -0
  14. package/cli/agents/mission-scope-validator.md +112 -0
  15. package/cli/agents/project-context-extractor.md +107 -0
  16. package/cli/agents/project-documentation-creator.json +226 -0
  17. package/cli/agents/project-documentation-creator.md +595 -0
  18. package/cli/agents/question-prefiller.md +269 -0
  19. package/cli/agents/refiner-epic.md +39 -0
  20. package/cli/agents/refiner-story.md +42 -0
  21. package/cli/agents/solver-epic-api.json +15 -0
  22. package/cli/agents/solver-epic-api.md +39 -0
  23. package/cli/agents/solver-epic-backend.json +15 -0
  24. package/cli/agents/solver-epic-backend.md +39 -0
  25. package/cli/agents/solver-epic-cloud.json +15 -0
  26. package/cli/agents/solver-epic-cloud.md +39 -0
  27. package/cli/agents/solver-epic-data.json +15 -0
  28. package/cli/agents/solver-epic-data.md +39 -0
  29. package/cli/agents/solver-epic-database.json +15 -0
  30. package/cli/agents/solver-epic-database.md +39 -0
  31. package/cli/agents/solver-epic-developer.json +15 -0
  32. package/cli/agents/solver-epic-developer.md +39 -0
  33. package/cli/agents/solver-epic-devops.json +15 -0
  34. package/cli/agents/solver-epic-devops.md +39 -0
  35. package/cli/agents/solver-epic-frontend.json +15 -0
  36. package/cli/agents/solver-epic-frontend.md +39 -0
  37. package/cli/agents/solver-epic-mobile.json +15 -0
  38. package/cli/agents/solver-epic-mobile.md +39 -0
  39. package/cli/agents/solver-epic-qa.json +15 -0
  40. package/cli/agents/solver-epic-qa.md +39 -0
  41. package/cli/agents/solver-epic-security.json +15 -0
  42. package/cli/agents/solver-epic-security.md +39 -0
  43. package/cli/agents/solver-epic-solution-architect.json +15 -0
  44. package/cli/agents/solver-epic-solution-architect.md +39 -0
  45. package/cli/agents/solver-epic-test-architect.json +15 -0
  46. package/cli/agents/solver-epic-test-architect.md +39 -0
  47. package/cli/agents/solver-epic-ui.json +15 -0
  48. package/cli/agents/solver-epic-ui.md +39 -0
  49. package/cli/agents/solver-epic-ux.json +15 -0
  50. package/cli/agents/solver-epic-ux.md +39 -0
  51. package/cli/agents/solver-story-api.json +15 -0
  52. package/cli/agents/solver-story-api.md +39 -0
  53. package/cli/agents/solver-story-backend.json +15 -0
  54. package/cli/agents/solver-story-backend.md +39 -0
  55. package/cli/agents/solver-story-cloud.json +15 -0
  56. package/cli/agents/solver-story-cloud.md +39 -0
  57. package/cli/agents/solver-story-data.json +15 -0
  58. package/cli/agents/solver-story-data.md +39 -0
  59. package/cli/agents/solver-story-database.json +15 -0
  60. package/cli/agents/solver-story-database.md +39 -0
  61. package/cli/agents/solver-story-developer.json +15 -0
  62. package/cli/agents/solver-story-developer.md +39 -0
  63. package/cli/agents/solver-story-devops.json +15 -0
  64. package/cli/agents/solver-story-devops.md +39 -0
  65. package/cli/agents/solver-story-frontend.json +15 -0
  66. package/cli/agents/solver-story-frontend.md +39 -0
  67. package/cli/agents/solver-story-mobile.json +15 -0
  68. package/cli/agents/solver-story-mobile.md +39 -0
  69. package/cli/agents/solver-story-qa.json +15 -0
  70. package/cli/agents/solver-story-qa.md +39 -0
  71. package/cli/agents/solver-story-security.json +15 -0
  72. package/cli/agents/solver-story-security.md +39 -0
  73. package/cli/agents/solver-story-solution-architect.json +15 -0
  74. package/cli/agents/solver-story-solution-architect.md +39 -0
  75. package/cli/agents/solver-story-test-architect.json +15 -0
  76. package/cli/agents/solver-story-test-architect.md +39 -0
  77. package/cli/agents/solver-story-ui.json +15 -0
  78. package/cli/agents/solver-story-ui.md +39 -0
  79. package/cli/agents/solver-story-ux.json +15 -0
  80. package/cli/agents/solver-story-ux.md +39 -0
  81. package/cli/agents/story-doc-enricher.md +133 -0
  82. package/cli/agents/suggestion-business-analyst.md +88 -0
  83. package/cli/agents/suggestion-deployment-architect.md +263 -0
  84. package/cli/agents/suggestion-product-manager.md +129 -0
  85. package/cli/agents/suggestion-security-specialist.md +156 -0
  86. package/cli/agents/suggestion-technical-architect.md +269 -0
  87. package/cli/agents/suggestion-ux-researcher.md +93 -0
  88. package/cli/agents/task-subtask-decomposer.md +188 -0
  89. package/cli/agents/validator-documentation.json +152 -0
  90. package/cli/agents/validator-documentation.md +453 -0
  91. package/cli/agents/validator-epic-api.json +93 -0
  92. package/cli/agents/validator-epic-api.md +137 -0
  93. package/cli/agents/validator-epic-backend.json +93 -0
  94. package/cli/agents/validator-epic-backend.md +130 -0
  95. package/cli/agents/validator-epic-cloud.json +93 -0
  96. package/cli/agents/validator-epic-cloud.md +137 -0
  97. package/cli/agents/validator-epic-data.json +93 -0
  98. package/cli/agents/validator-epic-data.md +130 -0
  99. package/cli/agents/validator-epic-database.json +93 -0
  100. package/cli/agents/validator-epic-database.md +137 -0
  101. package/cli/agents/validator-epic-developer.json +74 -0
  102. package/cli/agents/validator-epic-developer.md +153 -0
  103. package/cli/agents/validator-epic-devops.json +74 -0
  104. package/cli/agents/validator-epic-devops.md +153 -0
  105. package/cli/agents/validator-epic-frontend.json +74 -0
  106. package/cli/agents/validator-epic-frontend.md +153 -0
  107. package/cli/agents/validator-epic-mobile.json +93 -0
  108. package/cli/agents/validator-epic-mobile.md +130 -0
  109. package/cli/agents/validator-epic-qa.json +93 -0
  110. package/cli/agents/validator-epic-qa.md +130 -0
  111. package/cli/agents/validator-epic-security.json +74 -0
  112. package/cli/agents/validator-epic-security.md +154 -0
  113. package/cli/agents/validator-epic-solution-architect.json +74 -0
  114. package/cli/agents/validator-epic-solution-architect.md +156 -0
  115. package/cli/agents/validator-epic-test-architect.json +93 -0
  116. package/cli/agents/validator-epic-test-architect.md +130 -0
  117. package/cli/agents/validator-epic-ui.json +93 -0
  118. package/cli/agents/validator-epic-ui.md +130 -0
  119. package/cli/agents/validator-epic-ux.json +93 -0
  120. package/cli/agents/validator-epic-ux.md +130 -0
  121. package/cli/agents/validator-selector.md +211 -0
  122. package/cli/agents/validator-story-api.json +104 -0
  123. package/cli/agents/validator-story-api.md +152 -0
  124. package/cli/agents/validator-story-backend.json +104 -0
  125. package/cli/agents/validator-story-backend.md +152 -0
  126. package/cli/agents/validator-story-cloud.json +104 -0
  127. package/cli/agents/validator-story-cloud.md +152 -0
  128. package/cli/agents/validator-story-data.json +104 -0
  129. package/cli/agents/validator-story-data.md +152 -0
  130. package/cli/agents/validator-story-database.json +104 -0
  131. package/cli/agents/validator-story-database.md +152 -0
  132. package/cli/agents/validator-story-developer.json +104 -0
  133. package/cli/agents/validator-story-developer.md +152 -0
  134. package/cli/agents/validator-story-devops.json +104 -0
  135. package/cli/agents/validator-story-devops.md +152 -0
  136. package/cli/agents/validator-story-frontend.json +104 -0
  137. package/cli/agents/validator-story-frontend.md +152 -0
  138. package/cli/agents/validator-story-mobile.json +104 -0
  139. package/cli/agents/validator-story-mobile.md +152 -0
  140. package/cli/agents/validator-story-qa.json +104 -0
  141. package/cli/agents/validator-story-qa.md +152 -0
  142. package/cli/agents/validator-story-security.json +104 -0
  143. package/cli/agents/validator-story-security.md +152 -0
  144. package/cli/agents/validator-story-solution-architect.json +104 -0
  145. package/cli/agents/validator-story-solution-architect.md +152 -0
  146. package/cli/agents/validator-story-test-architect.json +104 -0
  147. package/cli/agents/validator-story-test-architect.md +152 -0
  148. package/cli/agents/validator-story-ui.json +104 -0
  149. package/cli/agents/validator-story-ui.md +152 -0
  150. package/cli/agents/validator-story-ux.json +104 -0
  151. package/cli/agents/validator-story-ux.md +152 -0
  152. package/cli/ansi-colors.js +21 -0
  153. package/cli/build-docs.js +29 -8
  154. package/cli/ceremony-history.js +369 -0
  155. package/cli/command-logger.js +49 -12
  156. package/cli/components/static-output.js +63 -0
  157. package/cli/console-output-manager.js +94 -0
  158. package/cli/docs-sync.js +306 -0
  159. package/cli/epic-story-validator.js +1174 -0
  160. package/cli/evaluation-prompts.js +1008 -0
  161. package/cli/execution-context.js +195 -0
  162. package/cli/generate-summary-table.js +340 -0
  163. package/cli/index.js +0 -0
  164. package/cli/init-model-config.js +697 -0
  165. package/cli/init.js +1311 -274
  166. package/cli/kanban-server-manager.js +228 -0
  167. package/cli/llm-claude.js +83 -1
  168. package/cli/llm-gemini.js +85 -0
  169. package/cli/llm-mock.js +233 -0
  170. package/cli/llm-openai.js +233 -0
  171. package/cli/llm-provider.js +240 -3
  172. package/cli/llm-token-limits.js +102 -0
  173. package/cli/llm-verifier.js +454 -0
  174. package/cli/message-constants.js +58 -0
  175. package/cli/message-manager.js +334 -0
  176. package/cli/message-types.js +96 -0
  177. package/cli/messaging-api.js +297 -0
  178. package/cli/model-pricing.js +169 -0
  179. package/cli/model-query-engine.js +468 -0
  180. package/cli/model-recommendation-analyzer.js +495 -0
  181. package/cli/model-selector.js +269 -0
  182. package/cli/output-buffer.js +107 -0
  183. package/cli/process-manager.js +73 -2
  184. package/cli/repl-ink.js +4988 -1217
  185. package/cli/repl-old.js +4 -4
  186. package/cli/seed-processor.js +792 -0
  187. package/cli/sprint-planning-processor.js +1813 -0
  188. package/cli/template-processor.js +2102 -105
  189. package/cli/templates/project.md +25 -8
  190. package/cli/templates/vitepress-config.mts.template +5 -4
  191. package/cli/token-tracker.js +520 -0
  192. package/cli/tools/generate-story-validators.js +317 -0
  193. package/cli/tools/generate-validators.js +669 -0
  194. package/cli/update-checker.js +19 -17
  195. package/cli/update-notifier.js +4 -4
  196. package/cli/validation-router.js +605 -0
  197. package/cli/verification-tracker.js +563 -0
  198. package/kanban/README.md +386 -0
  199. package/kanban/client/README.md +205 -0
  200. package/kanban/client/components.json +20 -0
  201. package/kanban/client/dist/assets/index-CiD8PS2e.js +306 -0
  202. package/kanban/client/dist/assets/index-nLh0m82Q.css +1 -0
  203. package/kanban/client/dist/index.html +16 -0
  204. package/kanban/client/dist/vite.svg +1 -0
  205. package/kanban/client/index.html +15 -0
  206. package/kanban/client/package-lock.json +9442 -0
  207. package/kanban/client/package.json +44 -0
  208. package/kanban/client/postcss.config.js +6 -0
  209. package/kanban/client/public/vite.svg +1 -0
  210. package/kanban/client/src/App.jsx +622 -0
  211. package/kanban/client/src/components/ProjectFileEditorPopup.jsx +117 -0
  212. package/kanban/client/src/components/ceremony/AskArchPopup.jsx +416 -0
  213. package/kanban/client/src/components/ceremony/AskModelPopup.jsx +616 -0
  214. package/kanban/client/src/components/ceremony/CeremonyWorkflowModal.jsx +946 -0
  215. package/kanban/client/src/components/ceremony/EpicStorySelectionModal.jsx +254 -0
  216. package/kanban/client/src/components/ceremony/SponsorCallModal.jsx +619 -0
  217. package/kanban/client/src/components/ceremony/SprintPlanningModal.jsx +704 -0
  218. package/kanban/client/src/components/ceremony/steps/ArchitectureStep.jsx +150 -0
  219. package/kanban/client/src/components/ceremony/steps/CompleteStep.jsx +154 -0
  220. package/kanban/client/src/components/ceremony/steps/DatabaseStep.jsx +202 -0
  221. package/kanban/client/src/components/ceremony/steps/DeploymentStep.jsx +123 -0
  222. package/kanban/client/src/components/ceremony/steps/MissionStep.jsx +106 -0
  223. package/kanban/client/src/components/ceremony/steps/ReviewAnswersStep.jsx +125 -0
  224. package/kanban/client/src/components/ceremony/steps/RunningStep.jsx +228 -0
  225. package/kanban/client/src/components/kanban/CardDetailModal.jsx +559 -0
  226. package/kanban/client/src/components/kanban/EpicSection.jsx +146 -0
  227. package/kanban/client/src/components/kanban/FilterToolbar.jsx +222 -0
  228. package/kanban/client/src/components/kanban/GroupingSelector.jsx +57 -0
  229. package/kanban/client/src/components/kanban/KanbanBoard.jsx +211 -0
  230. package/kanban/client/src/components/kanban/KanbanCard.jsx +138 -0
  231. package/kanban/client/src/components/kanban/KanbanColumn.jsx +90 -0
  232. package/kanban/client/src/components/kanban/RefineWorkItemPopup.jsx +789 -0
  233. package/kanban/client/src/components/layout/LoadingScreen.jsx +82 -0
  234. package/kanban/client/src/components/process/ProcessMonitorBar.jsx +80 -0
  235. package/kanban/client/src/components/settings/AgentEditorPopup.jsx +171 -0
  236. package/kanban/client/src/components/settings/AgentsTab.jsx +353 -0
  237. package/kanban/client/src/components/settings/ApiKeysTab.jsx +113 -0
  238. package/kanban/client/src/components/settings/CeremonyModelsTab.jsx +98 -0
  239. package/kanban/client/src/components/settings/CostThresholdsTab.jsx +94 -0
  240. package/kanban/client/src/components/settings/ModelPricingTab.jsx +204 -0
  241. package/kanban/client/src/components/settings/ServersTab.jsx +121 -0
  242. package/kanban/client/src/components/settings/SettingsModal.jsx +84 -0
  243. package/kanban/client/src/components/stats/CostModal.jsx +353 -0
  244. package/kanban/client/src/components/ui/badge.jsx +27 -0
  245. package/kanban/client/src/components/ui/dialog.jsx +121 -0
  246. package/kanban/client/src/components/ui/tabs.jsx +85 -0
  247. package/kanban/client/src/hooks/__tests__/useGrouping.test.js +232 -0
  248. package/kanban/client/src/hooks/useGrouping.js +118 -0
  249. package/kanban/client/src/hooks/useWebSocket.js +120 -0
  250. package/kanban/client/src/lib/__tests__/api.test.js +196 -0
  251. package/kanban/client/src/lib/__tests__/status-grouping.test.js +94 -0
  252. package/kanban/client/src/lib/api.js +401 -0
  253. package/kanban/client/src/lib/status-grouping.js +144 -0
  254. package/kanban/client/src/lib/utils.js +11 -0
  255. package/kanban/client/src/main.jsx +10 -0
  256. package/kanban/client/src/store/__tests__/kanbanStore.test.js +164 -0
  257. package/kanban/client/src/store/ceremonyStore.js +172 -0
  258. package/kanban/client/src/store/filterStore.js +201 -0
  259. package/kanban/client/src/store/kanbanStore.js +115 -0
  260. package/kanban/client/src/store/processStore.js +65 -0
  261. package/kanban/client/src/store/sprintPlanningStore.js +33 -0
  262. package/kanban/client/src/styles/globals.css +59 -0
  263. package/kanban/client/tailwind.config.js +77 -0
  264. package/kanban/client/vite.config.js +28 -0
  265. package/kanban/client/vitest.config.js +28 -0
  266. package/kanban/dev-start.sh +47 -0
  267. package/kanban/package.json +12 -0
  268. package/kanban/server/index.js +516 -0
  269. package/kanban/server/routes/ceremony.js +305 -0
  270. package/kanban/server/routes/costs.js +157 -0
  271. package/kanban/server/routes/processes.js +50 -0
  272. package/kanban/server/routes/settings.js +303 -0
  273. package/kanban/server/routes/websocket.js +276 -0
  274. package/kanban/server/routes/work-items.js +347 -0
  275. package/kanban/server/services/CeremonyService.js +1190 -0
  276. package/kanban/server/services/FileSystemScanner.js +95 -0
  277. package/kanban/server/services/FileWatcher.js +144 -0
  278. package/kanban/server/services/HierarchyBuilder.js +196 -0
  279. package/kanban/server/services/ProcessRegistry.js +122 -0
  280. package/kanban/server/services/WorkItemReader.js +123 -0
  281. package/kanban/server/services/WorkItemRefineService.js +510 -0
  282. package/kanban/server/start.js +49 -0
  283. package/kanban/server/utils/kanban-logger.js +132 -0
  284. package/kanban/server/utils/markdown.js +91 -0
  285. package/kanban/server/utils/status-grouping.js +107 -0
  286. package/kanban/server/workers/sponsor-call-worker.js +84 -0
  287. package/kanban/server/workers/sprint-planning-worker.js +130 -0
  288. package/package.json +18 -5
  289. package/cli/agents/documentation.md +0 -302
@@ -0,0 +1,619 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { X, Info, AlertTriangle, Settings as SettingsIcon } from 'lucide-react';
3
+ import { useCeremonyStore } from '../../store/ceremonyStore';
4
+ import {
5
+ analyzeDatabase,
6
+ analyzeArchitecture,
7
+ prefillAnswers,
8
+ runCeremony,
9
+ getSettings,
10
+ getModels,
11
+ saveCeremonies,
12
+ pauseCeremony,
13
+ resumeCeremony,
14
+ cancelCeremony,
15
+ getSponsorCallDraft,
16
+ saveSponsorCallDraft,
17
+ deleteSponsorCallDraft,
18
+ } from '../../lib/api';
19
+ import { CeremonyWorkflowModal } from './CeremonyWorkflowModal';
20
+
21
+ import { DeploymentStep } from './steps/DeploymentStep';
22
+ import { MissionStep } from './steps/MissionStep';
23
+ import { DatabaseStep } from './steps/DatabaseStep';
24
+ import { ArchitectureStep } from './steps/ArchitectureStep';
25
+ import { ReviewAnswersStep } from './steps/ReviewAnswersStep';
26
+ import { RunningStep } from './steps/RunningStep';
27
+ import { CompleteStep } from './steps/CompleteStep';
28
+
29
+ const KEY_LABELS = {
30
+ anthropic: 'Anthropic API Key (ANTHROPIC_API_KEY)',
31
+ gemini: 'Google Gemini API Key (GEMINI_API_KEY)',
32
+ openai: 'OpenAI API Key (OPENAI_API_KEY)',
33
+ };
34
+
35
+ function normalizeProvider(provider = '') {
36
+ const p = provider.toLowerCase();
37
+ if (p === 'claude' || p === 'anthropic') return 'anthropic';
38
+ return p;
39
+ }
40
+
41
+ function computeMissingProviders(settings) {
42
+ const ceremony = settings.ceremonies?.find((c) => c.name === 'sponsor-call');
43
+ const needed = new Set();
44
+
45
+ // stages is an object: { stageName: { provider, model }, ... }
46
+ if (ceremony?.stages && typeof ceremony.stages === 'object') {
47
+ for (const stage of Object.values(ceremony.stages)) {
48
+ if (stage?.provider) needed.add(normalizeProvider(stage.provider));
49
+ }
50
+ }
51
+
52
+ // validation: top-level { model, provider } and/or sub-areas { areaName: { model, provider } }
53
+ if (ceremony?.validation && typeof ceremony.validation === 'object') {
54
+ if (ceremony.validation.provider) needed.add(normalizeProvider(ceremony.validation.provider));
55
+ for (const val of Object.values(ceremony.validation)) {
56
+ if (val && typeof val === 'object' && typeof val.provider === 'string') {
57
+ needed.add(normalizeProvider(val.provider));
58
+ }
59
+ }
60
+ }
61
+
62
+ const apiKeys = settings.apiKeys ?? {};
63
+ return [...needed].filter((p) => !apiKeys[p]?.isSet);
64
+ }
65
+
66
+ // Step definitions for the progress header (shown steps vary based on hasDb)
67
+ const ALL_STEPS = [
68
+ { id: 1, label: 'Strategy' },
69
+ { id: 2, label: 'Mission' },
70
+ { id: 3, label: 'Database' },
71
+ { id: 4, label: 'Architecture' },
72
+ { id: 5, label: 'Review' },
73
+ { id: 6, label: 'Generate' },
74
+ { id: 7, label: 'Done' },
75
+ ];
76
+
77
+ function StepProgress({ currentStep, hasDb }) {
78
+ const visibleSteps = hasDb ? ALL_STEPS : ALL_STEPS.filter((s) => s.id !== 3);
79
+ // Map real wizardStep → display index
80
+ const getDisplayIndex = (step) => {
81
+ if (!hasDb && step >= 4) return step - 1; // shift steps 4-7 down by 1 when db skipped
82
+ return step;
83
+ };
84
+ const displayCurrent = getDisplayIndex(currentStep);
85
+
86
+ return (
87
+ <div className="flex items-center gap-1 flex-nowrap overflow-x-auto pb-0.5">
88
+ {visibleSteps.map((s, idx) => {
89
+ const displayIdx = idx + 1;
90
+ const isDone = displayCurrent > displayIdx;
91
+ const isCurrent = displayCurrent === displayIdx;
92
+
93
+ return (
94
+ <div key={s.id} className="flex items-center gap-1">
95
+ <div
96
+ className={`flex items-center gap-1 text-xs px-2 py-0.5 rounded-full whitespace-nowrap ${
97
+ isCurrent
98
+ ? 'bg-blue-600 text-white font-medium'
99
+ : isDone
100
+ ? 'bg-green-100 text-green-700'
101
+ : 'bg-slate-100 text-slate-400'
102
+ }`}
103
+ >
104
+ {isDone ? '✓' : displayIdx} {s.label}
105
+ </div>
106
+ {idx < visibleSteps.length - 1 && (
107
+ <span className="text-slate-300 text-xs">›</span>
108
+ )}
109
+ </div>
110
+ );
111
+ })}
112
+ </div>
113
+ );
114
+ }
115
+
116
+ export function SponsorCallModal({ onClose, onOpenSettings, costLimitPending, onContinuePastCostLimit, onCancelFromCostLimit }) {
117
+ const {
118
+ isOpen,
119
+ wizardStep,
120
+ setWizardStep,
121
+ analyzing,
122
+ setAnalyzing,
123
+ strategy,
124
+ mission,
125
+ initialScope,
126
+ dbResult,
127
+ setDbResult,
128
+ dbChoice,
129
+ archOptions,
130
+ setArchOptions,
131
+ selectedArch,
132
+ applyPrefill,
133
+ requirements,
134
+ ceremonyStatus,
135
+ isPaused,
136
+ setCeremonyStatus,
137
+ setCeremonyResult,
138
+ setCeremonyError,
139
+ appendProgress,
140
+ startRun,
141
+ resetWizard,
142
+ closeWizard,
143
+ setProcessId,
144
+ setStrategy,
145
+ setMission,
146
+ setInitialScope,
147
+ setDbChoice,
148
+ setSelectedArch,
149
+ setPrefillResult,
150
+ setRequirements,
151
+ } = useCeremonyStore();
152
+
153
+ const [analyzingMessage, setAnalyzingMessage] = useState('');
154
+ const [showCancelConfirm, setShowCancelConfirm] = useState(false);
155
+ const [transitioning, setTransitioning] = useState(null); // null | 'pausing' | 'cancelling'
156
+ const [workflowOpen, setWorkflowOpen] = useState(false);
157
+ const [workflowCeremony, setWorkflowCeremony] = useState(null);
158
+ const [workflowModels, setWorkflowModels] = useState([]);
159
+ const [workflowMissionGenValidation, setWorkflowMissionGenValidation] = useState(null);
160
+ const [workflowAllCeremonies, setWorkflowAllCeremonies] = useState([]);
161
+ const [apiKeyCheck, setApiKeyCheck] = useState({ loading: true, missing: [] });
162
+ const [showResumePrompt, setShowResumePrompt] = useState(false);
163
+ const [draftData, setDraftData] = useState(null);
164
+
165
+ // Check required API keys when the modal opens
166
+ useEffect(() => {
167
+ let cancelled = false;
168
+ getSettings()
169
+ .then((s) => {
170
+ if (!cancelled) setApiKeyCheck({ loading: false, missing: computeMissingProviders(s) });
171
+ })
172
+ .catch(() => {
173
+ if (!cancelled) setApiKeyCheck({ loading: false, missing: [] }); // fail open
174
+ });
175
+ return () => { cancelled = true; };
176
+ }, []);
177
+
178
+ // Check for an existing draft when the wizard opens at step 1 (fresh open / post-refresh)
179
+ // Only show resume prompt when ceremony is idle (not during a running ceremony reopen)
180
+ useEffect(() => {
181
+ if (!isOpen || wizardStep !== 1 || ceremonyStatus !== 'idle') return;
182
+ let cancelled = false;
183
+ getSponsorCallDraft().then((draft) => {
184
+ if (!cancelled && draft && draft.wizardStep && draft.wizardStep > 1) {
185
+ setDraftData(draft);
186
+ setShowResumePrompt(true);
187
+ }
188
+ });
189
+ return () => { cancelled = true; };
190
+ }, [isOpen]); // eslint-disable-line react-hooks/exhaustive-deps
191
+
192
+ const recheckKeys = () => {
193
+ setApiKeyCheck({ loading: true, missing: [] });
194
+ getSettings()
195
+ .then((s) => setApiKeyCheck({ loading: false, missing: computeMissingProviders(s) }))
196
+ .catch(() => setApiKeyCheck({ loading: false, missing: [] }));
197
+ };
198
+
199
+ // Snapshot current wizard state and persist to server
200
+ const saveDraft = (overrides = {}) => {
201
+ const snap = useCeremonyStore.getState();
202
+ saveSponsorCallDraft({
203
+ wizardStep: snap.wizardStep,
204
+ strategy: snap.strategy,
205
+ mission: snap.mission,
206
+ initialScope: snap.initialScope,
207
+ dbResult: snap.dbResult,
208
+ dbChoice: snap.dbChoice,
209
+ archOptions: snap.archOptions,
210
+ selectedArch: snap.selectedArch,
211
+ prefillResult: snap.prefillResult,
212
+ requirements: snap.requirements,
213
+ ...overrides,
214
+ }).catch(() => {});
215
+ };
216
+
217
+ const handleResumeDraft = () => {
218
+ const d = draftData;
219
+ setShowResumePrompt(false);
220
+ setDraftData(null);
221
+ if (!d) return;
222
+ // Restore all wizard fields from draft
223
+ if (d.strategy != null) setStrategy(d.strategy);
224
+ if (d.mission != null) setMission(d.mission);
225
+ if (d.initialScope != null) setInitialScope(d.initialScope);
226
+ if (d.dbResult != null) setDbResult(d.dbResult);
227
+ if (d.dbChoice != null) setDbChoice(d.dbChoice);
228
+ if (d.archOptions != null) setArchOptions(d.archOptions);
229
+ if (d.selectedArch != null) setSelectedArch(d.selectedArch);
230
+ if (d.prefillResult != null) setPrefillResult(d.prefillResult);
231
+ if (d.requirements != null) setRequirements(d.requirements);
232
+ setWizardStep(d.wizardStep);
233
+ };
234
+
235
+ const handleStartFresh = () => {
236
+ setShowResumePrompt(false);
237
+ setDraftData(null);
238
+ deleteSponsorCallDraft();
239
+ resetWizard();
240
+ };
241
+
242
+ const handleWorkflowSave = async (updatedCeremony, updatedMG) => {
243
+ const base = workflowAllCeremonies.length > 0 ? workflowAllCeremonies : [updatedCeremony];
244
+ const next = base.map((c) => c.name === updatedCeremony.name ? updatedCeremony : c);
245
+ await saveCeremonies(next, { validation: updatedMG });
246
+ setWorkflowCeremony(updatedCeremony);
247
+ setWorkflowMissionGenValidation(updatedMG);
248
+ setWorkflowAllCeremonies(next);
249
+ };
250
+
251
+ const handleWorkflowClose = () => {
252
+ setWorkflowOpen(false);
253
+ recheckKeys();
254
+ };
255
+
256
+ // Clear transitioning state when WS events arrive
257
+ useEffect(() => {
258
+ if (transitioning === 'pausing' && isPaused) setTransitioning(null);
259
+ }, [isPaused, transitioning]);
260
+
261
+ useEffect(() => {
262
+ if (transitioning === 'cancelling' && ceremonyStatus === 'idle') setTransitioning(null);
263
+ }, [ceremonyStatus, transitioning]);
264
+
265
+ if (!isOpen) return null;
266
+
267
+ const hasDb = dbResult?.hasDatabaseNeeds === true;
268
+
269
+ const handlePause = async () => {
270
+ setTransitioning('pausing');
271
+ try { await pauseCeremony(); } catch (_) {}
272
+ };
273
+
274
+ const handleResume = async () => {
275
+ try { await resumeCeremony(); } catch (_) {}
276
+ };
277
+
278
+ const handleConfirmCancel = async () => {
279
+ setShowCancelConfirm(false);
280
+ setTransitioning('cancelling');
281
+ try { await cancelCeremony(); } catch (_) {}
282
+ };
283
+
284
+ // Close handler
285
+ const handleClose = () => {
286
+ if (ceremonyStatus === 'running') return; // block close while running
287
+ closeWizard();
288
+ if (onClose) onClose();
289
+ };
290
+
291
+ // Step 2 → (DB analysis) → Step 3 or 4
292
+ const handleMissionNext = async () => {
293
+ setAnalyzing(true);
294
+ setAnalyzingMessage('Checking database needs…');
295
+ try {
296
+ const dbData = await analyzeDatabase(mission, initialScope, strategy);
297
+ setDbResult(dbData);
298
+
299
+ if (dbData.hasDatabaseNeeds) {
300
+ setWizardStep(3); // Show database step
301
+ saveDraft({ wizardStep: 3, dbResult: dbData });
302
+ } else {
303
+ // Skip database step — go straight to architecture
304
+ setAnalyzingMessage('Analysing architecture options…');
305
+ const archData = await analyzeArchitecture(mission, initialScope, null, strategy);
306
+ setArchOptions(archData);
307
+ setWizardStep(4);
308
+ saveDraft({ wizardStep: 4, dbResult: dbData, archOptions: archData });
309
+ }
310
+ } catch (err) {
311
+ console.error('Mission analysis error:', err);
312
+ alert(`Analysis failed: ${err.message}`);
313
+ } finally {
314
+ setAnalyzing(false);
315
+ setAnalyzingMessage('');
316
+ }
317
+ };
318
+
319
+ // Step 3 → Architecture analysis → Step 4
320
+ const handleDatabaseNext = async () => {
321
+ setAnalyzing(true);
322
+ setAnalyzingMessage('Analysing architecture options…');
323
+ try {
324
+ const dbContext = dbResult ? { ...dbResult, userChoice: dbChoice } : null;
325
+ const archData = await analyzeArchitecture(mission, initialScope, dbContext, strategy);
326
+ setArchOptions(archData);
327
+ setWizardStep(4);
328
+ saveDraft({ wizardStep: 4, archOptions: archData });
329
+ } catch (err) {
330
+ console.error('Architecture analysis error:', err);
331
+ alert(`Analysis failed: ${err.message}`);
332
+ } finally {
333
+ setAnalyzing(false);
334
+ setAnalyzingMessage('');
335
+ }
336
+ };
337
+
338
+ // Step 4 → Prefill → Step 5
339
+ const handleArchitectureNext = async () => {
340
+ setAnalyzing(true);
341
+ setAnalyzingMessage('Pre-filling requirements from your selections…');
342
+ try {
343
+ const dbContext = dbResult ? { ...dbResult, userChoice: dbChoice } : null;
344
+ const prefill = await prefillAnswers(mission, initialScope, selectedArch, dbContext, strategy);
345
+ applyPrefill(prefill, strategy, mission, initialScope);
346
+ setWizardStep(5);
347
+ // Save after applyPrefill updates requirements in the store
348
+ setTimeout(() => saveDraft({ wizardStep: 5, prefillResult: prefill }), 0);
349
+ } catch (err) {
350
+ console.error('Prefill error:', err);
351
+ alert(`Prefill failed: ${err.message}`);
352
+ } finally {
353
+ setAnalyzing(false);
354
+ setAnalyzingMessage('');
355
+ }
356
+ };
357
+
358
+ // Step 5 → Run ceremony → Step 6
359
+ const handleReviewNext = async () => {
360
+ deleteSponsorCallDraft(); // ceremony is starting — draft no longer needed
361
+ try {
362
+ startRun();
363
+ setWizardStep(6);
364
+ const result = await runCeremony(requirements);
365
+ if (result?.processId) setProcessId(result.processId);
366
+ } catch (err) {
367
+ console.error('Run ceremony error:', err);
368
+ setCeremonyStatus('error');
369
+ setCeremonyError(err.message);
370
+ }
371
+ };
372
+
373
+ const renderStep = () => {
374
+ switch (wizardStep) {
375
+ case 1:
376
+ return (
377
+ <DeploymentStep
378
+ onNext={() => {
379
+ setWizardStep(2);
380
+ saveDraft({ wizardStep: 2 });
381
+ }}
382
+ />
383
+ );
384
+ case 2:
385
+ return <MissionStep onNext={handleMissionNext} onBack={() => setWizardStep(1)} analyzing={analyzing} onOpenSettings={onOpenSettings} />;
386
+ case 3:
387
+ return <DatabaseStep onNext={handleDatabaseNext} onBack={() => setWizardStep(2)} analyzing={analyzing} />;
388
+ case 4:
389
+ return <ArchitectureStep onNext={handleArchitectureNext} onBack={() => setWizardStep(hasDb ? 3 : 2)} analyzing={analyzing} onOpenSettings={onOpenSettings} />;
390
+ case 5:
391
+ return <ReviewAnswersStep onNext={handleReviewNext} onBack={() => setWizardStep(4)} />;
392
+ case 6:
393
+ return (
394
+ <RunningStep
395
+ transitioning={transitioning}
396
+ onPause={handlePause}
397
+ onResume={handleResume}
398
+ onCancel={() => setShowCancelConfirm(true)}
399
+ onBackground={closeWizard}
400
+ />
401
+ );
402
+ case 7:
403
+ return <CompleteStep onClose={handleClose} />;
404
+ default:
405
+ return null;
406
+ }
407
+ };
408
+
409
+ return (
410
+ <div className="fixed inset-0 z-50 flex items-center justify-center">
411
+ {/* Backdrop */}
412
+ <div
413
+ className="absolute inset-0 bg-black/40"
414
+ onClick={wizardStep !== 6 ? handleClose : undefined}
415
+ />
416
+
417
+ {/* Modal */}
418
+ <div className="relative bg-white rounded-2xl shadow-2xl w-full max-w-3xl mx-4 max-h-[90vh] flex flex-col">
419
+ {/* Resume draft overlay */}
420
+ {showResumePrompt && (
421
+ <div className="absolute inset-0 z-10 flex items-center justify-center bg-white/90 rounded-2xl">
422
+ <div className="bg-white border border-slate-200 rounded-xl shadow-lg p-6 max-w-sm mx-4 text-center space-y-4">
423
+ <p className="text-base font-semibold text-slate-900">Resume previous session?</p>
424
+ <p className="text-sm text-slate-500">
425
+ A previous wizard session was saved
426
+ {draftData?.savedAt ? ` on ${new Date(draftData.savedAt).toLocaleDateString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })}` : ''}.
427
+ Would you like to continue where you left off?
428
+ </p>
429
+ <div className="flex gap-3 justify-center pt-1">
430
+ <button
431
+ onClick={handleStartFresh}
432
+ className="px-4 py-2 text-sm rounded-lg border border-slate-200 text-slate-700 hover:bg-slate-50"
433
+ >
434
+ Start Fresh
435
+ </button>
436
+ <button
437
+ onClick={handleResumeDraft}
438
+ className="px-4 py-2 text-sm rounded-lg bg-blue-600 text-white hover:bg-blue-700"
439
+ >
440
+ Resume
441
+ </button>
442
+ </div>
443
+ </div>
444
+ </div>
445
+ )}
446
+
447
+ {/* Cost-limit pause overlay */}
448
+ {costLimitPending && (
449
+ <div className="absolute inset-0 z-20 flex items-center justify-center bg-white/90 rounded-2xl">
450
+ <div className="bg-white border border-amber-200 rounded-xl shadow-lg p-6 max-w-sm mx-4 text-center space-y-4">
451
+ <div className="text-3xl">⚠️</div>
452
+ <p className="text-base font-semibold text-slate-900">Cost Limit Reached</p>
453
+ <p className="text-sm text-slate-600">
454
+ <span className="font-mono font-medium">${costLimitPending.cost.toFixed(4)}</span> spent
455
+ {costLimitPending.threshold != null && (
456
+ <> (limit: <span className="font-mono">${Number(costLimitPending.threshold).toFixed(2)}</span>)</>
457
+ )}
458
+ </p>
459
+ <p className="text-sm text-slate-500">
460
+ The ceremony is paused. What would you like to do?
461
+ </p>
462
+ <div className="flex gap-3 justify-center pt-1">
463
+ <button
464
+ onClick={onContinuePastCostLimit}
465
+ className="px-4 py-2 text-sm rounded-lg bg-slate-900 text-white hover:bg-slate-700"
466
+ >
467
+ Continue Anyway
468
+ </button>
469
+ <button
470
+ onClick={onCancelFromCostLimit}
471
+ className="px-4 py-2 text-sm rounded-lg border border-red-200 text-red-600 hover:bg-red-50"
472
+ >
473
+ Cancel Ceremony
474
+ </button>
475
+ </div>
476
+ <p className="text-xs text-slate-400">
477
+ Continue disables cost checking for the rest of this run.
478
+ </p>
479
+ </div>
480
+ </div>
481
+ )}
482
+
483
+ {/* Cancel confirmation overlay */}
484
+ {showCancelConfirm && (
485
+ <div className="absolute inset-0 z-10 flex items-center justify-center bg-white/90 rounded-2xl">
486
+ <div className="bg-white border border-slate-200 rounded-xl shadow-lg p-6 max-w-sm mx-4 text-center space-y-4">
487
+ <p className="text-base font-semibold text-slate-900">Stop documentation generation?</p>
488
+ <p className="text-sm text-slate-500">
489
+ You can restart the ceremony later. Any files written so far will be kept.
490
+ </p>
491
+ <div className="flex gap-3 justify-center pt-1">
492
+ <button
493
+ onClick={() => setShowCancelConfirm(false)}
494
+ className="px-4 py-2 text-sm rounded-lg border border-slate-200 text-slate-700 hover:bg-slate-50"
495
+ >
496
+ Keep Running
497
+ </button>
498
+ <button
499
+ onClick={handleConfirmCancel}
500
+ className="px-4 py-2 text-sm rounded-lg bg-red-600 text-white hover:bg-red-700"
501
+ >
502
+ Cancel Run
503
+ </button>
504
+ </div>
505
+ </div>
506
+ </div>
507
+ )}
508
+
509
+ {/* Header */}
510
+ <div className="flex items-start justify-between px-6 pt-5 pb-4 border-b border-slate-200 flex-shrink-0">
511
+ <div className="min-w-0 flex-1">
512
+ <h1 className="text-base font-semibold text-slate-900">Sponsor Call Ceremony</h1>
513
+ <div className="mt-2">
514
+ <StepProgress currentStep={wizardStep} hasDb={hasDb} />
515
+ </div>
516
+ </div>
517
+ <div className="flex items-center gap-3 ml-4 mt-0.5 flex-shrink-0">
518
+ {ceremonyStatus !== 'running' && (
519
+ <button
520
+ type="button"
521
+ onClick={async () => {
522
+ try {
523
+ const [s, m] = await Promise.all([getSettings(), getModels()]);
524
+ const sc = s.ceremonies?.find((c) => c.name === 'sponsor-call') ?? {};
525
+ setWorkflowCeremony(sc);
526
+ setWorkflowModels(m);
527
+ setWorkflowMissionGenValidation(s.missionGenerator?.validation ?? null);
528
+ setWorkflowAllCeremonies(s.ceremonies || []);
529
+ setWorkflowOpen(true);
530
+ } catch {}
531
+ }}
532
+ className="flex items-center gap-1 text-xs text-slate-400 hover:text-blue-500 transition-colors whitespace-nowrap"
533
+ title="View ceremony workflow"
534
+ >
535
+ <Info className="w-3.5 h-3.5" />
536
+ How it works
537
+ </button>
538
+ )}
539
+ {ceremonyStatus !== 'running' && (
540
+ <button
541
+ onClick={handleClose}
542
+ className="text-slate-400 hover:text-slate-600 transition-colors"
543
+ >
544
+ <X className="w-5 h-5" />
545
+ </button>
546
+ )}
547
+ </div>
548
+ </div>
549
+
550
+ {/* Scrollable content */}
551
+ <div className="flex-1 overflow-y-auto px-6 py-5">
552
+ {apiKeyCheck.loading ? (
553
+ <div className="flex items-center justify-center py-12">
554
+ <div className="animate-spin rounded-full h-6 w-6 border-b-2 border-blue-600" />
555
+ </div>
556
+ ) : apiKeyCheck.missing.length > 0 ? (
557
+ <div className="flex flex-col gap-5">
558
+ <div className="flex items-start gap-3 p-4 bg-amber-50 border border-amber-200 rounded-lg">
559
+ <AlertTriangle className="w-5 h-5 text-amber-500 flex-shrink-0 mt-0.5" />
560
+ <div>
561
+ <p className="text-sm font-semibold text-amber-900">API Keys Required</p>
562
+ <p className="text-xs text-amber-700 mt-1">
563
+ Configure the following API keys before running the Sponsor Call ceremony:
564
+ </p>
565
+ <ul className="mt-2 space-y-1.5">
566
+ {apiKeyCheck.missing.map((p) => (
567
+ <li key={p} className="flex items-center gap-2 text-xs text-amber-800">
568
+ <span className="w-1.5 h-1.5 rounded-full bg-amber-400 flex-shrink-0" />
569
+ {KEY_LABELS[p] || p}
570
+ </li>
571
+ ))}
572
+ </ul>
573
+ </div>
574
+ </div>
575
+ <div className="flex items-center gap-3">
576
+ <button
577
+ onClick={() => onOpenSettings?.()}
578
+ className="flex items-center gap-1.5 px-4 py-2 text-sm font-medium bg-slate-900 text-white rounded-lg hover:bg-slate-700 transition-colors"
579
+ >
580
+ <SettingsIcon className="w-4 h-4" />
581
+ Open Settings
582
+ </button>
583
+ <button
584
+ onClick={recheckKeys}
585
+ className="px-3 py-2 text-sm text-slate-500 hover:text-slate-800 transition-colors"
586
+ >
587
+ Re-check
588
+ </button>
589
+ </div>
590
+ </div>
591
+ ) : (
592
+ renderStep()
593
+ )}
594
+ </div>
595
+
596
+ {/* Status bar — always rendered to prevent height flicker */}
597
+ <div className="flex-shrink-0 border-t border-slate-100 px-6 h-8 flex items-center gap-2">
598
+ {analyzingMessage && (
599
+ <span className="w-3 h-3 border-2 border-blue-300 border-t-blue-600 rounded-full animate-spin flex-shrink-0" />
600
+ )}
601
+ <p className={`text-xs truncate ${analyzingMessage ? 'text-blue-600 font-medium' : 'text-slate-400'}`}>
602
+ {analyzingMessage}
603
+ </p>
604
+ </div>
605
+ </div>
606
+
607
+ {workflowOpen && workflowCeremony && (
608
+ <CeremonyWorkflowModal
609
+ ceremony={workflowCeremony}
610
+ models={workflowModels}
611
+ missionGenValidation={workflowMissionGenValidation}
612
+ readOnly={ceremonyStatus === 'running'}
613
+ onSave={ceremonyStatus !== 'running' ? handleWorkflowSave : undefined}
614
+ onClose={handleWorkflowClose}
615
+ />
616
+ )}
617
+ </div>
618
+ );
619
+ }