@agile-vibe-coding/avc 0.2.3 → 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 (261) hide show
  1. package/cli/agents/agent-selector.md +23 -0
  2. package/cli/agents/code-implementer.md +117 -0
  3. package/cli/agents/code-validator.md +80 -0
  4. package/cli/agents/context-reviewer-epic.md +101 -0
  5. package/cli/agents/context-reviewer-story.md +92 -0
  6. package/cli/agents/context-writer-epic.md +145 -0
  7. package/cli/agents/context-writer-story.md +111 -0
  8. package/cli/agents/doc-writer-epic.md +42 -0
  9. package/cli/agents/doc-writer-story.md +43 -0
  10. package/cli/agents/duplicate-detector.md +110 -0
  11. package/cli/agents/epic-story-decomposer.md +318 -39
  12. package/cli/agents/mission-scope-generator.md +68 -4
  13. package/cli/agents/mission-scope-validator.md +40 -6
  14. package/cli/agents/project-context-extractor.md +21 -6
  15. package/cli/agents/scaffolding-generator.md +99 -0
  16. package/cli/agents/seed-validator.md +71 -0
  17. package/cli/agents/story-scope-reviewer.md +147 -0
  18. package/cli/agents/story-splitter.md +83 -0
  19. package/cli/agents/validator-documentation.json +31 -0
  20. package/cli/agents/validator-documentation.md +3 -1
  21. package/cli/api-reference-tool.js +368 -0
  22. package/cli/checks/catalog.json +76 -0
  23. package/cli/checks/code/quality.json +26 -0
  24. package/cli/checks/code/testing.json +14 -0
  25. package/cli/checks/code/traceability.json +26 -0
  26. package/cli/checks/cross-refs/epic.json +171 -0
  27. package/cli/checks/cross-refs/story.json +149 -0
  28. package/cli/checks/epic/api.json +114 -0
  29. package/cli/checks/epic/backend.json +126 -0
  30. package/cli/checks/epic/cloud.json +126 -0
  31. package/cli/checks/epic/data.json +102 -0
  32. package/cli/checks/epic/database.json +114 -0
  33. package/cli/checks/epic/developer.json +182 -0
  34. package/cli/checks/epic/devops.json +174 -0
  35. package/cli/checks/epic/frontend.json +162 -0
  36. package/cli/checks/epic/mobile.json +102 -0
  37. package/cli/checks/epic/qa.json +90 -0
  38. package/cli/checks/epic/security.json +184 -0
  39. package/cli/checks/epic/solution-architect.json +192 -0
  40. package/cli/checks/epic/test-architect.json +90 -0
  41. package/cli/checks/epic/ui.json +102 -0
  42. package/cli/checks/epic/ux.json +90 -0
  43. package/cli/checks/fixes/epic-fix-template.md +10 -0
  44. package/cli/checks/fixes/story-fix-template.md +10 -0
  45. package/cli/checks/story/api.json +186 -0
  46. package/cli/checks/story/backend.json +102 -0
  47. package/cli/checks/story/cloud.json +102 -0
  48. package/cli/checks/story/data.json +210 -0
  49. package/cli/checks/story/database.json +102 -0
  50. package/cli/checks/story/developer.json +168 -0
  51. package/cli/checks/story/devops.json +102 -0
  52. package/cli/checks/story/frontend.json +174 -0
  53. package/cli/checks/story/mobile.json +102 -0
  54. package/cli/checks/story/qa.json +210 -0
  55. package/cli/checks/story/security.json +198 -0
  56. package/cli/checks/story/solution-architect.json +230 -0
  57. package/cli/checks/story/test-architect.json +210 -0
  58. package/cli/checks/story/ui.json +102 -0
  59. package/cli/checks/story/ux.json +102 -0
  60. package/cli/coding-order.js +401 -0
  61. package/cli/dependency-checker.js +72 -0
  62. package/cli/epic-story-validator.js +284 -799
  63. package/cli/index.js +0 -0
  64. package/cli/init-model-config.js +17 -10
  65. package/cli/init.js +514 -92
  66. package/cli/kanban-server-manager.js +1 -2
  67. package/cli/llm-claude.js +98 -31
  68. package/cli/llm-gemini.js +29 -5
  69. package/cli/llm-local.js +493 -0
  70. package/cli/llm-openai.js +262 -41
  71. package/cli/llm-provider.js +147 -8
  72. package/cli/llm-token-limits.js +113 -4
  73. package/cli/llm-verifier.js +209 -1
  74. package/cli/llm-xiaomi.js +143 -0
  75. package/cli/message-constants.js +3 -12
  76. package/cli/messaging-api.js +6 -12
  77. package/cli/micro-check-fixer.js +335 -0
  78. package/cli/micro-check-runner.js +449 -0
  79. package/cli/micro-check-scorer.js +148 -0
  80. package/cli/micro-check-validator.js +538 -0
  81. package/cli/model-pricing.js +23 -0
  82. package/cli/model-selector.js +3 -2
  83. package/cli/prompt-logger.js +57 -0
  84. package/cli/repl-ink.js +106 -346
  85. package/cli/repl-old.js +1 -2
  86. package/cli/seed-processor.js +194 -24
  87. package/cli/sprint-planning-processor.js +2638 -289
  88. package/cli/template-processor.js +50 -3
  89. package/cli/token-tracker.js +50 -23
  90. package/cli/tools/generate-story-validators.js +1 -1
  91. package/cli/validation-router.js +70 -8
  92. package/cli/worktree-runner.js +654 -0
  93. package/kanban/client/dist/assets/index-D_KC5EQT.css +1 -0
  94. package/kanban/client/dist/assets/index-DjY5zqW7.js +351 -0
  95. package/kanban/client/dist/index.html +2 -2
  96. package/kanban/client/src/App.jsx +43 -14
  97. package/kanban/client/src/components/ceremony/AskArchPopup.jsx +7 -3
  98. package/kanban/client/src/components/ceremony/AskModelPopup.jsx +23 -10
  99. package/kanban/client/src/components/ceremony/CeremonyWorkflowModal.jsx +320 -133
  100. package/kanban/client/src/components/ceremony/ProviderSwitcherButton.jsx +290 -0
  101. package/kanban/client/src/components/ceremony/SponsorCallModal.jsx +80 -13
  102. package/kanban/client/src/components/ceremony/SprintPlanningModal.jsx +156 -22
  103. package/kanban/client/src/components/ceremony/steps/ArchitectureStep.jsx +11 -11
  104. package/kanban/client/src/components/ceremony/steps/CompleteStep.jsx +3 -21
  105. package/kanban/client/src/components/ceremony/steps/ReviewAnswersStep.jsx +214 -10
  106. package/kanban/client/src/components/ceremony/steps/RunningStep.jsx +23 -2
  107. package/kanban/client/src/components/kanban/CardDetailModal.jsx +97 -10
  108. package/kanban/client/src/components/kanban/GroupingSelector.jsx +7 -1
  109. package/kanban/client/src/components/kanban/KanbanCard.jsx +23 -14
  110. package/kanban/client/src/components/kanban/RefineWorkItemPopup.jsx +9 -14
  111. package/kanban/client/src/components/kanban/RunButton.jsx +162 -0
  112. package/kanban/client/src/components/kanban/SeedButton.jsx +176 -0
  113. package/kanban/client/src/components/settings/AgentsTab.jsx +103 -75
  114. package/kanban/client/src/components/settings/ApiKeysTab.jsx +31 -2
  115. package/kanban/client/src/components/settings/CeremonyModelsTab.jsx +9 -2
  116. package/kanban/client/src/components/settings/CheckEditorPopup.jsx +507 -0
  117. package/kanban/client/src/components/settings/CostThresholdsTab.jsx +3 -2
  118. package/kanban/client/src/components/settings/ModelPricingTab.jsx +72 -7
  119. package/kanban/client/src/components/settings/OpenAIAuthSection.jsx +412 -0
  120. package/kanban/client/src/components/settings/SettingsModal.jsx +4 -4
  121. package/kanban/client/src/components/stats/CostModal.jsx +34 -3
  122. package/kanban/client/src/hooks/useGrouping.js +59 -0
  123. package/kanban/client/src/lib/api.js +118 -4
  124. package/kanban/client/src/lib/status-grouping.js +10 -0
  125. package/kanban/client/src/store/kanbanStore.js +8 -0
  126. package/kanban/server/index.js +23 -2
  127. package/kanban/server/routes/ceremony.js +153 -4
  128. package/kanban/server/routes/costs.js +9 -3
  129. package/kanban/server/routes/openai-oauth.js +366 -0
  130. package/kanban/server/routes/settings.js +447 -14
  131. package/kanban/server/routes/websocket.js +7 -2
  132. package/kanban/server/routes/work-items.js +141 -1
  133. package/kanban/server/services/CeremonyService.js +275 -24
  134. package/kanban/server/services/TaskRunnerService.js +261 -0
  135. package/kanban/server/workers/run-task-worker.js +121 -0
  136. package/kanban/server/workers/seed-worker.js +94 -0
  137. package/kanban/server/workers/sponsor-call-worker.js +14 -6
  138. package/kanban/server/workers/sprint-planning-worker.js +94 -12
  139. package/package.json +2 -3
  140. package/cli/agents/solver-epic-api.json +0 -15
  141. package/cli/agents/solver-epic-api.md +0 -39
  142. package/cli/agents/solver-epic-backend.json +0 -15
  143. package/cli/agents/solver-epic-backend.md +0 -39
  144. package/cli/agents/solver-epic-cloud.json +0 -15
  145. package/cli/agents/solver-epic-cloud.md +0 -39
  146. package/cli/agents/solver-epic-data.json +0 -15
  147. package/cli/agents/solver-epic-data.md +0 -39
  148. package/cli/agents/solver-epic-database.json +0 -15
  149. package/cli/agents/solver-epic-database.md +0 -39
  150. package/cli/agents/solver-epic-developer.json +0 -15
  151. package/cli/agents/solver-epic-developer.md +0 -39
  152. package/cli/agents/solver-epic-devops.json +0 -15
  153. package/cli/agents/solver-epic-devops.md +0 -39
  154. package/cli/agents/solver-epic-frontend.json +0 -15
  155. package/cli/agents/solver-epic-frontend.md +0 -39
  156. package/cli/agents/solver-epic-mobile.json +0 -15
  157. package/cli/agents/solver-epic-mobile.md +0 -39
  158. package/cli/agents/solver-epic-qa.json +0 -15
  159. package/cli/agents/solver-epic-qa.md +0 -39
  160. package/cli/agents/solver-epic-security.json +0 -15
  161. package/cli/agents/solver-epic-security.md +0 -39
  162. package/cli/agents/solver-epic-solution-architect.json +0 -15
  163. package/cli/agents/solver-epic-solution-architect.md +0 -39
  164. package/cli/agents/solver-epic-test-architect.json +0 -15
  165. package/cli/agents/solver-epic-test-architect.md +0 -39
  166. package/cli/agents/solver-epic-ui.json +0 -15
  167. package/cli/agents/solver-epic-ui.md +0 -39
  168. package/cli/agents/solver-epic-ux.json +0 -15
  169. package/cli/agents/solver-epic-ux.md +0 -39
  170. package/cli/agents/solver-story-api.json +0 -15
  171. package/cli/agents/solver-story-api.md +0 -39
  172. package/cli/agents/solver-story-backend.json +0 -15
  173. package/cli/agents/solver-story-backend.md +0 -39
  174. package/cli/agents/solver-story-cloud.json +0 -15
  175. package/cli/agents/solver-story-cloud.md +0 -39
  176. package/cli/agents/solver-story-data.json +0 -15
  177. package/cli/agents/solver-story-data.md +0 -39
  178. package/cli/agents/solver-story-database.json +0 -15
  179. package/cli/agents/solver-story-database.md +0 -39
  180. package/cli/agents/solver-story-developer.json +0 -15
  181. package/cli/agents/solver-story-developer.md +0 -39
  182. package/cli/agents/solver-story-devops.json +0 -15
  183. package/cli/agents/solver-story-devops.md +0 -39
  184. package/cli/agents/solver-story-frontend.json +0 -15
  185. package/cli/agents/solver-story-frontend.md +0 -39
  186. package/cli/agents/solver-story-mobile.json +0 -15
  187. package/cli/agents/solver-story-mobile.md +0 -39
  188. package/cli/agents/solver-story-qa.json +0 -15
  189. package/cli/agents/solver-story-qa.md +0 -39
  190. package/cli/agents/solver-story-security.json +0 -15
  191. package/cli/agents/solver-story-security.md +0 -39
  192. package/cli/agents/solver-story-solution-architect.json +0 -15
  193. package/cli/agents/solver-story-solution-architect.md +0 -39
  194. package/cli/agents/solver-story-test-architect.json +0 -15
  195. package/cli/agents/solver-story-test-architect.md +0 -39
  196. package/cli/agents/solver-story-ui.json +0 -15
  197. package/cli/agents/solver-story-ui.md +0 -39
  198. package/cli/agents/solver-story-ux.json +0 -15
  199. package/cli/agents/solver-story-ux.md +0 -39
  200. package/cli/agents/validator-epic-api.json +0 -93
  201. package/cli/agents/validator-epic-api.md +0 -137
  202. package/cli/agents/validator-epic-backend.json +0 -93
  203. package/cli/agents/validator-epic-backend.md +0 -130
  204. package/cli/agents/validator-epic-cloud.json +0 -93
  205. package/cli/agents/validator-epic-cloud.md +0 -137
  206. package/cli/agents/validator-epic-data.json +0 -93
  207. package/cli/agents/validator-epic-data.md +0 -130
  208. package/cli/agents/validator-epic-database.json +0 -93
  209. package/cli/agents/validator-epic-database.md +0 -137
  210. package/cli/agents/validator-epic-developer.json +0 -74
  211. package/cli/agents/validator-epic-developer.md +0 -153
  212. package/cli/agents/validator-epic-devops.json +0 -74
  213. package/cli/agents/validator-epic-devops.md +0 -153
  214. package/cli/agents/validator-epic-frontend.json +0 -74
  215. package/cli/agents/validator-epic-frontend.md +0 -153
  216. package/cli/agents/validator-epic-mobile.json +0 -93
  217. package/cli/agents/validator-epic-mobile.md +0 -130
  218. package/cli/agents/validator-epic-qa.json +0 -93
  219. package/cli/agents/validator-epic-qa.md +0 -130
  220. package/cli/agents/validator-epic-security.json +0 -74
  221. package/cli/agents/validator-epic-security.md +0 -154
  222. package/cli/agents/validator-epic-solution-architect.json +0 -74
  223. package/cli/agents/validator-epic-solution-architect.md +0 -156
  224. package/cli/agents/validator-epic-test-architect.json +0 -93
  225. package/cli/agents/validator-epic-test-architect.md +0 -130
  226. package/cli/agents/validator-epic-ui.json +0 -93
  227. package/cli/agents/validator-epic-ui.md +0 -130
  228. package/cli/agents/validator-epic-ux.json +0 -93
  229. package/cli/agents/validator-epic-ux.md +0 -130
  230. package/cli/agents/validator-story-api.json +0 -104
  231. package/cli/agents/validator-story-api.md +0 -152
  232. package/cli/agents/validator-story-backend.json +0 -104
  233. package/cli/agents/validator-story-backend.md +0 -152
  234. package/cli/agents/validator-story-cloud.json +0 -104
  235. package/cli/agents/validator-story-cloud.md +0 -152
  236. package/cli/agents/validator-story-data.json +0 -104
  237. package/cli/agents/validator-story-data.md +0 -152
  238. package/cli/agents/validator-story-database.json +0 -104
  239. package/cli/agents/validator-story-database.md +0 -152
  240. package/cli/agents/validator-story-developer.json +0 -104
  241. package/cli/agents/validator-story-developer.md +0 -152
  242. package/cli/agents/validator-story-devops.json +0 -104
  243. package/cli/agents/validator-story-devops.md +0 -152
  244. package/cli/agents/validator-story-frontend.json +0 -104
  245. package/cli/agents/validator-story-frontend.md +0 -152
  246. package/cli/agents/validator-story-mobile.json +0 -104
  247. package/cli/agents/validator-story-mobile.md +0 -152
  248. package/cli/agents/validator-story-qa.json +0 -104
  249. package/cli/agents/validator-story-qa.md +0 -152
  250. package/cli/agents/validator-story-security.json +0 -104
  251. package/cli/agents/validator-story-security.md +0 -152
  252. package/cli/agents/validator-story-solution-architect.json +0 -104
  253. package/cli/agents/validator-story-solution-architect.md +0 -152
  254. package/cli/agents/validator-story-test-architect.json +0 -104
  255. package/cli/agents/validator-story-test-architect.md +0 -152
  256. package/cli/agents/validator-story-ui.json +0 -104
  257. package/cli/agents/validator-story-ui.md +0 -152
  258. package/cli/agents/validator-story-ux.json +0 -104
  259. package/cli/agents/validator-story-ux.md +0 -152
  260. package/kanban/client/dist/assets/index-CiD8PS2e.js +0 -306
  261. package/kanban/client/dist/assets/index-nLh0m82Q.css +0 -1
@@ -7,8 +7,8 @@
7
7
  <meta name="avc-kanban" content="true" />
8
8
  <meta name="generator" content="Agile Vibe Coding" />
9
9
  <title>AVC Kanban Board</title>
10
- <script type="module" crossorigin src="/assets/index-CiD8PS2e.js"></script>
11
- <link rel="stylesheet" crossorigin href="/assets/index-nLh0m82Q.css">
10
+ <script type="module" crossorigin src="/assets/index-DjY5zqW7.js"></script>
11
+ <link rel="stylesheet" crossorigin href="/assets/index-D_KC5EQT.css">
12
12
  </head>
13
13
  <body>
14
14
  <div id="root"></div>
@@ -1,6 +1,6 @@
1
1
  import { useEffect, useState, useMemo, useRef } from 'react';
2
2
  import { Pencil, Check, X, BookOpen, Settings, DollarSign } from 'lucide-react';
3
- import { getHealth, getBoardTitle, updateBoardTitle, getDocsUrl, getSettings, getModels, getCostSummary, getProjectStatus, getCeremonyStatus, continuePastCostLimit, cancelCeremony } from './lib/api';
3
+ import { getHealth, getBoardTitle, updateBoardTitle, getDocsUrl, getSettings, getModels, getCostSummary, getProjectStatus, getCeremonyStatus, continuePastCostLimit, continueAfterQuota, cancelCeremony } from './lib/api';
4
4
  import { useWebSocket } from './hooks/useWebSocket';
5
5
  import { useKanbanStore } from './store/kanbanStore';
6
6
  import { useFilterStore } from './store/filterStore';
@@ -43,6 +43,9 @@ function App() {
43
43
  // Cost-limit pause dialog — { cost, threshold, runningType } when ceremony hits limit
44
44
  const [costLimitPending, setCostLimitPending] = useState(null);
45
45
 
46
+ // Quota-limit pause dialog — { provider, model, errMsg, validatorName, runningType } when quota exceeded
47
+ const [quotaLimitPending, setQuotaLimitPending] = useState(null);
48
+
46
49
  // Refine work item state — { itemId, jobId, message } / { itemId, jobId, result } / { itemId, jobId, error }
47
50
  const [refineProgress, setRefineProgress] = useState(null);
48
51
  const [refineResult, setRefineResult] = useState(null);
@@ -54,7 +57,7 @@ function App() {
54
57
  const [editingProjectFile, setEditingProjectFile] = useState(null); // 'doc' | null
55
58
 
56
59
  // Zustand stores
57
- const { workItems, loadWorkItems, loading, error } = useKanbanStore();
60
+ const { workItems, loadWorkItems, loading, error, setCeremonyActive } = useKanbanStore();
58
61
  const { typeFilters, searchQuery } = useFilterStore();
59
62
  const {
60
63
  isOpen: ceremonyOpen,
@@ -122,6 +125,7 @@ function App() {
122
125
  appendProgress({ type: 'detail', detail: message.detail });
123
126
  } else if (message.type === 'ceremony:complete') {
124
127
  setCostLimitPending(null);
128
+ setQuotaLimitPending(null);
125
129
  setCeremonyStatus('complete');
126
130
  setCeremonyResult(message.result);
127
131
  setWizardStep(7);
@@ -134,6 +138,8 @@ function App() {
134
138
  appendMissionProgress({ step: message.step, message: message.message });
135
139
  } else if (message.type === 'ceremony:cost-limit') {
136
140
  setCostLimitPending({ cost: message.cost, threshold: message.threshold, runningType: message.runningType });
141
+ } else if (message.type === 'ceremony:quota-limit') {
142
+ setQuotaLimitPending({ provider: message.provider, model: message.model, errMsg: message.errMsg, validatorName: message.validatorName, runningType: message.runningType });
137
143
  } else if (message.type === 'cost:update') {
138
144
  getCostSummary().then(setCostSummary).catch(() => { });
139
145
  } else if (message.type === 'sprint-planning:progress') {
@@ -148,33 +154,48 @@ function App() {
148
154
  setSprintPlanningStep(3);
149
155
  } else if (message.type === 'sprint-planning:complete') {
150
156
  setCostLimitPending(null);
157
+ setQuotaLimitPending(null);
151
158
  setSprintPlanningStatus('complete');
152
159
  setSprintPlanningResult(message.result);
153
160
  setSprintPlanningDecomposedHierarchy(null);
154
161
  setSprintPlanningStep(4);
162
+ setCeremonyActive(false);
155
163
  loadWorkItems();
156
164
  } else if (message.type === 'sprint-planning:error') {
157
165
  setSprintPlanningStatus('error');
158
166
  setSprintPlanningError(message.error);
167
+ setCeremonyActive(false);
159
168
  } else if (message.type === 'sprint-planning:paused') {
160
169
  setSprintPlanningPaused(true);
161
170
  } else if (message.type === 'sprint-planning:resumed') {
162
171
  setSprintPlanningPaused(false);
163
172
  } else if (message.type === 'sprint-planning:cancelled') {
164
173
  setCostLimitPending(null);
174
+ setQuotaLimitPending(null);
165
175
  setSprintPlanningStatus('idle');
166
176
  setSprintPlanningStep(1);
167
177
  setSprintPlanningPaused(false);
168
178
  setSprintPlanningDecomposedHierarchy(null);
179
+ setCeremonyActive(false);
169
180
  } else if (message.type === 'ceremony:paused') {
170
181
  setCeremonyPaused(true);
171
182
  } else if (message.type === 'ceremony:resumed') {
172
183
  setCeremonyPaused(false);
173
184
  } else if (message.type === 'ceremony:cancelled') {
174
185
  setCostLimitPending(null);
186
+ setQuotaLimitPending(null);
175
187
  setCeremonyStatus('idle');
176
188
  setWizardStep(1);
177
189
  setCeremonyPaused(false);
190
+ } else if (message.type?.startsWith('seed:') || message.type?.startsWith('run-task:')) {
191
+ // Dispatch seed/run events to components via CustomEvent
192
+ window.dispatchEvent(new CustomEvent('avc-ws-message', { detail: message }));
193
+ // Refresh board on completion or error
194
+ if (message.type.endsWith(':complete') || message.type.endsWith(':error')) {
195
+ loadWorkItems();
196
+ }
197
+ } else if (message.type === 'refresh') {
198
+ loadWorkItems();
178
199
  } else if (message.type === 'refine:progress') {
179
200
  setRefineProgress({ itemId: message.itemId, jobId: message.jobId, message: message.message });
180
201
  } else if (message.type === 'refine:complete') {
@@ -187,6 +208,7 @@ function App() {
187
208
  handleProcessMessage(message);
188
209
  if (message.processType === 'sprint-planning') {
189
210
  setSprintPlanningProcessId(message.processId);
211
+ setCeremonyActive(true);
190
212
  } else if (message.processType === 'sponsor-call') {
191
213
  setCeremonyProcessId(message.processId);
192
214
  }
@@ -196,8 +218,9 @@ function App() {
196
218
  // Server sends this on WebSocket connect when a ceremony is already running.
197
219
  // Restores client state without requiring an HTTP round-trip.
198
220
  const cs = message.ceremonyStatus;
199
- if (cs?.status === 'running' || cs?.status === 'cost-limit-pending' || cs?.status === 'awaiting-selection') {
221
+ if (cs?.status === 'running' || cs?.status === 'cost-limit-pending' || cs?.status === 'quota-limit-pending' || cs?.status === 'awaiting-selection') {
200
222
  if (cs.runningType === 'sprint-planning') {
223
+ setCeremonyActive(true);
201
224
  if (cs.status === 'awaiting-selection') {
202
225
  setSprintPlanningStatus('awaiting-selection');
203
226
  setSprintPlanningDecomposedHierarchy(cs.decomposedHierarchy || null);
@@ -215,6 +238,9 @@ function App() {
215
238
  if (cs.status === 'cost-limit-pending' && cs.costLimitInfo) {
216
239
  setCostLimitPending({ ...cs.costLimitInfo, runningType: cs.runningType });
217
240
  }
241
+ if (cs.status === 'quota-limit-pending' && cs.quotaLimitInfo) {
242
+ setQuotaLimitPending({ ...cs.quotaLimitInfo, runningType: cs.runningType });
243
+ }
218
244
  }
219
245
  }
220
246
  },
@@ -238,7 +264,7 @@ function App() {
238
264
 
239
265
  // Restore running ceremony state BEFORE revealing projectFilesLoaded so the
240
266
  // board never shows "Start" buttons for an already-running ceremony.
241
- if (ceremonyState?.status === 'running' || ceremonyState?.status === 'cost-limit-pending' || ceremonyState?.status === 'awaiting-selection') {
267
+ if (ceremonyState?.status === 'running' || ceremonyState?.status === 'cost-limit-pending' || ceremonyState?.status === 'quota-limit-pending' || ceremonyState?.status === 'awaiting-selection') {
242
268
  if (ceremonyState.runningType === 'sprint-planning') {
243
269
  if (ceremonyState.status === 'awaiting-selection') {
244
270
  setSprintPlanningStatus('awaiting-selection');
@@ -257,6 +283,9 @@ function App() {
257
283
  if (ceremonyState.status === 'cost-limit-pending' && ceremonyState.costLimitInfo) {
258
284
  setCostLimitPending({ ...ceremonyState.costLimitInfo, runningType: ceremonyState.runningType });
259
285
  }
286
+ if (ceremonyState.status === 'quota-limit-pending' && ceremonyState.quotaLimitInfo) {
287
+ setQuotaLimitPending({ ...ceremonyState.quotaLimitInfo, runningType: ceremonyState.runningType });
288
+ }
260
289
  }
261
290
 
262
291
  setProjectFilesLoaded(true);
@@ -320,11 +349,13 @@ function App() {
320
349
 
321
350
  // ── Settings modal ─────────────────────────────────────────────────────────
322
351
 
323
- const openSettings = async () => {
352
+ const [settingsInitialTab, setSettingsInitialTab] = useState('api-keys');
353
+ const openSettings = async (initialTab = 'api-keys') => {
324
354
  try {
325
355
  const [data, modelList] = await Promise.all([getSettings(), getModels()]);
326
356
  setSettingsSnapshot(data);
327
357
  setModelsSnapshot(modelList);
358
+ setSettingsInitialTab(initialTab);
328
359
  setSettingsOpen(true);
329
360
  } catch (err) {
330
361
  console.error('Failed to load settings:', err);
@@ -337,6 +368,7 @@ function App() {
337
368
  setSettingsSnapshot(data);
338
369
  const title = await getBoardTitle();
339
370
  setBoardTitle(title);
371
+ document.dispatchEvent(new CustomEvent('avc:settings-saved'));
340
372
  } catch (err) {
341
373
  console.error('Failed to refresh settings:', err);
342
374
  }
@@ -486,7 +518,7 @@ function App() {
486
518
 
487
519
  {/* Settings button */}
488
520
  <button
489
- onClick={openSettings}
521
+ onClick={() => openSettings()}
490
522
  className="flex items-center gap-1.5 text-sm text-slate-500 hover:text-slate-800 transition-colors"
491
523
  title="Project settings"
492
524
  >
@@ -548,14 +580,7 @@ function App() {
548
580
 
549
581
  {/* Footer */}
550
582
  <footer className="bg-white border-t border-slate-200 py-4 text-center text-sm text-slate-500 flex-shrink-0">
551
- <a
552
- href="https://agilevibecoding.org"
553
- target="_blank"
554
- rel="noopener noreferrer"
555
- className="text-blue-600 hover:text-blue-700 hover:underline"
556
- >
557
- Agile Vibe Coding
558
- </a>
583
+ Agile Vibe Coding
559
584
  </footer>
560
585
 
561
586
  {/* Detail Modal */}
@@ -589,6 +614,9 @@ function App() {
589
614
  costLimitPending={costLimitPending?.runningType === 'sprint-planning' ? costLimitPending : null}
590
615
  onContinuePastCostLimit={async () => { try { await continuePastCostLimit(); setCostLimitPending(null); } catch (_) {} }}
591
616
  onCancelFromCostLimit={async () => { try { await cancelCeremony(); setCostLimitPending(null); } catch (_) {} }}
617
+ quotaLimitPending={quotaLimitPending?.runningType === 'sprint-planning' ? quotaLimitPending : null}
618
+ onContinueAfterQuota={async (newProvider, newModel) => { try { await continueAfterQuota(newProvider, newModel); setQuotaLimitPending(null); } catch (_) {} }}
619
+ onCancelFromQuota={async () => { try { await cancelCeremony(); setQuotaLimitPending(null); } catch (_) {} }}
592
620
  />
593
621
  )}
594
622
 
@@ -602,6 +630,7 @@ function App() {
602
630
  models={modelsSnapshot}
603
631
  onClose={() => setSettingsOpen(false)}
604
632
  onSaved={handleSettingsSaved}
633
+ initialTab={settingsInitialTab}
605
634
  />
606
635
  )}
607
636
 
@@ -12,6 +12,7 @@ const KEY_LABELS = {
12
12
  anthropic: 'Anthropic API Key (ANTHROPIC_API_KEY)',
13
13
  gemini: 'Google Gemini API Key (GEMINI_API_KEY)',
14
14
  openai: 'OpenAI API Key (OPENAI_API_KEY)',
15
+ xiaomi: 'Xiaomi MiMo API Key (XIAOMI_API_KEY)',
15
16
  };
16
17
 
17
18
  const COST_TIER_COLOR = {
@@ -106,8 +107,11 @@ export function AskArchPopup({ onUse, onClose, onOpenSettings }) {
106
107
  .then(([data, settings]) => {
107
108
  setModels(data);
108
109
  setApiKeyStatus(settings.apiKeys ?? {});
109
- const firstReady = data.find((m) => settings.apiKeys?.[normalizeProvider(m.provider)]?.isSet);
110
- setSelectedModelId(firstReady ? firstReady.modelId : (data.length > 0 ? data[0].modelId : ''));
110
+ // Prefer "pro" tier model for architecture generation
111
+ const ready = data.filter((m) => settings.apiKeys?.[normalizeProvider(m.provider)]?.isSet);
112
+ const isPro = (id) => /pro|opus|sonnet/i.test(id);
113
+ const best = ready.find((m) => isPro(m.modelId)) || ready[0];
114
+ setSelectedModelId(best ? best.modelId : (data.length > 0 ? data[0].modelId : ''));
111
115
  })
112
116
  .catch(() => setError('Failed to load available models.'));
113
117
  }, []);
@@ -264,7 +268,7 @@ export function AskArchPopup({ onUse, onClose, onOpenSettings }) {
264
268
  {onOpenSettings && (
265
269
  <button
266
270
  type="button"
267
- onClick={onOpenSettings}
271
+ onClick={() => onOpenSettings()}
268
272
  className="flex items-center gap-1 text-xs font-medium bg-slate-900 text-white px-2.5 py-1 rounded-md hover:bg-slate-700 transition-colors"
269
273
  >
270
274
  <SettingsIcon className="w-3 h-3" />
@@ -14,6 +14,7 @@ const KEY_LABELS = {
14
14
  anthropic: 'Anthropic API Key (ANTHROPIC_API_KEY)',
15
15
  gemini: 'Google Gemini API Key (GEMINI_API_KEY)',
16
16
  openai: 'OpenAI API Key (OPENAI_API_KEY)',
17
+ xiaomi: 'Xiaomi MiMo API Key (XIAOMI_API_KEY)',
17
18
  };
18
19
 
19
20
  /**
@@ -224,24 +225,36 @@ export function AskModelPopup({ onUse, onClose, onOpenSettings }) {
224
225
  .then(([data, settings]) => {
225
226
  setModels(data);
226
227
  setApiKeyStatus(settings.apiKeys ?? {});
227
- // Auto-select first model whose provider has an API key
228
- const firstReady = data.find((m) => settings.apiKeys?.[normalizeProvider(m.provider)]?.isSet);
229
- const generatorId = firstReady ? firstReady.modelId : (data.length > 0 ? data[0].modelId : '');
228
+ // Auto-select best models: prefer "pro" tier for generator, "flash/lite" for validator
229
+ const ready = data.filter((m) => settings.apiKeys?.[normalizeProvider(m.provider)]?.isSet);
230
+ const isPro = (id) => /pro|opus|sonnet/i.test(id);
231
+ const isLite = (id) => /flash|lite|haiku|mini/i.test(id);
232
+ const generator = ready.find((m) => isPro(m.modelId)) || ready[0];
233
+ const generatorId = generator ? generator.modelId : (data.length > 0 ? data[0].modelId : '');
230
234
  setSelectedModelId(generatorId);
231
- const otherReady = data.find(
232
- (m) => settings.apiKeys?.[normalizeProvider(m.provider)]?.isSet && m.modelId !== generatorId
233
- );
234
- setSelectedValidatorModelId(otherReady ? otherReady.modelId : generatorId);
235
+ const validator = ready.find((m) => isLite(m.modelId) && m.modelId !== generatorId)
236
+ || ready.find((m) => m.modelId !== generatorId)
237
+ || generator;
238
+ setSelectedValidatorModelId(validator ? validator.modelId : generatorId);
235
239
  })
236
240
  .catch(() => setError('Failed to load available models.'));
237
241
  }, []);
238
242
 
239
243
  function recheckKeys() {
240
- getSettings()
241
- .then((s) => setApiKeyStatus(s.apiKeys ?? {}))
244
+ Promise.all([getSettings(), getModels()])
245
+ .then(([s, updatedModels]) => {
246
+ setApiKeyStatus(s.apiKeys ?? {});
247
+ setModels(updatedModels);
248
+ })
242
249
  .catch(() => {});
243
250
  }
244
251
 
252
+ // Auto-recheck when settings are saved from anywhere in the app
253
+ useEffect(() => {
254
+ document.addEventListener('avc:settings-saved', recheckKeys);
255
+ return () => document.removeEventListener('avc:settings-saved', recheckKeys);
256
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
257
+
245
258
  const selectedModel = models.find((m) => m.modelId === selectedModelId);
246
259
  const selectedValidatorModel = models.find((m) => m.modelId === selectedValidatorModelId);
247
260
 
@@ -428,7 +441,7 @@ export function AskModelPopup({ onUse, onClose, onOpenSettings }) {
428
441
  {onOpenSettings && (
429
442
  <button
430
443
  type="button"
431
- onClick={onOpenSettings}
444
+ onClick={() => onOpenSettings()}
432
445
  className="flex items-center gap-1 text-xs font-medium bg-slate-900 text-white px-2.5 py-1 rounded-md hover:bg-slate-700 transition-colors"
433
446
  >
434
447
  <SettingsIcon className="w-3 h-3" />