@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,162 @@
1
+ import { useState, useEffect, useRef } from 'react';
2
+ import { Play, Loader2, AlertTriangle, X, ChevronDown, ChevronUp } from 'lucide-react';
3
+ import { getDependencyStatus, startRunTask } from '../../lib/api';
4
+ import { cn } from '../../lib/utils';
5
+
6
+ /**
7
+ * Run Button — runs a task in a git worktree (implement + test + commit).
8
+ * Shows dependency check, and streaming progress log.
9
+ */
10
+ export function RunButton({ taskId, onStarted }) {
11
+ const [state, setState] = useState('idle'); // idle | checking | blocked | running | complete | error
12
+ const [blockers, setBlockers] = useState([]);
13
+ const [error, setError] = useState(null);
14
+ const [progress, setProgress] = useState([]);
15
+ const [showLog, setShowLog] = useState(false);
16
+ const [processId, setProcessId] = useState(null);
17
+ const logRef = useRef(null);
18
+
19
+ useEffect(() => {
20
+ if (logRef.current) logRef.current.scrollTop = logRef.current.scrollHeight;
21
+ }, [progress]);
22
+
23
+ // Listen for WebSocket run-task events dispatched by App.jsx
24
+ useEffect(() => {
25
+ if (!processId) return;
26
+
27
+ const handler = (event) => {
28
+ const msg = event.detail;
29
+ if (!msg || msg.processId !== processId) return;
30
+
31
+ if (msg.type === 'run-task:progress') {
32
+ setProgress(prev => [...prev, msg.message]);
33
+ } else if (msg.type === 'run-task:complete') {
34
+ setState('complete');
35
+ setProgress(prev => [...prev, 'Task complete — code committed.']);
36
+ onStarted?.();
37
+ } else if (msg.type === 'run-task:error') {
38
+ setState('error');
39
+ setError(msg.error || 'Run failed');
40
+ setProgress(prev => [...prev, `Error: ${msg.error}`]);
41
+ }
42
+ };
43
+
44
+ window.addEventListener('avc-ws-message', handler);
45
+ return () => window.removeEventListener('avc-ws-message', handler);
46
+ }, [processId, onStarted]);
47
+
48
+ const handleClick = async () => {
49
+ if (state === 'checking' || state === 'running') return;
50
+
51
+ setState('checking');
52
+ setError(null);
53
+ setBlockers([]);
54
+ setProgress([]);
55
+
56
+ try {
57
+ const depStatus = await getDependencyStatus(taskId);
58
+ if (!depStatus.ready) {
59
+ setState('blocked');
60
+ setBlockers(depStatus.blockers || []);
61
+ return;
62
+ }
63
+
64
+ setState('running');
65
+ setShowLog(true);
66
+ setProgress(['Starting task implementation...']);
67
+ const result = await startRunTask(taskId);
68
+ setProcessId(result.processId);
69
+ setProgress(prev => [...prev, `Process started: ${result.processId}`]);
70
+ } catch (err) {
71
+ setError(err.message);
72
+ setState('error');
73
+ }
74
+ };
75
+
76
+ const dismiss = () => {
77
+ setState('idle');
78
+ setBlockers([]);
79
+ setError(null);
80
+ setProgress([]);
81
+ setShowLog(false);
82
+ setProcessId(null);
83
+ };
84
+
85
+ if (state === 'blocked') {
86
+ return (
87
+ <div className="space-y-2">
88
+ <div className="flex items-center gap-2 text-amber-700 text-sm bg-amber-50 border border-amber-200 rounded-lg px-3 py-2">
89
+ <AlertTriangle className="w-4 h-4 flex-shrink-0" />
90
+ <div>
91
+ <div className="font-medium">Dependencies not met</div>
92
+ <ul className="mt-1 text-xs space-y-0.5">
93
+ {blockers.map((b) => (<li key={b.id}>{b.id}: {b.name} ({b.status})</li>))}
94
+ </ul>
95
+ </div>
96
+ </div>
97
+ <button onClick={dismiss} className="text-xs text-slate-500 hover:text-slate-700">Dismiss</button>
98
+ </div>
99
+ );
100
+ }
101
+
102
+ if ((state === 'running' || state === 'complete' || state === 'error') && showLog) {
103
+ return (
104
+ <div className="space-y-2">
105
+ <div className="flex items-center justify-between">
106
+ <div className="flex items-center gap-2 text-sm font-medium">
107
+ {state === 'running' && <Loader2 className="w-4 h-4 animate-spin text-blue-600" />}
108
+ {state === 'complete' && <Play className="w-4 h-4 text-green-600" />}
109
+ {state === 'error' && <AlertTriangle className="w-4 h-4 text-red-600" />}
110
+ <span className={cn(
111
+ state === 'running' && 'text-blue-700',
112
+ state === 'complete' && 'text-green-700',
113
+ state === 'error' && 'text-red-700',
114
+ )}>
115
+ {state === 'running' ? 'Implementing...' : state === 'complete' ? 'Complete' : 'Failed'}
116
+ </span>
117
+ </div>
118
+ <div className="flex items-center gap-1">
119
+ <button onClick={() => setShowLog(!showLog)} className="p-1 text-slate-400 hover:text-slate-600">
120
+ {showLog ? <ChevronUp className="w-3.5 h-3.5" /> : <ChevronDown className="w-3.5 h-3.5" />}
121
+ </button>
122
+ {state !== 'running' && (
123
+ <button onClick={dismiss} className="p-1 text-slate-400 hover:text-slate-600">
124
+ <X className="w-3.5 h-3.5" />
125
+ </button>
126
+ )}
127
+ </div>
128
+ </div>
129
+ {showLog && (
130
+ <div ref={logRef} className="max-h-32 overflow-y-auto bg-slate-900 text-slate-300 text-xs font-mono rounded-md p-2 space-y-0.5">
131
+ {progress.map((msg, i) => (
132
+ <div key={i} className={cn(
133
+ msg.startsWith('Error') && 'text-red-400',
134
+ msg.includes('complete') && 'text-green-400',
135
+ )}>{msg}</div>
136
+ ))}
137
+ {state === 'running' && <div className="text-slate-500 animate-pulse">...</div>}
138
+ </div>
139
+ )}
140
+ </div>
141
+ );
142
+ }
143
+
144
+ return (
145
+ <div className="space-y-1">
146
+ <button
147
+ onClick={handleClick}
148
+ disabled={state === 'checking'}
149
+ className={cn(
150
+ 'flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm font-medium transition-colors',
151
+ state === 'checking'
152
+ ? 'bg-blue-100 text-blue-700 cursor-wait'
153
+ : 'bg-blue-600 text-white hover:bg-blue-700'
154
+ )}
155
+ >
156
+ {state === 'checking' ? <Loader2 className="w-4 h-4 animate-spin" /> : <Play className="w-4 h-4" />}
157
+ {state === 'checking' ? 'Checking...' : 'Run'}
158
+ </button>
159
+ {error && <p className="text-xs text-red-600">{error}</p>}
160
+ </div>
161
+ );
162
+ }
@@ -0,0 +1,176 @@
1
+ import { useState, useEffect, useRef } from 'react';
2
+ import { Sprout, Loader2, AlertTriangle, X, ChevronDown, ChevronUp, Settings } from 'lucide-react';
3
+ import { getDependencyStatus, startSeedCeremony, getSettings } from '../../lib/api';
4
+ import { cn } from '../../lib/utils';
5
+
6
+ /**
7
+ * Seed Button — decomposes a story into tasks/subtasks.
8
+ * Shows dependency check, model selector, and streaming progress log.
9
+ */
10
+ export function SeedButton({ storyId, onStarted }) {
11
+ const [state, setState] = useState('idle'); // idle | checking | blocked | configuring | seeding | complete | error
12
+ const [blockers, setBlockers] = useState([]);
13
+ const [error, setError] = useState(null);
14
+ const [progress, setProgress] = useState([]);
15
+ const [showLog, setShowLog] = useState(false);
16
+ const [processId, setProcessId] = useState(null);
17
+ const logRef = useRef(null);
18
+
19
+ // Auto-scroll progress log
20
+ useEffect(() => {
21
+ if (logRef.current) logRef.current.scrollTop = logRef.current.scrollHeight;
22
+ }, [progress]);
23
+
24
+ // Listen for WebSocket seed events dispatched by App.jsx
25
+ useEffect(() => {
26
+ if (!processId) return;
27
+
28
+ const handler = (event) => {
29
+ const msg = event.detail;
30
+ if (!msg || msg.processId !== processId) return;
31
+
32
+ if (msg.type === 'seed:progress') {
33
+ setProgress(prev => [...prev, msg.message]);
34
+ } else if (msg.type === 'seed:complete') {
35
+ setState('complete');
36
+ setProgress(prev => [...prev, 'Seed complete — tasks created.']);
37
+ onStarted?.();
38
+ } else if (msg.type === 'seed:error') {
39
+ setState('error');
40
+ setError(msg.error || 'Seed failed');
41
+ setProgress(prev => [...prev, `Error: ${msg.error}`]);
42
+ }
43
+ };
44
+
45
+ window.addEventListener('avc-ws-message', handler);
46
+ return () => window.removeEventListener('avc-ws-message', handler);
47
+ }, [processId, onStarted]);
48
+
49
+ const handleClick = async () => {
50
+ if (state === 'checking' || state === 'seeding') return;
51
+
52
+ setState('checking');
53
+ setError(null);
54
+ setBlockers([]);
55
+ setProgress([]);
56
+
57
+ try {
58
+ const depStatus = await getDependencyStatus(storyId);
59
+ if (!depStatus.ready) {
60
+ setState('blocked');
61
+ setBlockers(depStatus.blockers || []);
62
+ return;
63
+ }
64
+
65
+ // Go straight to seeding (model config comes from avc.json server-side)
66
+ setState('seeding');
67
+ setShowLog(true);
68
+ setProgress(['Starting seed ceremony...']);
69
+ const result = await startSeedCeremony(storyId);
70
+ setProcessId(result.processId);
71
+ setProgress(prev => [...prev, `Process started: ${result.processId}`]);
72
+ } catch (err) {
73
+ setError(err.message);
74
+ setState('error');
75
+ }
76
+ };
77
+
78
+ const dismiss = () => {
79
+ setState('idle');
80
+ setBlockers([]);
81
+ setError(null);
82
+ setProgress([]);
83
+ setShowLog(false);
84
+ setProcessId(null);
85
+ };
86
+
87
+ // Blocked state — show blockers
88
+ if (state === 'blocked') {
89
+ return (
90
+ <div className="space-y-2">
91
+ <div className="flex items-center gap-2 text-amber-700 text-sm bg-amber-50 border border-amber-200 rounded-lg px-3 py-2">
92
+ <AlertTriangle className="w-4 h-4 flex-shrink-0" />
93
+ <div>
94
+ <div className="font-medium">Dependencies not met</div>
95
+ <ul className="mt-1 text-xs space-y-0.5">
96
+ {blockers.map((b) => (
97
+ <li key={b.id}>{b.id}: {b.name} ({b.status})</li>
98
+ ))}
99
+ </ul>
100
+ </div>
101
+ </div>
102
+ <button onClick={dismiss} className="text-xs text-slate-500 hover:text-slate-700">Dismiss</button>
103
+ </div>
104
+ );
105
+ }
106
+
107
+ // Seeding/complete/error state — show progress log
108
+ if ((state === 'seeding' || state === 'complete' || state === 'error') && showLog) {
109
+ return (
110
+ <div className="space-y-2">
111
+ <div className="flex items-center justify-between">
112
+ <div className="flex items-center gap-2 text-sm font-medium">
113
+ {state === 'seeding' && <Loader2 className="w-4 h-4 animate-spin text-green-600" />}
114
+ {state === 'complete' && <Sprout className="w-4 h-4 text-green-600" />}
115
+ {state === 'error' && <AlertTriangle className="w-4 h-4 text-red-600" />}
116
+ <span className={cn(
117
+ state === 'seeding' && 'text-green-700',
118
+ state === 'complete' && 'text-green-700',
119
+ state === 'error' && 'text-red-700',
120
+ )}>
121
+ {state === 'seeding' ? 'Seeding...' : state === 'complete' ? 'Seed Complete' : 'Seed Failed'}
122
+ </span>
123
+ </div>
124
+ <div className="flex items-center gap-1">
125
+ <button onClick={() => setShowLog(!showLog)} className="p-1 text-slate-400 hover:text-slate-600">
126
+ {showLog ? <ChevronUp className="w-3.5 h-3.5" /> : <ChevronDown className="w-3.5 h-3.5" />}
127
+ </button>
128
+ {state !== 'seeding' && (
129
+ <button onClick={dismiss} className="p-1 text-slate-400 hover:text-slate-600">
130
+ <X className="w-3.5 h-3.5" />
131
+ </button>
132
+ )}
133
+ </div>
134
+ </div>
135
+ {showLog && (
136
+ <div
137
+ ref={logRef}
138
+ className="max-h-32 overflow-y-auto bg-slate-900 text-slate-300 text-xs font-mono rounded-md p-2 space-y-0.5"
139
+ >
140
+ {progress.map((msg, i) => (
141
+ <div key={i} className={cn(
142
+ msg.startsWith('Error') && 'text-red-400',
143
+ msg.includes('complete') && 'text-green-400',
144
+ )}>{msg}</div>
145
+ ))}
146
+ {state === 'seeding' && <div className="text-slate-500 animate-pulse">...</div>}
147
+ </div>
148
+ )}
149
+ </div>
150
+ );
151
+ }
152
+
153
+ // Default idle state — seed button
154
+ return (
155
+ <div className="space-y-1">
156
+ <button
157
+ onClick={handleClick}
158
+ disabled={state === 'checking'}
159
+ className={cn(
160
+ 'flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm font-medium transition-colors',
161
+ state === 'checking'
162
+ ? 'bg-green-100 text-green-700 cursor-wait'
163
+ : 'bg-green-600 text-white hover:bg-green-700'
164
+ )}
165
+ >
166
+ {state === 'checking' ? (
167
+ <Loader2 className="w-4 h-4 animate-spin" />
168
+ ) : (
169
+ <Sprout className="w-4 h-4" />
170
+ )}
171
+ {state === 'checking' ? 'Checking...' : 'Seed'}
172
+ </button>
173
+ {error && <p className="text-xs text-red-600">{error}</p>}
174
+ </div>
175
+ );
176
+ }
@@ -0,0 +1,82 @@
1
+ import { motion } from 'framer-motion';
2
+
3
+ /**
4
+ * Loading Screen Component
5
+ * Beautiful loading state with animation
6
+ */
7
+ export function LoadingScreen({ message = 'Loading...' }) {
8
+ return (
9
+ <div className="flex items-center justify-center min-h-screen bg-slate-50">
10
+ <div className="text-center">
11
+ <motion.div
12
+ animate={{ rotate: 360 }}
13
+ transition={{ duration: 1, repeat: Infinity, ease: 'linear' }}
14
+ className="inline-block"
15
+ >
16
+ <div className="w-16 h-16 border-4 border-blue-200 border-t-blue-600 rounded-full" />
17
+ </motion.div>
18
+ <motion.p
19
+ initial={{ opacity: 0 }}
20
+ animate={{ opacity: 1 }}
21
+ transition={{ delay: 0.2 }}
22
+ className="mt-4 text-slate-600 font-medium"
23
+ >
24
+ {message}
25
+ </motion.p>
26
+ </div>
27
+ </div>
28
+ );
29
+ }
30
+
31
+ /**
32
+ * Card Skeleton Component
33
+ * Skeleton placeholder for kanban cards
34
+ */
35
+ export function CardSkeleton() {
36
+ return (
37
+ <div className="bg-white rounded-lg border border-slate-200 p-4 animate-pulse">
38
+ <div className="flex items-center justify-between mb-3">
39
+ <div className="h-6 bg-slate-200 rounded w-20"></div>
40
+ <div className="h-6 bg-slate-200 rounded w-16"></div>
41
+ </div>
42
+ <div className="h-4 bg-slate-200 rounded w-full mb-2"></div>
43
+ <div className="h-4 bg-slate-200 rounded w-3/4"></div>
44
+ </div>
45
+ );
46
+ }
47
+
48
+ /**
49
+ * Column Skeleton Component
50
+ * Skeleton placeholder for kanban columns
51
+ */
52
+ export function ColumnSkeleton() {
53
+ return (
54
+ <div className="min-w-[320px] max-w-[380px] bg-slate-100 rounded-lg p-4">
55
+ <div className="flex items-center justify-between mb-4">
56
+ <div className="h-6 bg-slate-200 rounded w-24 animate-pulse"></div>
57
+ <div className="h-6 bg-slate-200 rounded-full w-16 animate-pulse"></div>
58
+ </div>
59
+ <div className="space-y-3">
60
+ <CardSkeleton />
61
+ <CardSkeleton />
62
+ <CardSkeleton />
63
+ </div>
64
+ </div>
65
+ );
66
+ }
67
+
68
+ /**
69
+ * Board Skeleton Component
70
+ * Skeleton placeholder for the entire kanban board
71
+ */
72
+ export function BoardSkeleton() {
73
+ return (
74
+ <div className="flex gap-4 overflow-x-auto pb-4">
75
+ <ColumnSkeleton />
76
+ <ColumnSkeleton />
77
+ <ColumnSkeleton />
78
+ <ColumnSkeleton />
79
+ <ColumnSkeleton />
80
+ </div>
81
+ );
82
+ }
@@ -0,0 +1,80 @@
1
+ import { Activity } from 'lucide-react';
2
+ import { useProcessStore } from '../../store/processStore';
3
+ import { useSprintPlanningStore } from '../../store/sprintPlanningStore';
4
+ import { useCeremonyStore } from '../../store/ceremonyStore';
5
+
6
+ const STATUS_PILL = {
7
+ running: 'bg-blue-50 text-blue-700 border-blue-200 hover:bg-blue-100',
8
+ paused: 'bg-amber-50 text-amber-700 border-amber-200 hover:bg-amber-100',
9
+ complete: 'bg-green-50 text-green-700 border-green-200 hover:bg-green-100',
10
+ error: 'bg-red-50 text-red-700 border-red-200 hover:bg-red-100',
11
+ cancelled: 'bg-slate-50 text-slate-500 border-slate-200 hover:bg-slate-100',
12
+ };
13
+
14
+ const STATUS_DOT = {
15
+ running: <span className="w-1.5 h-1.5 rounded-full bg-blue-500 animate-pulse flex-shrink-0" />,
16
+ paused: <span className="w-1.5 h-1.5 rounded-full bg-amber-500 flex-shrink-0" />,
17
+ complete: <span className="w-1.5 h-1.5 rounded-full bg-green-500 flex-shrink-0" />,
18
+ error: <span className="w-1.5 h-1.5 rounded-full bg-red-500 flex-shrink-0" />,
19
+ cancelled: <span className="w-1.5 h-1.5 rounded-full bg-slate-400 flex-shrink-0" />,
20
+ };
21
+
22
+ function elapsed(startedAt, endedAt) {
23
+ const ms = (endedAt ?? Date.now()) - startedAt;
24
+ const s = Math.floor(ms / 1000);
25
+ if (s < 60) return `${s}s`;
26
+ return `${Math.floor(s / 60)}m ${s % 60}s`;
27
+ }
28
+
29
+ export function ProcessMonitorBar() {
30
+ const { processes, clearCompleted } = useProcessStore();
31
+
32
+ // Sponsor call runs only once — hide its chip once it's no longer running.
33
+ // Sprint planning — hide when cancelled (nothing to review); keep for complete/error.
34
+ const visibleProcesses = processes.filter(p =>
35
+ !(p.type === 'sponsor-call' && p.status !== 'running') &&
36
+ !(p.type === 'sprint-planning' && p.status === 'cancelled')
37
+ );
38
+
39
+ if (visibleProcesses.length === 0) return null;
40
+
41
+ const hasCompleted = visibleProcesses.some(p =>
42
+ ['complete', 'error', 'cancelled'].includes(p.status)
43
+ );
44
+
45
+ const handleChipClick = (p) => {
46
+ if (p.type === 'sprint-planning') {
47
+ useSprintPlanningStore.getState().reopenModal();
48
+ } else if (p.type === 'sponsor-call') {
49
+ useCeremonyStore.getState().reopenWizard();
50
+ }
51
+ };
52
+
53
+ return (
54
+ <div className="flex items-center gap-2 px-4 py-1.5 bg-white border-b border-slate-200 flex-shrink-0 flex-wrap">
55
+ <Activity className="w-4 h-4 text-slate-400 flex-shrink-0" />
56
+
57
+ {visibleProcesses.map(p => (
58
+ <button
59
+ key={p.id}
60
+ onClick={() => handleChipClick(p)}
61
+ title="Click to view"
62
+ className={`flex items-center gap-1.5 text-xs px-2.5 py-1 rounded-full border transition-colors cursor-pointer ${STATUS_PILL[p.status] ?? STATUS_PILL.cancelled}`}
63
+ >
64
+ {STATUS_DOT[p.status] ?? STATUS_DOT.cancelled}
65
+ <span className="font-medium">{p.label}</span>
66
+ <span className="opacity-60">{elapsed(p.startedAt, p.endedAt)}</span>
67
+ </button>
68
+ ))}
69
+
70
+ {hasCompleted && (
71
+ <button
72
+ onClick={clearCompleted}
73
+ className="ml-auto text-xs text-slate-400 hover:text-slate-600 px-2 py-0.5 transition-colors"
74
+ >
75
+ Clear done
76
+ </button>
77
+ )}
78
+ </div>
79
+ );
80
+ }
@@ -0,0 +1,171 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { X, RotateCcw } from 'lucide-react';
3
+ import { getAgentContent, saveAgentContent, resetAgent } from '../../lib/api';
4
+
5
+ export function AgentEditorPopup({ agentName, onClose, onSaved, onReset }) {
6
+ const [data, setData] = useState(null); // { content, isCustomized, defaultContent }
7
+ const [editValue, setEditValue] = useState('');
8
+ const [loading, setLoading] = useState(true);
9
+ const [saving, setSaving] = useState(false);
10
+ const [saved, setSaved] = useState(false);
11
+ const [error, setError] = useState(null);
12
+ // pendingReset: editor has been loaded with defaultContent, Save will call resetAgent()
13
+ const [pendingReset, setPendingReset] = useState(false);
14
+
15
+ useEffect(() => {
16
+ setLoading(true);
17
+ setError(null);
18
+ setPendingReset(false);
19
+ getAgentContent(agentName)
20
+ .then(d => {
21
+ setData(d);
22
+ setEditValue(d.content);
23
+ })
24
+ .catch(err => setError(err.message))
25
+ .finally(() => setLoading(false));
26
+ }, [agentName]);
27
+
28
+ const isDirty = data && editValue !== data.content;
29
+
30
+ const handleSave = async () => {
31
+ if (!data || !isDirty) return;
32
+ setSaving(true);
33
+ setError(null);
34
+ try {
35
+ if (pendingReset) {
36
+ // Reset to default: delete customized file
37
+ await resetAgent(agentName);
38
+ setData(prev => ({ ...prev, content: prev.defaultContent, isCustomized: false }));
39
+ setPendingReset(false);
40
+ onReset?.();
41
+ } else {
42
+ await saveAgentContent(agentName, editValue);
43
+ setData(prev => ({ ...prev, content: editValue, isCustomized: true }));
44
+ onSaved?.();
45
+ }
46
+ setSaved(true);
47
+ setTimeout(() => setSaved(false), 2000);
48
+ } catch (err) {
49
+ setError(err.message);
50
+ } finally {
51
+ setSaving(false);
52
+ }
53
+ };
54
+
55
+ // Cancel closes the popup without saving
56
+ const handleCancel = () => {
57
+ onClose();
58
+ };
59
+
60
+ // Reset loads default content into the editor — persisted only on Save
61
+ const handleReset = () => {
62
+ if (!data?.defaultContent) return;
63
+ setEditValue(data.defaultContent);
64
+ setPendingReset(true);
65
+ };
66
+
67
+ const canReset = data?.isCustomized && !pendingReset;
68
+
69
+ return (
70
+ <div
71
+ className="fixed inset-0 z-[90] flex items-center justify-center bg-black/50 backdrop-blur-sm p-4"
72
+ onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}
73
+ >
74
+ <div
75
+ className="w-full max-w-4xl bg-white rounded-2xl shadow-2xl flex flex-col overflow-hidden"
76
+ style={{ height: '85vh' }}
77
+ >
78
+ {/* Header */}
79
+ <div className="flex items-center justify-between px-5 py-3 border-b border-slate-100 flex-shrink-0">
80
+ <div className="flex items-center gap-2 min-w-0">
81
+ <span className="text-sm font-mono font-medium text-slate-700 truncate">
82
+ {agentName}
83
+ </span>
84
+ {data?.isCustomized && !pendingReset && (
85
+ <span className="flex-shrink-0 text-xs font-medium px-1.5 py-0.5 rounded bg-amber-100 text-amber-700">
86
+ Custom
87
+ </span>
88
+ )}
89
+ {pendingReset && (
90
+ <span className="flex-shrink-0 text-xs font-medium px-1.5 py-0.5 rounded bg-blue-100 text-blue-700">
91
+ Reset pending — save to apply
92
+ </span>
93
+ )}
94
+ </div>
95
+ <button
96
+ type="button"
97
+ onClick={onClose}
98
+ className="text-slate-400 hover:text-slate-600 transition-colors ml-4 flex-shrink-0"
99
+ aria-label="Close"
100
+ >
101
+ <X className="w-4 h-4" />
102
+ </button>
103
+ </div>
104
+
105
+ {/* Body */}
106
+ {loading ? (
107
+ <div className="flex-1 flex items-center justify-center text-sm text-slate-400">
108
+ <span className="w-4 h-4 border border-slate-300 border-t-slate-600 rounded-full animate-spin mr-2" />
109
+ Loading…
110
+ </div>
111
+ ) : error && !data ? (
112
+ <div className="flex-1 flex items-center justify-center text-sm text-red-500 px-6 text-center">
113
+ {error}
114
+ </div>
115
+ ) : (
116
+ <textarea
117
+ value={editValue}
118
+ onChange={e => { setEditValue(e.target.value); setPendingReset(false); }}
119
+ className="flex-1 resize-none font-mono text-xs text-slate-800 leading-relaxed px-5 py-4 focus:outline-none"
120
+ spellCheck={false}
121
+ />
122
+ )}
123
+
124
+ {/* Footer */}
125
+ <div className="flex items-center justify-between px-5 py-3 border-t border-slate-100 flex-shrink-0">
126
+ <div>
127
+ {error && !loading && (
128
+ <p className="text-xs text-red-600">{error}</p>
129
+ )}
130
+ {saved && (
131
+ <p className="text-xs text-green-600 font-medium">Saved ✓</p>
132
+ )}
133
+ </div>
134
+ <div className="flex items-center gap-2">
135
+ {/* Reset: loads default content into editor; Save then applies it */}
136
+ <button
137
+ type="button"
138
+ onClick={handleReset}
139
+ disabled={!canReset}
140
+ className="flex items-center gap-1.5 px-3 py-1.5 text-xs text-slate-500 hover:text-amber-600 transition-colors disabled:opacity-30 disabled:cursor-not-allowed"
141
+ title={canReset ? 'Load default content into editor' : 'Only available for customized agents'}
142
+ >
143
+ <RotateCcw className="w-3 h-3" />
144
+ Reset to default
145
+ </button>
146
+ <button
147
+ type="button"
148
+ onClick={handleCancel}
149
+ className="px-3 py-1.5 text-xs font-medium text-slate-500 hover:text-slate-700 transition-colors"
150
+ >
151
+ Cancel
152
+ </button>
153
+ <button
154
+ type="button"
155
+ onClick={handleSave}
156
+ disabled={!isDirty || saving}
157
+ className="px-3 py-1.5 text-xs font-medium bg-slate-900 text-white rounded-md hover:bg-slate-700 transition-colors disabled:opacity-40"
158
+ >
159
+ {saving ? (
160
+ <span className="inline-flex items-center gap-1">
161
+ <span className="w-3 h-3 border border-white/40 border-t-white rounded-full animate-spin" />
162
+ Saving
163
+ </span>
164
+ ) : pendingReset ? 'Save & Reset' : 'Save'}
165
+ </button>
166
+ </div>
167
+ </div>
168
+ </div>
169
+ </div>
170
+ );
171
+ }