@agile-vibe-coding/avc 0.1.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/cli/agent-loader.js +21 -0
  2. package/cli/agents/agent-selector.md +129 -0
  3. package/cli/agents/architecture-recommender.md +418 -0
  4. package/cli/agents/database-deep-dive.md +470 -0
  5. package/cli/agents/database-recommender.md +634 -0
  6. package/cli/agents/doc-distributor.md +176 -0
  7. package/cli/agents/documentation-updater.md +203 -0
  8. package/cli/agents/epic-story-decomposer.md +280 -0
  9. package/cli/agents/feature-context-generator.md +91 -0
  10. package/cli/agents/gap-checker-epic.md +52 -0
  11. package/cli/agents/impact-checker-story.md +51 -0
  12. package/cli/agents/migration-guide-generator.md +305 -0
  13. package/cli/agents/mission-scope-generator.md +79 -0
  14. package/cli/agents/mission-scope-validator.md +112 -0
  15. package/cli/agents/project-context-extractor.md +107 -0
  16. package/cli/agents/project-documentation-creator.json +226 -0
  17. package/cli/agents/project-documentation-creator.md +595 -0
  18. package/cli/agents/question-prefiller.md +269 -0
  19. package/cli/agents/refiner-epic.md +39 -0
  20. package/cli/agents/refiner-story.md +42 -0
  21. package/cli/agents/solver-epic-api.json +15 -0
  22. package/cli/agents/solver-epic-api.md +39 -0
  23. package/cli/agents/solver-epic-backend.json +15 -0
  24. package/cli/agents/solver-epic-backend.md +39 -0
  25. package/cli/agents/solver-epic-cloud.json +15 -0
  26. package/cli/agents/solver-epic-cloud.md +39 -0
  27. package/cli/agents/solver-epic-data.json +15 -0
  28. package/cli/agents/solver-epic-data.md +39 -0
  29. package/cli/agents/solver-epic-database.json +15 -0
  30. package/cli/agents/solver-epic-database.md +39 -0
  31. package/cli/agents/solver-epic-developer.json +15 -0
  32. package/cli/agents/solver-epic-developer.md +39 -0
  33. package/cli/agents/solver-epic-devops.json +15 -0
  34. package/cli/agents/solver-epic-devops.md +39 -0
  35. package/cli/agents/solver-epic-frontend.json +15 -0
  36. package/cli/agents/solver-epic-frontend.md +39 -0
  37. package/cli/agents/solver-epic-mobile.json +15 -0
  38. package/cli/agents/solver-epic-mobile.md +39 -0
  39. package/cli/agents/solver-epic-qa.json +15 -0
  40. package/cli/agents/solver-epic-qa.md +39 -0
  41. package/cli/agents/solver-epic-security.json +15 -0
  42. package/cli/agents/solver-epic-security.md +39 -0
  43. package/cli/agents/solver-epic-solution-architect.json +15 -0
  44. package/cli/agents/solver-epic-solution-architect.md +39 -0
  45. package/cli/agents/solver-epic-test-architect.json +15 -0
  46. package/cli/agents/solver-epic-test-architect.md +39 -0
  47. package/cli/agents/solver-epic-ui.json +15 -0
  48. package/cli/agents/solver-epic-ui.md +39 -0
  49. package/cli/agents/solver-epic-ux.json +15 -0
  50. package/cli/agents/solver-epic-ux.md +39 -0
  51. package/cli/agents/solver-story-api.json +15 -0
  52. package/cli/agents/solver-story-api.md +39 -0
  53. package/cli/agents/solver-story-backend.json +15 -0
  54. package/cli/agents/solver-story-backend.md +39 -0
  55. package/cli/agents/solver-story-cloud.json +15 -0
  56. package/cli/agents/solver-story-cloud.md +39 -0
  57. package/cli/agents/solver-story-data.json +15 -0
  58. package/cli/agents/solver-story-data.md +39 -0
  59. package/cli/agents/solver-story-database.json +15 -0
  60. package/cli/agents/solver-story-database.md +39 -0
  61. package/cli/agents/solver-story-developer.json +15 -0
  62. package/cli/agents/solver-story-developer.md +39 -0
  63. package/cli/agents/solver-story-devops.json +15 -0
  64. package/cli/agents/solver-story-devops.md +39 -0
  65. package/cli/agents/solver-story-frontend.json +15 -0
  66. package/cli/agents/solver-story-frontend.md +39 -0
  67. package/cli/agents/solver-story-mobile.json +15 -0
  68. package/cli/agents/solver-story-mobile.md +39 -0
  69. package/cli/agents/solver-story-qa.json +15 -0
  70. package/cli/agents/solver-story-qa.md +39 -0
  71. package/cli/agents/solver-story-security.json +15 -0
  72. package/cli/agents/solver-story-security.md +39 -0
  73. package/cli/agents/solver-story-solution-architect.json +15 -0
  74. package/cli/agents/solver-story-solution-architect.md +39 -0
  75. package/cli/agents/solver-story-test-architect.json +15 -0
  76. package/cli/agents/solver-story-test-architect.md +39 -0
  77. package/cli/agents/solver-story-ui.json +15 -0
  78. package/cli/agents/solver-story-ui.md +39 -0
  79. package/cli/agents/solver-story-ux.json +15 -0
  80. package/cli/agents/solver-story-ux.md +39 -0
  81. package/cli/agents/story-doc-enricher.md +133 -0
  82. package/cli/agents/suggestion-business-analyst.md +88 -0
  83. package/cli/agents/suggestion-deployment-architect.md +263 -0
  84. package/cli/agents/suggestion-product-manager.md +129 -0
  85. package/cli/agents/suggestion-security-specialist.md +156 -0
  86. package/cli/agents/suggestion-technical-architect.md +269 -0
  87. package/cli/agents/suggestion-ux-researcher.md +93 -0
  88. package/cli/agents/task-subtask-decomposer.md +188 -0
  89. package/cli/agents/validator-documentation.json +152 -0
  90. package/cli/agents/validator-documentation.md +453 -0
  91. package/cli/agents/validator-epic-api.json +93 -0
  92. package/cli/agents/validator-epic-api.md +137 -0
  93. package/cli/agents/validator-epic-backend.json +93 -0
  94. package/cli/agents/validator-epic-backend.md +130 -0
  95. package/cli/agents/validator-epic-cloud.json +93 -0
  96. package/cli/agents/validator-epic-cloud.md +137 -0
  97. package/cli/agents/validator-epic-data.json +93 -0
  98. package/cli/agents/validator-epic-data.md +130 -0
  99. package/cli/agents/validator-epic-database.json +93 -0
  100. package/cli/agents/validator-epic-database.md +137 -0
  101. package/cli/agents/validator-epic-developer.json +74 -0
  102. package/cli/agents/validator-epic-developer.md +153 -0
  103. package/cli/agents/validator-epic-devops.json +74 -0
  104. package/cli/agents/validator-epic-devops.md +153 -0
  105. package/cli/agents/validator-epic-frontend.json +74 -0
  106. package/cli/agents/validator-epic-frontend.md +153 -0
  107. package/cli/agents/validator-epic-mobile.json +93 -0
  108. package/cli/agents/validator-epic-mobile.md +130 -0
  109. package/cli/agents/validator-epic-qa.json +93 -0
  110. package/cli/agents/validator-epic-qa.md +130 -0
  111. package/cli/agents/validator-epic-security.json +74 -0
  112. package/cli/agents/validator-epic-security.md +154 -0
  113. package/cli/agents/validator-epic-solution-architect.json +74 -0
  114. package/cli/agents/validator-epic-solution-architect.md +156 -0
  115. package/cli/agents/validator-epic-test-architect.json +93 -0
  116. package/cli/agents/validator-epic-test-architect.md +130 -0
  117. package/cli/agents/validator-epic-ui.json +93 -0
  118. package/cli/agents/validator-epic-ui.md +130 -0
  119. package/cli/agents/validator-epic-ux.json +93 -0
  120. package/cli/agents/validator-epic-ux.md +130 -0
  121. package/cli/agents/validator-selector.md +211 -0
  122. package/cli/agents/validator-story-api.json +104 -0
  123. package/cli/agents/validator-story-api.md +152 -0
  124. package/cli/agents/validator-story-backend.json +104 -0
  125. package/cli/agents/validator-story-backend.md +152 -0
  126. package/cli/agents/validator-story-cloud.json +104 -0
  127. package/cli/agents/validator-story-cloud.md +152 -0
  128. package/cli/agents/validator-story-data.json +104 -0
  129. package/cli/agents/validator-story-data.md +152 -0
  130. package/cli/agents/validator-story-database.json +104 -0
  131. package/cli/agents/validator-story-database.md +152 -0
  132. package/cli/agents/validator-story-developer.json +104 -0
  133. package/cli/agents/validator-story-developer.md +152 -0
  134. package/cli/agents/validator-story-devops.json +104 -0
  135. package/cli/agents/validator-story-devops.md +152 -0
  136. package/cli/agents/validator-story-frontend.json +104 -0
  137. package/cli/agents/validator-story-frontend.md +152 -0
  138. package/cli/agents/validator-story-mobile.json +104 -0
  139. package/cli/agents/validator-story-mobile.md +152 -0
  140. package/cli/agents/validator-story-qa.json +104 -0
  141. package/cli/agents/validator-story-qa.md +152 -0
  142. package/cli/agents/validator-story-security.json +104 -0
  143. package/cli/agents/validator-story-security.md +152 -0
  144. package/cli/agents/validator-story-solution-architect.json +104 -0
  145. package/cli/agents/validator-story-solution-architect.md +152 -0
  146. package/cli/agents/validator-story-test-architect.json +104 -0
  147. package/cli/agents/validator-story-test-architect.md +152 -0
  148. package/cli/agents/validator-story-ui.json +104 -0
  149. package/cli/agents/validator-story-ui.md +152 -0
  150. package/cli/agents/validator-story-ux.json +104 -0
  151. package/cli/agents/validator-story-ux.md +152 -0
  152. package/cli/ansi-colors.js +21 -0
  153. package/cli/build-docs.js +29 -8
  154. package/cli/ceremony-history.js +369 -0
  155. package/cli/command-logger.js +49 -12
  156. package/cli/components/static-output.js +63 -0
  157. package/cli/console-output-manager.js +94 -0
  158. package/cli/docs-sync.js +306 -0
  159. package/cli/epic-story-validator.js +1174 -0
  160. package/cli/evaluation-prompts.js +1008 -0
  161. package/cli/execution-context.js +195 -0
  162. package/cli/generate-summary-table.js +340 -0
  163. package/cli/index.js +0 -0
  164. package/cli/init-model-config.js +697 -0
  165. package/cli/init.js +1311 -274
  166. package/cli/kanban-server-manager.js +228 -0
  167. package/cli/llm-claude.js +83 -1
  168. package/cli/llm-gemini.js +85 -0
  169. package/cli/llm-mock.js +233 -0
  170. package/cli/llm-openai.js +233 -0
  171. package/cli/llm-provider.js +240 -3
  172. package/cli/llm-token-limits.js +102 -0
  173. package/cli/llm-verifier.js +454 -0
  174. package/cli/message-constants.js +58 -0
  175. package/cli/message-manager.js +334 -0
  176. package/cli/message-types.js +96 -0
  177. package/cli/messaging-api.js +297 -0
  178. package/cli/model-pricing.js +169 -0
  179. package/cli/model-query-engine.js +468 -0
  180. package/cli/model-recommendation-analyzer.js +495 -0
  181. package/cli/model-selector.js +269 -0
  182. package/cli/output-buffer.js +107 -0
  183. package/cli/process-manager.js +73 -2
  184. package/cli/repl-ink.js +4988 -1217
  185. package/cli/repl-old.js +4 -4
  186. package/cli/seed-processor.js +792 -0
  187. package/cli/sprint-planning-processor.js +1813 -0
  188. package/cli/template-processor.js +2102 -105
  189. package/cli/templates/project.md +25 -8
  190. package/cli/templates/vitepress-config.mts.template +5 -4
  191. package/cli/token-tracker.js +520 -0
  192. package/cli/tools/generate-story-validators.js +317 -0
  193. package/cli/tools/generate-validators.js +669 -0
  194. package/cli/update-checker.js +19 -17
  195. package/cli/update-notifier.js +4 -4
  196. package/cli/validation-router.js +605 -0
  197. package/cli/verification-tracker.js +563 -0
  198. package/kanban/README.md +386 -0
  199. package/kanban/client/README.md +205 -0
  200. package/kanban/client/components.json +20 -0
  201. package/kanban/client/dist/assets/index-CiD8PS2e.js +306 -0
  202. package/kanban/client/dist/assets/index-nLh0m82Q.css +1 -0
  203. package/kanban/client/dist/index.html +16 -0
  204. package/kanban/client/dist/vite.svg +1 -0
  205. package/kanban/client/index.html +15 -0
  206. package/kanban/client/package-lock.json +9442 -0
  207. package/kanban/client/package.json +44 -0
  208. package/kanban/client/postcss.config.js +6 -0
  209. package/kanban/client/public/vite.svg +1 -0
  210. package/kanban/client/src/App.jsx +622 -0
  211. package/kanban/client/src/components/ProjectFileEditorPopup.jsx +117 -0
  212. package/kanban/client/src/components/ceremony/AskArchPopup.jsx +416 -0
  213. package/kanban/client/src/components/ceremony/AskModelPopup.jsx +616 -0
  214. package/kanban/client/src/components/ceremony/CeremonyWorkflowModal.jsx +946 -0
  215. package/kanban/client/src/components/ceremony/EpicStorySelectionModal.jsx +254 -0
  216. package/kanban/client/src/components/ceremony/SponsorCallModal.jsx +619 -0
  217. package/kanban/client/src/components/ceremony/SprintPlanningModal.jsx +704 -0
  218. package/kanban/client/src/components/ceremony/steps/ArchitectureStep.jsx +150 -0
  219. package/kanban/client/src/components/ceremony/steps/CompleteStep.jsx +154 -0
  220. package/kanban/client/src/components/ceremony/steps/DatabaseStep.jsx +202 -0
  221. package/kanban/client/src/components/ceremony/steps/DeploymentStep.jsx +123 -0
  222. package/kanban/client/src/components/ceremony/steps/MissionStep.jsx +106 -0
  223. package/kanban/client/src/components/ceremony/steps/ReviewAnswersStep.jsx +125 -0
  224. package/kanban/client/src/components/ceremony/steps/RunningStep.jsx +228 -0
  225. package/kanban/client/src/components/kanban/CardDetailModal.jsx +559 -0
  226. package/kanban/client/src/components/kanban/EpicSection.jsx +146 -0
  227. package/kanban/client/src/components/kanban/FilterToolbar.jsx +222 -0
  228. package/kanban/client/src/components/kanban/GroupingSelector.jsx +57 -0
  229. package/kanban/client/src/components/kanban/KanbanBoard.jsx +211 -0
  230. package/kanban/client/src/components/kanban/KanbanCard.jsx +138 -0
  231. package/kanban/client/src/components/kanban/KanbanColumn.jsx +90 -0
  232. package/kanban/client/src/components/kanban/RefineWorkItemPopup.jsx +789 -0
  233. package/kanban/client/src/components/layout/LoadingScreen.jsx +82 -0
  234. package/kanban/client/src/components/process/ProcessMonitorBar.jsx +80 -0
  235. package/kanban/client/src/components/settings/AgentEditorPopup.jsx +171 -0
  236. package/kanban/client/src/components/settings/AgentsTab.jsx +353 -0
  237. package/kanban/client/src/components/settings/ApiKeysTab.jsx +113 -0
  238. package/kanban/client/src/components/settings/CeremonyModelsTab.jsx +98 -0
  239. package/kanban/client/src/components/settings/CostThresholdsTab.jsx +94 -0
  240. package/kanban/client/src/components/settings/ModelPricingTab.jsx +204 -0
  241. package/kanban/client/src/components/settings/ServersTab.jsx +121 -0
  242. package/kanban/client/src/components/settings/SettingsModal.jsx +84 -0
  243. package/kanban/client/src/components/stats/CostModal.jsx +353 -0
  244. package/kanban/client/src/components/ui/badge.jsx +27 -0
  245. package/kanban/client/src/components/ui/dialog.jsx +121 -0
  246. package/kanban/client/src/components/ui/tabs.jsx +85 -0
  247. package/kanban/client/src/hooks/__tests__/useGrouping.test.js +232 -0
  248. package/kanban/client/src/hooks/useGrouping.js +118 -0
  249. package/kanban/client/src/hooks/useWebSocket.js +120 -0
  250. package/kanban/client/src/lib/__tests__/api.test.js +196 -0
  251. package/kanban/client/src/lib/__tests__/status-grouping.test.js +94 -0
  252. package/kanban/client/src/lib/api.js +401 -0
  253. package/kanban/client/src/lib/status-grouping.js +144 -0
  254. package/kanban/client/src/lib/utils.js +11 -0
  255. package/kanban/client/src/main.jsx +10 -0
  256. package/kanban/client/src/store/__tests__/kanbanStore.test.js +164 -0
  257. package/kanban/client/src/store/ceremonyStore.js +172 -0
  258. package/kanban/client/src/store/filterStore.js +201 -0
  259. package/kanban/client/src/store/kanbanStore.js +115 -0
  260. package/kanban/client/src/store/processStore.js +65 -0
  261. package/kanban/client/src/store/sprintPlanningStore.js +33 -0
  262. package/kanban/client/src/styles/globals.css +59 -0
  263. package/kanban/client/tailwind.config.js +77 -0
  264. package/kanban/client/vite.config.js +28 -0
  265. package/kanban/client/vitest.config.js +28 -0
  266. package/kanban/dev-start.sh +47 -0
  267. package/kanban/package.json +12 -0
  268. package/kanban/server/index.js +516 -0
  269. package/kanban/server/routes/ceremony.js +305 -0
  270. package/kanban/server/routes/costs.js +157 -0
  271. package/kanban/server/routes/processes.js +50 -0
  272. package/kanban/server/routes/settings.js +303 -0
  273. package/kanban/server/routes/websocket.js +276 -0
  274. package/kanban/server/routes/work-items.js +347 -0
  275. package/kanban/server/services/CeremonyService.js +1190 -0
  276. package/kanban/server/services/FileSystemScanner.js +95 -0
  277. package/kanban/server/services/FileWatcher.js +144 -0
  278. package/kanban/server/services/HierarchyBuilder.js +196 -0
  279. package/kanban/server/services/ProcessRegistry.js +122 -0
  280. package/kanban/server/services/WorkItemReader.js +123 -0
  281. package/kanban/server/services/WorkItemRefineService.js +510 -0
  282. package/kanban/server/start.js +49 -0
  283. package/kanban/server/utils/kanban-logger.js +132 -0
  284. package/kanban/server/utils/markdown.js +91 -0
  285. package/kanban/server/utils/status-grouping.js +107 -0
  286. package/kanban/server/workers/sponsor-call-worker.js +84 -0
  287. package/kanban/server/workers/sprint-planning-worker.js +130 -0
  288. package/package.json +18 -5
  289. package/cli/agents/documentation.md +0 -302
@@ -0,0 +1,697 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import dotenv from 'dotenv';
4
+ import { getModelPricing, calculateCost, formatCost, getEstimatedTokens } from './model-pricing.js';
5
+
6
+ /**
7
+ * Handles interactive model configuration during project initialization.
8
+ * Allows users to select which LLM models to use for ceremonies and stages.
9
+ */
10
+ export class ModelConfigurator {
11
+ constructor(projectRoot) {
12
+ this.projectRoot = projectRoot;
13
+ this.avcConfigPath = path.join(projectRoot, '.avc', 'avc.json');
14
+ this.envPath = path.join(projectRoot, '.env');
15
+ this.availableProviders = [];
16
+ this.config = null;
17
+ }
18
+
19
+ /**
20
+ * Detect which API keys are available in .env file.
21
+ * Returns array of provider names (informational - used for UI indicators).
22
+ * @returns {string[]} Array of provider names: ['claude', 'gemini', 'openai']
23
+ */
24
+ detectAvailableProviders() {
25
+ // Load .env file if it exists
26
+ if (fs.existsSync(this.envPath)) {
27
+ dotenv.config({ path: this.envPath });
28
+ }
29
+
30
+ const providers = [];
31
+
32
+ // Check for each provider's API key
33
+ if (process.env.ANTHROPIC_API_KEY) {
34
+ providers.push('claude');
35
+ }
36
+ if (process.env.GEMINI_API_KEY) {
37
+ providers.push('gemini');
38
+ }
39
+ if (process.env.OPENAI_API_KEY) {
40
+ providers.push('openai');
41
+ }
42
+
43
+ return providers;
44
+ }
45
+
46
+ /**
47
+ * Read current ceremony configuration from avc.json.
48
+ * @returns {Object} Parsed configuration object
49
+ */
50
+ readConfig() {
51
+ if (!fs.existsSync(this.avcConfigPath)) {
52
+ throw new Error('avc.json not found. Run /init first.');
53
+ }
54
+
55
+ const raw = fs.readFileSync(this.avcConfigPath, 'utf8');
56
+ this.config = JSON.parse(raw);
57
+ return this.config;
58
+ }
59
+
60
+ /**
61
+ * Get all ceremonies with their stage configurations.
62
+ * @returns {Array} Array of ceremony objects with provider/model info
63
+ */
64
+ getCeremonies() {
65
+ if (!this.config) {
66
+ this.readConfig();
67
+ }
68
+
69
+ return this.config.settings.ceremonies.map(ceremony => {
70
+ // Build stages object with all available stages for this ceremony
71
+ const stages = {};
72
+
73
+ // Define available stages per ceremony (excluding 'main' and 'validation' which are handled separately)
74
+ const ceremonyStages = {
75
+ 'sponsor-call': ['suggestions', 'architecture-recommendation', 'question-prefilling', 'documentation'],
76
+ 'sprint-planning': ['decomposition', 'doc-distribution', 'validation'],
77
+ 'seed': ['decomposition', 'validation', 'context-generation'],
78
+ 'context-retrospective': ['documentation-update', 'context-refinement']
79
+ };
80
+
81
+ const availableStages = ceremonyStages[ceremony.name] || [];
82
+
83
+ // Build stages object with effective provider/model for each stage
84
+ availableStages.forEach(stageName => {
85
+ const stageConfig = ceremony.stages?.[stageName];
86
+
87
+ if (stageConfig) {
88
+ // Stage is explicitly configured
89
+ stages[stageName] = {
90
+ provider: stageConfig.provider,
91
+ model: stageConfig.model
92
+ };
93
+ } else {
94
+ // Stage not configured, use ceremony default
95
+ stages[stageName] = {
96
+ provider: ceremony.provider,
97
+ model: ceremony.defaultModel
98
+ };
99
+ }
100
+ });
101
+
102
+ return {
103
+ name: ceremony.name,
104
+ mainProvider: ceremony.provider,
105
+ mainModel: ceremony.defaultModel,
106
+ validationProvider: ceremony.validation?.provider,
107
+ validationModel: ceremony.validation?.model,
108
+ stages
109
+ };
110
+ });
111
+ }
112
+
113
+ /**
114
+ * Get descriptive name for a stage based on its type and ceremony context.
115
+ * @param {string} stageName - The stage identifier (e.g., 'suggestions', 'documentation')
116
+ * @param {string} ceremonyName - The ceremony name for context
117
+ * @returns {string} Descriptive stage name
118
+ */
119
+ _getStageDisplayName(stageName, ceremonyName) {
120
+ // Context-aware stage descriptions that explain what each stage does
121
+ const ceremonyStageDescriptions = {
122
+ 'sponsor-call': {
123
+ 'suggestions': 'Questionnaire Suggestions - AI analyzes project name and suggests answers',
124
+ 'architecture-recommendation': 'Architecture Recommendation - AI suggests 3-5 deployment architectures based on project scope',
125
+ 'question-prefilling': 'Question Pre-filling - AI generates intelligent answers based on selected architecture',
126
+ 'documentation': 'Documentation Generation - AI creates initial project documentation'
127
+ },
128
+ 'sprint-planning': {
129
+ 'decomposition': 'Epic & Story Decomposition - AI breaks down project scope into epics and stories',
130
+ 'validation': 'Multi-Agent Validation - Domain experts validate each epic and story',
131
+ 'doc-distribution': 'Documentation Distribution - AI moves and elaborates content from parent doc.md to each epic and story'
132
+ },
133
+ 'seed': {
134
+ 'decomposition': 'Task Decomposition - AI breaks down stories into tasks and subtasks',
135
+ 'validation': 'Task Validation - AI validates task hierarchy and completeness',
136
+ 'context-generation': 'Task Documentation - AI creates doc.md for each task'
137
+ },
138
+ 'context-retrospective': {
139
+ 'documentation-update': 'Documentation Enhancement - AI refines and improves project documentation',
140
+ 'context-refinement': 'Documentation Enhancement - AI enriches doc.md files with learned insights'
141
+ }
142
+ };
143
+
144
+ // Try ceremony-specific description first, then fallback to generic
145
+ const ceremonyDescriptions = ceremonyStageDescriptions[ceremonyName];
146
+ if (ceremonyDescriptions && ceremonyDescriptions[stageName]) {
147
+ return ceremonyDescriptions[stageName];
148
+ }
149
+
150
+ // Generic fallback descriptions
151
+ const genericDescriptions = {
152
+ suggestions: 'AI-Assisted Questionnaire',
153
+ documentation: 'Project Documentation Creation',
154
+ decomposition: 'Work Item Decomposition',
155
+ 'context-generation': 'Task Documentation Generation',
156
+ 'doc-distribution': 'Documentation Distribution',
157
+ 'documentation-update': 'Documentation Refinement',
158
+ 'context-refinement': 'Context Enhancement',
159
+ enhancement: 'Content Enhancement'
160
+ };
161
+
162
+ return genericDescriptions[stageName] || `${stageName.charAt(0).toUpperCase()}${stageName.slice(1)}`;
163
+ }
164
+
165
+ /**
166
+ * Get all stages for a specific ceremony.
167
+ * @param {string} ceremonyName - Name of the ceremony
168
+ * @returns {Array} Array of stage objects with id, name, provider, model, hasValidationTypes
169
+ */
170
+ getStagesForCeremony(ceremonyName) {
171
+ if (!this.config) {
172
+ this.readConfig();
173
+ }
174
+
175
+ const ceremony = this.config.settings.ceremonies.find(c => c.name === ceremonyName);
176
+ if (!ceremony) {
177
+ return [];
178
+ }
179
+
180
+ // Determine main stage description based on ceremony type
181
+ let mainStageName = 'Primary Execution';
182
+ if (ceremonyName === 'sponsor-call') {
183
+ mainStageName = 'Project Definition & Planning';
184
+ } else if (ceremonyName === 'sprint-planning') {
185
+ mainStageName = 'Epic & Story Expansion';
186
+ } else if (ceremonyName === 'context-retrospective') {
187
+ mainStageName = 'Context & Documentation Review';
188
+ } else if (ceremonyName === 'seed') {
189
+ mainStageName = 'Task & Subtask Planning';
190
+ }
191
+
192
+ const stages = [
193
+ {
194
+ id: 'main',
195
+ name: mainStageName,
196
+ provider: ceremony.provider,
197
+ model: ceremony.defaultModel,
198
+ hasValidationTypes: false
199
+ }
200
+ ];
201
+
202
+ // Add validation + refinement stages if configured
203
+ if (ceremony.validation) {
204
+ stages.push({
205
+ id: 'validation',
206
+ name: 'Quality Validator - Scores and identifies issues in generated output',
207
+ provider: ceremony.validation.provider || ceremony.provider,
208
+ model: ceremony.validation.model || ceremony.defaultModel,
209
+ hasValidationTypes: false
210
+ });
211
+ stages.push({
212
+ id: 'refinement',
213
+ name: 'Quality Refiner - Improves output based on validator feedback',
214
+ provider: ceremony.validation.refinement?.provider || ceremony.validation.provider || ceremony.provider,
215
+ model: ceremony.validation.refinement?.model || ceremony.validation.model || ceremony.defaultModel,
216
+ hasValidationTypes: false
217
+ });
218
+ }
219
+
220
+ // Define available stages per ceremony (same as getCeremonies)
221
+ const ceremonyStages = {
222
+ 'sponsor-call': ['suggestions', 'documentation'],
223
+ 'sprint-planning': ['decomposition', 'doc-distribution', 'validation'],
224
+ 'seed': ['decomposition', 'validation', 'context-generation'],
225
+ 'context-retrospective': ['documentation-update', 'context-refinement']
226
+ };
227
+
228
+ const availableStages = ceremonyStages[ceremonyName] || [];
229
+
230
+ // Add all available stages with effective provider/model
231
+ availableStages.forEach(stageName => {
232
+ const stageConfig = ceremony.stages?.[stageName];
233
+
234
+ // Check if this is the validation stage for sprint-planning
235
+ const hasValidationTypes = (ceremonyName === 'sprint-planning' && stageName === 'validation');
236
+
237
+ stages.push({
238
+ id: `stage-${stageName}`,
239
+ name: this._getStageDisplayName(stageName, ceremonyName),
240
+ provider: stageConfig?.provider || ceremony.provider,
241
+ model: stageConfig?.model || ceremony.defaultModel,
242
+ hasValidationTypes,
243
+ stageName // Store original stage name for validation type lookup
244
+ });
245
+ });
246
+
247
+ return stages;
248
+ }
249
+
250
+ /**
251
+ * Get validation types for sprint-planning validation stage
252
+ * @returns {Array} Array of validation type objects
253
+ */
254
+ getValidationTypes() {
255
+ return [
256
+ {
257
+ id: 'universal',
258
+ name: 'Universal Validators',
259
+ description: 'Always-applied (solution-architect, security, developer, qa, test-architect)'
260
+ },
261
+ {
262
+ id: 'domain',
263
+ name: 'Domain Validators',
264
+ description: 'Domain-specific (devops, database, frontend, api, backend, cloud, mobile, ui, ux, data)'
265
+ },
266
+ {
267
+ id: 'feature',
268
+ name: 'Feature Validators',
269
+ description: 'Inferred from acceptance criteria keywords'
270
+ },
271
+ {
272
+ id: 'default',
273
+ name: 'Default (All Validators)',
274
+ description: 'Fallback configuration for all validation types'
275
+ }
276
+ ];
277
+ }
278
+
279
+ /**
280
+ * Get current model configuration for a validation type
281
+ * @param {string} ceremonyName - Ceremony name (should be 'sprint-planning')
282
+ * @param {string} validationType - Validation type ID ('universal', 'domain', 'feature', 'default')
283
+ * @returns {Object|null} { provider, model } or null if not configured
284
+ */
285
+ getValidationTypeConfig(ceremonyName, validationType) {
286
+ if (!this.config) {
287
+ this.readConfig();
288
+ }
289
+
290
+ const ceremony = this.config.settings.ceremonies.find(c => c.name === ceremonyName);
291
+ if (!ceremony || !ceremony.stages || !ceremony.stages.validation) {
292
+ return null;
293
+ }
294
+
295
+ const validationStage = ceremony.stages.validation;
296
+ const validationTypeConfig = validationStage.validationTypes?.[validationType];
297
+
298
+ if (validationTypeConfig) {
299
+ return {
300
+ provider: validationTypeConfig.provider,
301
+ model: validationTypeConfig.model
302
+ };
303
+ }
304
+
305
+ // Return default validation stage config if validation type not configured
306
+ return {
307
+ provider: validationStage.provider || ceremony.provider,
308
+ model: validationStage.model || ceremony.defaultModel
309
+ };
310
+ }
311
+
312
+ /**
313
+ * Get available models, optionally filtered by provider.
314
+ * Shows ALL models from settings, regardless of API key availability.
315
+ * @param {string|null} providerFilter - Optional provider to filter by
316
+ * @returns {Array} Array of model objects with hasApiKey indicator
317
+ */
318
+ getAvailableModels(providerFilter = null) {
319
+ if (!this.config) {
320
+ this.readConfig();
321
+ }
322
+
323
+ const allModels = this.config.settings.models;
324
+ const filtered = [];
325
+
326
+ for (const [modelId, modelInfo] of Object.entries(allModels)) {
327
+ // Filter by provider if specified
328
+ if (providerFilter && modelInfo.provider !== providerFilter) {
329
+ continue;
330
+ }
331
+
332
+ // Add indicator if API key is available (informational only)
333
+ const hasApiKey = this.availableProviders.includes(modelInfo.provider);
334
+
335
+ filtered.push({
336
+ id: modelId,
337
+ displayName: modelInfo.displayName,
338
+ provider: modelInfo.provider,
339
+ pricing: modelInfo.pricing,
340
+ hasApiKey // User can still select models without keys
341
+ });
342
+ }
343
+
344
+ // Sort by provider, then by input price (high to low for quality indication)
345
+ return filtered.sort((a, b) => {
346
+ if (a.provider !== b.provider) {
347
+ return a.provider.localeCompare(b.provider);
348
+ }
349
+ return b.pricing.input - a.pricing.input;
350
+ });
351
+ }
352
+
353
+ /**
354
+ * Update stage configuration with new model.
355
+ * @param {string} ceremonyName - Name of the ceremony
356
+ * @param {string} stageId - ID of the stage (main, validation, stage-*)
357
+ * @param {string} newModel - New model ID to use
358
+ * @param {string|null} validationType - Optional validation type for sprint-planning validation stage
359
+ */
360
+ updateStage(ceremonyName, stageId, newModel, validationType = null) {
361
+ if (!this.config) {
362
+ this.readConfig();
363
+ }
364
+
365
+ const ceremony = this.config.settings.ceremonies.find(c => c.name === ceremonyName);
366
+ if (!ceremony) {
367
+ throw new Error(`Ceremony ${ceremonyName} not found`);
368
+ }
369
+
370
+ const modelInfo = this.config.settings.models[newModel];
371
+ if (!modelInfo) {
372
+ throw new Error(`Model ${newModel} not found`);
373
+ }
374
+
375
+ // Update the appropriate configuration section
376
+ if (stageId === 'main') {
377
+ ceremony.provider = modelInfo.provider;
378
+ ceremony.defaultModel = newModel;
379
+ } else if (stageId === 'validation') {
380
+ if (!ceremony.validation) {
381
+ ceremony.validation = { enabled: true };
382
+ }
383
+ ceremony.validation.provider = modelInfo.provider;
384
+ ceremony.validation.model = newModel;
385
+ } else if (stageId === 'refinement') {
386
+ if (!ceremony.validation) {
387
+ ceremony.validation = { enabled: true };
388
+ }
389
+ if (!ceremony.validation.refinement) {
390
+ ceremony.validation.refinement = {};
391
+ }
392
+ ceremony.validation.refinement.provider = modelInfo.provider;
393
+ ceremony.validation.refinement.model = newModel;
394
+ } else if (stageId.startsWith('stage-')) {
395
+ const stageName = stageId.replace('stage-', '');
396
+ if (!ceremony.stages) {
397
+ ceremony.stages = {};
398
+ }
399
+ if (!ceremony.stages[stageName]) {
400
+ ceremony.stages[stageName] = {};
401
+ }
402
+
403
+ // Handle validation type sub-configuration for sprint-planning validation stage
404
+ if (validationType && stageName === 'validation' && ceremonyName === 'sprint-planning') {
405
+ if (!ceremony.stages[stageName].validationTypes) {
406
+ ceremony.stages[stageName].validationTypes = {};
407
+ }
408
+ ceremony.stages[stageName].validationTypes[validationType] = {
409
+ provider: modelInfo.provider,
410
+ model: newModel
411
+ };
412
+ } else {
413
+ ceremony.stages[stageName].provider = modelInfo.provider;
414
+ ceremony.stages[stageName].model = newModel;
415
+ }
416
+ }
417
+ }
418
+
419
+ /**
420
+ * Save updated configuration to avc.json.
421
+ */
422
+ saveConfig() {
423
+ if (!this.config) {
424
+ throw new Error('No configuration loaded');
425
+ }
426
+
427
+ fs.writeFileSync(
428
+ this.avcConfigPath,
429
+ JSON.stringify(this.config, null, 2),
430
+ 'utf8'
431
+ );
432
+ }
433
+
434
+ /**
435
+ * Check if configuration has providers without API keys.
436
+ * Returns array of issues (informational warnings).
437
+ * @returns {Array} Array of issue objects with ceremony, stage, provider
438
+ */
439
+ validateConfig() {
440
+ if (!this.config) {
441
+ this.readConfig();
442
+ }
443
+
444
+ const issues = [];
445
+ const ceremonies = this.config.settings.ceremonies;
446
+
447
+ ceremonies.forEach(ceremony => {
448
+ // Check main provider
449
+ if (!this.availableProviders.includes(ceremony.provider)) {
450
+ issues.push({
451
+ ceremony: ceremony.name,
452
+ stage: 'main',
453
+ provider: ceremony.provider
454
+ });
455
+ }
456
+
457
+ // Check validation provider
458
+ if (ceremony.validation && !this.availableProviders.includes(ceremony.validation.provider)) {
459
+ issues.push({
460
+ ceremony: ceremony.name,
461
+ stage: 'validation',
462
+ provider: ceremony.validation.provider
463
+ });
464
+ }
465
+
466
+ // Check stage-specific providers
467
+ if (ceremony.stages) {
468
+ Object.keys(ceremony.stages).forEach(stageName => {
469
+ const stage = ceremony.stages[stageName];
470
+ if (stage.provider && !this.availableProviders.includes(stage.provider)) {
471
+ issues.push({
472
+ ceremony: ceremony.name,
473
+ stage: stageName,
474
+ provider: stage.provider
475
+ });
476
+ }
477
+ });
478
+ }
479
+ });
480
+
481
+ return issues;
482
+ }
483
+
484
+ /**
485
+ * Get complete configuration overview with cost estimates for a ceremony
486
+ * @param {string} ceremonyName - Name of ceremony (e.g., 'sprint-planning')
487
+ * @returns {Object} Configuration overview with costs
488
+ */
489
+ getConfigurationOverview(ceremonyName) {
490
+ if (!this.config) {
491
+ this.readConfig();
492
+ }
493
+
494
+ const ceremony = this.config.settings.ceremonies.find(c => c.name === ceremonyName);
495
+ if (!ceremony) {
496
+ throw new Error(`Ceremony '${ceremonyName}' not found in configuration`);
497
+ }
498
+
499
+ // Get ceremony-specific main label
500
+ const mainLabels = {
501
+ 'sponsor-call': 'Default Fallback Model - Used when specific stages are not configured',
502
+ 'sprint-planning': 'Default Fallback Model - Used when specific stages are not configured',
503
+ 'seed': 'Default Fallback Model - Used when specific stages are not configured',
504
+ 'context-retrospective': 'Default Fallback Model - Used when specific stages are not configured'
505
+ };
506
+
507
+ const overview = {
508
+ ceremony: ceremonyName,
509
+ main: {
510
+ label: mainLabels[ceremonyName] || 'Default Fallback Model',
511
+ provider: ceremony.provider || 'claude',
512
+ model: ceremony.defaultModel || 'claude-sonnet-4-5-20250929',
513
+ calls: 0,
514
+ cost: 0,
515
+ formattedCost: 'Free',
516
+ isDefault: true,
517
+ level: 0
518
+ },
519
+ stages: [],
520
+ validationTypes: null,
521
+ totalCost: 0,
522
+ totalCalls: 0
523
+ };
524
+
525
+ // Add stages
526
+ const stages = this.getStagesForCeremony(ceremonyName);
527
+ stages.forEach(stage => {
528
+ // Skip 'main' stage as it's already in overview.main
529
+ if (stage.id === 'main') {
530
+ return;
531
+ }
532
+
533
+ if (stage.id.startsWith('stage-') && stage.stageName === 'validation' && ceremonyName === 'sprint-planning') {
534
+ // Handle validation stage specially for sprint-planning
535
+ overview.stages.push(this._getValidationStageOverview(ceremony, stage));
536
+ } else {
537
+ const stageProvider = stage.provider || ceremony.provider;
538
+ const stageModel = stage.model || ceremony.defaultModel;
539
+
540
+ // Use stageName (without 'stage-' prefix) for lookups, fallback to id
541
+ const lookupName = stage.stageName || stage.id.replace('stage-', '');
542
+ const tokens = getEstimatedTokens(ceremonyName, lookupName);
543
+ const calls = this._getCallCount(ceremonyName, lookupName);
544
+ const cost = calculateCost(stageModel, tokens);
545
+
546
+ overview.stages.push({
547
+ id: stage.id,
548
+ label: stage.name,
549
+ provider: stageProvider,
550
+ model: stageModel,
551
+ calls,
552
+ cost,
553
+ formattedCost: formatCost(cost),
554
+ usingDefault: !stage.provider,
555
+ level: 1
556
+ });
557
+
558
+ overview.totalCost += cost;
559
+ overview.totalCalls += calls;
560
+ }
561
+ });
562
+
563
+ return overview;
564
+ }
565
+
566
+ /**
567
+ * Get validation stage overview with validation types (sprint-planning only)
568
+ */
569
+ _getValidationStageOverview(ceremony, validationStage) {
570
+ const validationConfig = ceremony.stages?.validation || {};
571
+ const validationProvider = validationConfig.provider || ceremony.provider;
572
+ const validationModel = validationConfig.model || ceremony.defaultModel;
573
+
574
+ const overview = {
575
+ id: 'validation',
576
+ label: 'Multi-Agent Validation - Domain experts validate each epic and story',
577
+ provider: validationProvider,
578
+ model: validationModel,
579
+ calls: 145,
580
+ cost: 0, // Calculated from types
581
+ formattedCost: '', // Set after calculating types
582
+ usingDefault: !validationConfig.provider,
583
+ level: 1,
584
+ validationTypes: []
585
+ };
586
+
587
+ const types = [
588
+ {
589
+ id: 'universal',
590
+ name: 'Universal Validators - Always applied (architecture, security, quality)',
591
+ calls: 30,
592
+ validators: ['solution-architect', 'developer', 'security', 'qa', 'test-architect']
593
+ },
594
+ {
595
+ id: 'domain',
596
+ name: 'Domain Validators - Applied based on project tech stack',
597
+ calls: 90,
598
+ validators: ['devops', 'database', 'api', 'frontend', 'backend', 'cloud', 'mobile', 'ui', 'ux', 'data']
599
+ },
600
+ {
601
+ id: 'feature',
602
+ name: 'Feature Validators - Applied based on acceptance criteria keywords',
603
+ calls: 25,
604
+ validators: ['testing', 'security', 'file-upload', 'notifications', 'reporting']
605
+ }
606
+ ];
607
+
608
+ let totalValidationCost = 0;
609
+
610
+ types.forEach(type => {
611
+ const typeConfig = validationConfig.validationTypes?.[type.id];
612
+ const typeProvider = typeConfig?.provider || validationProvider;
613
+ const typeModel = typeConfig?.model || validationModel;
614
+ const tokens = getEstimatedTokens('sprint-planning', `validation-${type.id}`);
615
+ const cost = calculateCost(typeModel, tokens);
616
+
617
+ overview.validationTypes.push({
618
+ id: type.id,
619
+ label: `${type.name} Validators`,
620
+ provider: typeProvider,
621
+ model: typeModel,
622
+ calls: type.calls,
623
+ cost,
624
+ formattedCost: formatCost(cost),
625
+ validators: type.validators,
626
+ usingDefault: !typeConfig,
627
+ level: 2
628
+ });
629
+
630
+ totalValidationCost += cost;
631
+ });
632
+
633
+ overview.cost = totalValidationCost;
634
+ overview.formattedCost = formatCost(totalValidationCost);
635
+
636
+ return overview;
637
+ }
638
+
639
+ /**
640
+ * Get call count for a stage
641
+ */
642
+ _getCallCount(ceremonyName, stageId) {
643
+ const callCounts = {
644
+ 'sprint-planning': {
645
+ 'decomposition': 1,
646
+ 'doc-distribution': 25,
647
+ 'validation': 145
648
+ },
649
+ 'sponsor-call': {
650
+ 'suggestions': 1,
651
+ 'documentation': 1,
652
+ 'validation': 2,
653
+ 'refinement': 2
654
+ },
655
+ 'seed': {
656
+ 'decomposition': 1,
657
+ 'validation': 20,
658
+ 'context-generation': 10
659
+ },
660
+ 'context-retrospective': {
661
+ 'documentation-update': 10,
662
+ 'context-refinement': 15
663
+ }
664
+ };
665
+
666
+ return callCounts[ceremonyName]?.[stageId] || 0;
667
+ }
668
+
669
+ /**
670
+ * Reset a configuration path to use main default
671
+ * @param {string} ceremonyName - Ceremony name
672
+ * @param {string} path - Configuration path (e.g., 'stages.decomposition')
673
+ */
674
+ resetToDefault(ceremonyName, configPath) {
675
+ if (!this.config) {
676
+ this.readConfig();
677
+ }
678
+
679
+ const ceremony = this.config.settings.ceremonies.find(c => c.name === ceremonyName);
680
+ if (!ceremony) {
681
+ throw new Error(`Ceremony '${ceremonyName}' not found`);
682
+ }
683
+
684
+ // Delete the configuration at the path to fall back to default
685
+ const pathParts = configPath.split('.');
686
+ let current = ceremony;
687
+
688
+ for (let i = 0; i < pathParts.length - 1; i++) {
689
+ current = current[pathParts[i]];
690
+ if (!current) return; // Path doesn't exist, nothing to reset
691
+ }
692
+
693
+ delete current[pathParts[pathParts.length - 1]];
694
+
695
+ this.saveConfig(this.config);
696
+ }
697
+ }