@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,164 @@
1
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
2
+ import { useKanbanStore } from '../kanbanStore';
3
+ import * as api from '../../lib/api';
4
+
5
+ // Mock the API module
6
+ vi.mock('../../lib/api', () => ({
7
+ getWorkItems: vi.fn(),
8
+ }));
9
+
10
+ describe('kanbanStore', () => {
11
+ beforeEach(() => {
12
+ // Reset store state before each test
13
+ useKanbanStore.setState({
14
+ workItems: [],
15
+ loading: false,
16
+ error: null,
17
+ });
18
+ vi.clearAllMocks();
19
+ });
20
+
21
+ describe('initial state', () => {
22
+ it('should have empty work items array', () => {
23
+ const state = useKanbanStore.getState();
24
+ expect(state.workItems).toEqual([]);
25
+ });
26
+
27
+ it('should not be loading initially', () => {
28
+ const state = useKanbanStore.getState();
29
+ expect(state.loading).toBe(false);
30
+ });
31
+
32
+ it('should have no error initially', () => {
33
+ const state = useKanbanStore.getState();
34
+ expect(state.error).toBeNull();
35
+ });
36
+ });
37
+
38
+ describe('loadWorkItems', () => {
39
+ it('should load work items successfully', async () => {
40
+ const mockItems = [
41
+ { id: 'EPIC-001', type: 'epic', status: 'implementing' },
42
+ { id: 'STORY-001', type: 'story', status: 'ready' },
43
+ ];
44
+
45
+ api.getWorkItems.mockResolvedValueOnce({ items: mockItems });
46
+
47
+ const { loadWorkItems } = useKanbanStore.getState();
48
+ await loadWorkItems();
49
+
50
+ const state = useKanbanStore.getState();
51
+ expect(state.workItems).toEqual(mockItems);
52
+ expect(state.loading).toBe(false);
53
+ expect(state.error).toBeNull();
54
+ });
55
+
56
+ it('should set loading state during fetch', async () => {
57
+ api.getWorkItems.mockImplementation(() => {
58
+ // Check loading state while promise is pending
59
+ const state = useKanbanStore.getState();
60
+ expect(state.loading).toBe(true);
61
+ return Promise.resolve({ items: [] });
62
+ });
63
+
64
+ const { loadWorkItems } = useKanbanStore.getState();
65
+ await loadWorkItems();
66
+ });
67
+
68
+ it('should handle errors gracefully', async () => {
69
+ const error = new Error('Failed to fetch');
70
+ api.getWorkItems.mockRejectedValueOnce(error);
71
+
72
+ const { loadWorkItems } = useKanbanStore.getState();
73
+ await loadWorkItems();
74
+
75
+ const state = useKanbanStore.getState();
76
+ expect(state.workItems).toEqual([]);
77
+ expect(state.loading).toBe(false);
78
+ expect(state.error).toBe('Failed to fetch');
79
+ });
80
+
81
+ it('should pass filters to API', async () => {
82
+ api.getWorkItems.mockResolvedValueOnce({ items: [] });
83
+
84
+ const { loadWorkItems } = useKanbanStore.getState();
85
+ await loadWorkItems({ type: 'epic', status: 'ready' });
86
+
87
+ expect(api.getWorkItems).toHaveBeenCalledWith({ type: 'epic', status: 'ready' });
88
+ });
89
+ });
90
+
91
+ describe('updateWorkItem', () => {
92
+ it('should update existing work item', () => {
93
+ const initialItems = [
94
+ { id: 'EPIC-001', name: 'Old Name', status: 'planned' },
95
+ { id: 'STORY-001', name: 'Story', status: 'ready' },
96
+ ];
97
+
98
+ useKanbanStore.setState({ workItems: initialItems });
99
+
100
+ const { updateWorkItem } = useKanbanStore.getState();
101
+ updateWorkItem('EPIC-001', { name: 'New Name', status: 'implementing' });
102
+
103
+ const state = useKanbanStore.getState();
104
+ expect(state.workItems[0]).toEqual({
105
+ id: 'EPIC-001',
106
+ name: 'New Name',
107
+ status: 'implementing',
108
+ });
109
+ expect(state.workItems[1]).toEqual(initialItems[1]); // Unchanged
110
+ });
111
+
112
+ it('should not modify state if item not found', () => {
113
+ const initialItems = [{ id: 'EPIC-001', name: 'Epic', status: 'planned' }];
114
+
115
+ useKanbanStore.setState({ workItems: initialItems });
116
+
117
+ const { updateWorkItem } = useKanbanStore.getState();
118
+ updateWorkItem('NONEXISTENT', { name: 'New Name' });
119
+
120
+ const state = useKanbanStore.getState();
121
+ expect(state.workItems).toEqual(initialItems);
122
+ });
123
+
124
+ it('should merge updates with existing properties', () => {
125
+ const initialItems = [
126
+ {
127
+ id: 'EPIC-001',
128
+ name: 'Epic',
129
+ status: 'planned',
130
+ description: 'Description',
131
+ type: 'epic',
132
+ },
133
+ ];
134
+
135
+ useKanbanStore.setState({ workItems: initialItems });
136
+
137
+ const { updateWorkItem } = useKanbanStore.getState();
138
+ updateWorkItem('EPIC-001', { status: 'implementing' });
139
+
140
+ const state = useKanbanStore.getState();
141
+ expect(state.workItems[0]).toEqual({
142
+ id: 'EPIC-001',
143
+ name: 'Epic',
144
+ status: 'implementing', // Updated
145
+ description: 'Description', // Preserved
146
+ type: 'epic', // Preserved
147
+ });
148
+ });
149
+ });
150
+
151
+ describe('refresh', () => {
152
+ it('should reload work items', async () => {
153
+ const mockItems = [{ id: 'EPIC-001', type: 'epic' }];
154
+ api.getWorkItems.mockResolvedValueOnce({ items: mockItems });
155
+
156
+ const { refresh } = useKanbanStore.getState();
157
+ await refresh();
158
+
159
+ const state = useKanbanStore.getState();
160
+ expect(state.workItems).toEqual(mockItems);
161
+ expect(api.getWorkItems).toHaveBeenCalled();
162
+ });
163
+ });
164
+ });
@@ -0,0 +1,172 @@
1
+ import { create } from 'zustand';
2
+
3
+ /**
4
+ * Ceremony Store
5
+ * Manages the sponsor-call wizard state (not persisted).
6
+ */
7
+ export const useCeremonyStore = create((set, get) => ({
8
+ // ── Modal open/close ───────────────────────────────────────────────────────
9
+ isOpen: false,
10
+
11
+ // ── Wizard navigation ──────────────────────────────────────────────────────
12
+ wizardStep: 1,
13
+ analyzing: false, // true while an analyze API call is in-flight
14
+
15
+ // ── Step 1: Deployment strategy ───────────────────────────────────────────
16
+ strategy: null, // 'local-mvp' | 'cloud'
17
+
18
+ // ── Step 2: Mission & Scope ───────────────────────────────────────────────
19
+ mission: '',
20
+ initialScope: '',
21
+
22
+ // ── Step 3: Database ──────────────────────────────────────────────────────
23
+ dbResult: null, // API response from analyze/database
24
+ dbChoice: null, // 'sql' | 'nosql' (user selection)
25
+
26
+ // ── Step 4: Architecture ──────────────────────────────────────────────────
27
+ archOptions: [], // array of architecture objects from analyze/architecture
28
+ selectedArch: null, // chosen architecture object
29
+
30
+ // ── Step 5: Review & Edit ─────────────────────────────────────────────────
31
+ prefillResult: null, // API response from analyze/prefill
32
+
33
+ // ── Requirements (all 7 template variables) ───────────────────────────────
34
+ requirements: {
35
+ MISSION_STATEMENT: '',
36
+ INITIAL_SCOPE: '',
37
+ TARGET_USERS: '',
38
+ DEPLOYMENT_TARGET: '',
39
+ TECHNICAL_CONSIDERATIONS: '',
40
+ TECHNICAL_EXCLUSIONS: '',
41
+ SECURITY_AND_COMPLIANCE_REQUIREMENTS: '',
42
+ },
43
+
44
+ // ── Mission generator progress ─────────────────────────────────────────────
45
+ missionProgressLog: [], // { step, message }
46
+
47
+ // ── Steps 6-7: Running / Complete ─────────────────────────────────────────
48
+ progressLog: [], // { type:'progress'|'substep', message?, substep?, meta? }
49
+ ceremonyStatus: 'idle', // 'idle' | 'running' | 'complete' | 'error'
50
+ ceremonyResult: null,
51
+ ceremonyError: null,
52
+ isPaused: false,
53
+ processId: null, // active fork processId (set after run starts)
54
+
55
+ // ── Actions ────────────────────────────────────────────────────────────────
56
+
57
+ openWizard: () => set({ isOpen: true }),
58
+
59
+ closeWizard: () => set({ isOpen: false }),
60
+
61
+ // Reopen without resetting state — used by ProcessMonitorBar chip click
62
+ reopenWizard: () => set((s) => ({
63
+ isOpen: true,
64
+ wizardStep: s.ceremonyStatus === 'complete' ? 7 : 6,
65
+ })),
66
+
67
+ resetWizard: () =>
68
+ set({
69
+ wizardStep: 1,
70
+ analyzing: false,
71
+ strategy: null,
72
+ mission: '',
73
+ initialScope: '',
74
+ dbResult: null,
75
+ dbChoice: null,
76
+ archOptions: [],
77
+ selectedArch: null,
78
+ prefillResult: null,
79
+ requirements: {
80
+ MISSION_STATEMENT: '',
81
+ INITIAL_SCOPE: '',
82
+ TARGET_USERS: '',
83
+ DEPLOYMENT_TARGET: '',
84
+ TECHNICAL_CONSIDERATIONS: '',
85
+ TECHNICAL_EXCLUSIONS: '',
86
+ SECURITY_AND_COMPLIANCE_REQUIREMENTS: '',
87
+ },
88
+ progressLog: [],
89
+ ceremonyStatus: 'idle',
90
+ ceremonyResult: null,
91
+ ceremonyError: null,
92
+ }),
93
+
94
+ setStrategy: (strategy) => set({ strategy }),
95
+
96
+ setMission: (mission) => set({ mission }),
97
+
98
+ setInitialScope: (initialScope) => set({ initialScope }),
99
+
100
+ setDbResult: (dbResult) => set({ dbResult }),
101
+
102
+ setDbChoice: (dbChoice) => set({ dbChoice }),
103
+
104
+ setArchOptions: (archOptions) => set({ archOptions }),
105
+
106
+ setSelectedArch: (selectedArch) => set({ selectedArch }),
107
+
108
+ setPrefillResult: (prefillResult) => set({ prefillResult }),
109
+
110
+ setAnalyzing: (analyzing) => set({ analyzing }),
111
+
112
+ setWizardStep: (wizardStep) => set({ wizardStep }),
113
+
114
+ updateRequirement: (key, value) =>
115
+ set((state) => ({
116
+ requirements: { ...state.requirements, [key]: value },
117
+ })),
118
+
119
+ setRequirements: (requirements) => set({ requirements }),
120
+
121
+ // Called from App.jsx WebSocket message handler
122
+ appendProgress: (entry) =>
123
+ set((state) => ({
124
+ progressLog: [...state.progressLog, entry],
125
+ })),
126
+
127
+ setProgressLog: (entries) => set({ progressLog: Array.isArray(entries) ? entries : [] }),
128
+
129
+ appendMissionProgress: (entry) =>
130
+ set((state) => ({
131
+ missionProgressLog: [...state.missionProgressLog, entry],
132
+ })),
133
+
134
+ clearMissionProgress: () => set({ missionProgressLog: [] }),
135
+
136
+ setCeremonyStatus: (ceremonyStatus) => set({ ceremonyStatus }),
137
+
138
+ setCeremonyResult: (ceremonyResult) => set({ ceremonyResult }),
139
+
140
+ setCeremonyError: (ceremonyError) => set({ ceremonyError }),
141
+
142
+ setPaused: (isPaused) => set({ isPaused }),
143
+
144
+ setProcessId: (processId) => set({ processId }),
145
+
146
+ // Sync requirements from prefill result + step 1-2 data
147
+ applyPrefill: (prefillResult, strategy, mission, initialScope) => {
148
+ set((state) => ({
149
+ prefillResult,
150
+ requirements: {
151
+ ...state.requirements,
152
+ MISSION_STATEMENT: mission,
153
+ INITIAL_SCOPE: initialScope,
154
+ TARGET_USERS: prefillResult.TARGET_USERS || '',
155
+ DEPLOYMENT_TARGET: prefillResult.DEPLOYMENT_TARGET || '',
156
+ TECHNICAL_CONSIDERATIONS: prefillResult.TECHNICAL_CONSIDERATIONS || '',
157
+ SECURITY_AND_COMPLIANCE_REQUIREMENTS:
158
+ prefillResult.SECURITY_AND_COMPLIANCE_REQUIREMENTS || '',
159
+ // TECHNICAL_EXCLUSIONS stays as user entered (empty by default)
160
+ },
161
+ }));
162
+ },
163
+
164
+ // Initialize ceremony run
165
+ startRun: () =>
166
+ set({
167
+ ceremonyStatus: 'running',
168
+ progressLog: [],
169
+ ceremonyResult: null,
170
+ ceremonyError: null,
171
+ }),
172
+ }));
@@ -0,0 +1,201 @@
1
+ import { create } from 'zustand';
2
+ import { persist } from 'zustand/middleware';
3
+
4
+ /**
5
+ * Filter Store
6
+ * Manages filters, grouping, and search state
7
+ * Persisted to localStorage
8
+ */
9
+ export const useFilterStore = create(
10
+ persist(
11
+ (set, get) => ({
12
+ // State
13
+ typeFilters: {
14
+ epic: true,
15
+ story: true,
16
+ task: true,
17
+ subtask: true,
18
+ },
19
+ columnVisibility: {
20
+ Backlog: true,
21
+ Ready: true,
22
+ 'In Progress': true,
23
+ Review: true,
24
+ Done: true,
25
+ },
26
+ searchQuery: '',
27
+ groupBy: 'epic', // 'status' | 'epic' | 'type' | 'category'
28
+
29
+ // Actions
30
+
31
+ /**
32
+ * Toggle type filter
33
+ * @param {string} type - Work item type
34
+ */
35
+ toggleTypeFilter: (type) => {
36
+ set((state) => ({
37
+ typeFilters: {
38
+ ...state.typeFilters,
39
+ [type]: !state.typeFilters[type],
40
+ },
41
+ }));
42
+ },
43
+
44
+ /**
45
+ * Set all type filters
46
+ * @param {boolean} value - Enable or disable all
47
+ */
48
+ setAllTypeFilters: (value) => {
49
+ set({
50
+ typeFilters: {
51
+ epic: value,
52
+ story: value,
53
+ task: value,
54
+ subtask: value,
55
+ },
56
+ });
57
+ },
58
+
59
+ /**
60
+ * Toggle column visibility
61
+ * @param {string} column - Column name
62
+ */
63
+ toggleColumnVisibility: (column) => {
64
+ set((state) => ({
65
+ columnVisibility: {
66
+ ...state.columnVisibility,
67
+ [column]: !state.columnVisibility[column],
68
+ },
69
+ }));
70
+ },
71
+
72
+ /**
73
+ * Set all column visibility
74
+ * @param {boolean} value - Show or hide all
75
+ */
76
+ setAllColumnsVisibility: (value) => {
77
+ set((state) => {
78
+ const updated = {};
79
+ Object.keys(state.columnVisibility).forEach((col) => {
80
+ updated[col] = value;
81
+ });
82
+ return { columnVisibility: updated };
83
+ });
84
+ },
85
+
86
+ /**
87
+ * Apply preset filter
88
+ * @param {string} preset - Preset name
89
+ */
90
+ applyPreset: (preset) => {
91
+ switch (preset) {
92
+ case 'all':
93
+ get().setAllColumnsVisibility(true);
94
+ get().setAllTypeFilters(true);
95
+ break;
96
+ case 'active':
97
+ set({
98
+ columnVisibility: {
99
+ Backlog: false,
100
+ Ready: true,
101
+ 'In Progress': true,
102
+ Review: true,
103
+ Done: false,
104
+ },
105
+ });
106
+ break;
107
+ case 'hide-completed':
108
+ set({
109
+ columnVisibility: {
110
+ Backlog: true,
111
+ Ready: true,
112
+ 'In Progress': true,
113
+ Review: true,
114
+ Done: false,
115
+ },
116
+ });
117
+ break;
118
+ default:
119
+ break;
120
+ }
121
+ },
122
+
123
+ /**
124
+ * Set search query
125
+ * @param {string} query - Search query
126
+ */
127
+ setSearchQuery: (query) => {
128
+ set({ searchQuery: query });
129
+ },
130
+
131
+ /**
132
+ * Clear search query
133
+ */
134
+ clearSearch: () => {
135
+ set({ searchQuery: '' });
136
+ },
137
+
138
+ /**
139
+ * Set grouping mode
140
+ * @param {string} mode - Grouping mode
141
+ */
142
+ setGroupBy: (mode) => {
143
+ set({ groupBy: mode });
144
+ },
145
+
146
+ /**
147
+ * Get active type filters
148
+ * @returns {string[]} Array of active types
149
+ */
150
+ getActiveTypes: () => {
151
+ const { typeFilters } = get();
152
+ return Object.entries(typeFilters)
153
+ .filter(([_, enabled]) => enabled)
154
+ .map(([type]) => type);
155
+ },
156
+
157
+ /**
158
+ * Get visible columns
159
+ * @returns {string[]} Array of visible column names
160
+ */
161
+ getVisibleColumns: () => {
162
+ const { columnVisibility } = get();
163
+ return Object.entries(columnVisibility)
164
+ .filter(([_, visible]) => visible)
165
+ .map(([column]) => column);
166
+ },
167
+
168
+ /**
169
+ * Reset all filters to defaults
170
+ */
171
+ resetFilters: () => {
172
+ set({
173
+ typeFilters: {
174
+ epic: true,
175
+ story: true,
176
+ task: true,
177
+ subtask: true,
178
+ },
179
+ columnVisibility: {
180
+ Backlog: true,
181
+ Ready: true,
182
+ 'In Progress': true,
183
+ Review: true,
184
+ Done: true,
185
+ },
186
+ searchQuery: '',
187
+ groupBy: 'epic',
188
+ });
189
+ },
190
+ }),
191
+ {
192
+ name: 'avc-kanban-filters', // localStorage key
193
+ partialize: (state) => ({
194
+ // Only persist these fields
195
+ typeFilters: state.typeFilters,
196
+ columnVisibility: state.columnVisibility,
197
+ groupBy: state.groupBy,
198
+ }),
199
+ }
200
+ )
201
+ );
@@ -0,0 +1,123 @@
1
+ import { create } from 'zustand';
2
+ import { getWorkItems, getWorkItemsGrouped } from '../lib/api';
3
+
4
+ /**
5
+ * Kanban Store
6
+ * Manages work items, loading states, and data fetching
7
+ */
8
+ export const useKanbanStore = create((set, get) => ({
9
+ // State
10
+ workItems: [],
11
+ groupedItems: {},
12
+ loading: false,
13
+ error: null,
14
+ lastUpdated: null,
15
+ ceremonyActive: false, // true while sprint-planning is writing items — board is read-only
16
+
17
+ // Actions
18
+
19
+ /**
20
+ * Load all work items
21
+ * @param {object} filters - Optional filters
22
+ */
23
+ loadWorkItems: async (filters = {}) => {
24
+ set({ loading: true, error: null });
25
+
26
+ try {
27
+ const data = await getWorkItems(filters);
28
+ set({
29
+ workItems: data.items || [],
30
+ loading: false,
31
+ lastUpdated: Date.now(),
32
+ error: null,
33
+ });
34
+ } catch (error) {
35
+ set({
36
+ loading: false,
37
+ error: error.message,
38
+ });
39
+ console.error('Failed to load work items:', error);
40
+ }
41
+ },
42
+
43
+ /**
44
+ * Load work items grouped by column
45
+ */
46
+ loadGroupedItems: async () => {
47
+ set({ loading: true, error: null });
48
+
49
+ try {
50
+ const data = await getWorkItemsGrouped();
51
+ set({
52
+ groupedItems: data,
53
+ loading: false,
54
+ lastUpdated: Date.now(),
55
+ error: null,
56
+ });
57
+ } catch (error) {
58
+ set({
59
+ loading: false,
60
+ error: error.message,
61
+ });
62
+ console.error('Failed to load grouped items:', error);
63
+ }
64
+ },
65
+
66
+ /**
67
+ * Refresh work items (re-fetch from server)
68
+ */
69
+ refresh: async () => {
70
+ const { loadWorkItems } = get();
71
+ await loadWorkItems();
72
+ },
73
+
74
+ /**
75
+ * Update a single work item in the store
76
+ * Used for WebSocket updates
77
+ * @param {string} id - Work item ID
78
+ * @param {object} updatedItem - Updated work item data
79
+ */
80
+ updateWorkItem: (id, updatedItem) => {
81
+ set((state) => ({
82
+ workItems: state.workItems.map((item) =>
83
+ item.id === id ? { ...item, ...updatedItem } : item
84
+ ),
85
+ }));
86
+ },
87
+
88
+ /**
89
+ * Add a new work item to the store
90
+ * Used for WebSocket updates
91
+ * @param {object} newItem - New work item
92
+ */
93
+ addWorkItem: (newItem) => {
94
+ set((state) => ({
95
+ workItems: [...state.workItems, newItem],
96
+ }));
97
+ },
98
+
99
+ /**
100
+ * Remove a work item from the store
101
+ * Used for WebSocket updates
102
+ * @param {string} id - Work item ID to remove
103
+ */
104
+ removeWorkItem: (id) => {
105
+ set((state) => ({
106
+ workItems: state.workItems.filter((item) => item.id !== id),
107
+ }));
108
+ },
109
+
110
+ /**
111
+ * Clear error state
112
+ */
113
+ clearError: () => {
114
+ set({ error: null });
115
+ },
116
+
117
+ /**
118
+ * Set ceremony active state (read-only mode for work items)
119
+ */
120
+ setCeremonyActive: (active) => {
121
+ set({ ceremonyActive: active });
122
+ },
123
+ }));