@agile-vibe-coding/avc 0.1.0 → 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 (290) hide show
  1. package/README.md +2 -0
  2. package/cli/agent-loader.js +21 -0
  3. package/cli/agents/agent-selector.md +129 -0
  4. package/cli/agents/architecture-recommender.md +418 -0
  5. package/cli/agents/database-deep-dive.md +470 -0
  6. package/cli/agents/database-recommender.md +634 -0
  7. package/cli/agents/doc-distributor.md +176 -0
  8. package/cli/agents/documentation-updater.md +203 -0
  9. package/cli/agents/epic-story-decomposer.md +280 -0
  10. package/cli/agents/feature-context-generator.md +91 -0
  11. package/cli/agents/gap-checker-epic.md +52 -0
  12. package/cli/agents/impact-checker-story.md +51 -0
  13. package/cli/agents/migration-guide-generator.md +305 -0
  14. package/cli/agents/mission-scope-generator.md +79 -0
  15. package/cli/agents/mission-scope-validator.md +112 -0
  16. package/cli/agents/project-context-extractor.md +107 -0
  17. package/cli/agents/project-documentation-creator.json +226 -0
  18. package/cli/agents/project-documentation-creator.md +595 -0
  19. package/cli/agents/question-prefiller.md +269 -0
  20. package/cli/agents/refiner-epic.md +39 -0
  21. package/cli/agents/refiner-story.md +42 -0
  22. package/cli/agents/solver-epic-api.json +15 -0
  23. package/cli/agents/solver-epic-api.md +39 -0
  24. package/cli/agents/solver-epic-backend.json +15 -0
  25. package/cli/agents/solver-epic-backend.md +39 -0
  26. package/cli/agents/solver-epic-cloud.json +15 -0
  27. package/cli/agents/solver-epic-cloud.md +39 -0
  28. package/cli/agents/solver-epic-data.json +15 -0
  29. package/cli/agents/solver-epic-data.md +39 -0
  30. package/cli/agents/solver-epic-database.json +15 -0
  31. package/cli/agents/solver-epic-database.md +39 -0
  32. package/cli/agents/solver-epic-developer.json +15 -0
  33. package/cli/agents/solver-epic-developer.md +39 -0
  34. package/cli/agents/solver-epic-devops.json +15 -0
  35. package/cli/agents/solver-epic-devops.md +39 -0
  36. package/cli/agents/solver-epic-frontend.json +15 -0
  37. package/cli/agents/solver-epic-frontend.md +39 -0
  38. package/cli/agents/solver-epic-mobile.json +15 -0
  39. package/cli/agents/solver-epic-mobile.md +39 -0
  40. package/cli/agents/solver-epic-qa.json +15 -0
  41. package/cli/agents/solver-epic-qa.md +39 -0
  42. package/cli/agents/solver-epic-security.json +15 -0
  43. package/cli/agents/solver-epic-security.md +39 -0
  44. package/cli/agents/solver-epic-solution-architect.json +15 -0
  45. package/cli/agents/solver-epic-solution-architect.md +39 -0
  46. package/cli/agents/solver-epic-test-architect.json +15 -0
  47. package/cli/agents/solver-epic-test-architect.md +39 -0
  48. package/cli/agents/solver-epic-ui.json +15 -0
  49. package/cli/agents/solver-epic-ui.md +39 -0
  50. package/cli/agents/solver-epic-ux.json +15 -0
  51. package/cli/agents/solver-epic-ux.md +39 -0
  52. package/cli/agents/solver-story-api.json +15 -0
  53. package/cli/agents/solver-story-api.md +39 -0
  54. package/cli/agents/solver-story-backend.json +15 -0
  55. package/cli/agents/solver-story-backend.md +39 -0
  56. package/cli/agents/solver-story-cloud.json +15 -0
  57. package/cli/agents/solver-story-cloud.md +39 -0
  58. package/cli/agents/solver-story-data.json +15 -0
  59. package/cli/agents/solver-story-data.md +39 -0
  60. package/cli/agents/solver-story-database.json +15 -0
  61. package/cli/agents/solver-story-database.md +39 -0
  62. package/cli/agents/solver-story-developer.json +15 -0
  63. package/cli/agents/solver-story-developer.md +39 -0
  64. package/cli/agents/solver-story-devops.json +15 -0
  65. package/cli/agents/solver-story-devops.md +39 -0
  66. package/cli/agents/solver-story-frontend.json +15 -0
  67. package/cli/agents/solver-story-frontend.md +39 -0
  68. package/cli/agents/solver-story-mobile.json +15 -0
  69. package/cli/agents/solver-story-mobile.md +39 -0
  70. package/cli/agents/solver-story-qa.json +15 -0
  71. package/cli/agents/solver-story-qa.md +39 -0
  72. package/cli/agents/solver-story-security.json +15 -0
  73. package/cli/agents/solver-story-security.md +39 -0
  74. package/cli/agents/solver-story-solution-architect.json +15 -0
  75. package/cli/agents/solver-story-solution-architect.md +39 -0
  76. package/cli/agents/solver-story-test-architect.json +15 -0
  77. package/cli/agents/solver-story-test-architect.md +39 -0
  78. package/cli/agents/solver-story-ui.json +15 -0
  79. package/cli/agents/solver-story-ui.md +39 -0
  80. package/cli/agents/solver-story-ux.json +15 -0
  81. package/cli/agents/solver-story-ux.md +39 -0
  82. package/cli/agents/story-doc-enricher.md +133 -0
  83. package/cli/agents/suggestion-business-analyst.md +88 -0
  84. package/cli/agents/suggestion-deployment-architect.md +263 -0
  85. package/cli/agents/suggestion-product-manager.md +129 -0
  86. package/cli/agents/suggestion-security-specialist.md +156 -0
  87. package/cli/agents/suggestion-technical-architect.md +269 -0
  88. package/cli/agents/suggestion-ux-researcher.md +93 -0
  89. package/cli/agents/task-subtask-decomposer.md +188 -0
  90. package/cli/agents/validator-documentation.json +152 -0
  91. package/cli/agents/validator-documentation.md +453 -0
  92. package/cli/agents/validator-epic-api.json +93 -0
  93. package/cli/agents/validator-epic-api.md +137 -0
  94. package/cli/agents/validator-epic-backend.json +93 -0
  95. package/cli/agents/validator-epic-backend.md +130 -0
  96. package/cli/agents/validator-epic-cloud.json +93 -0
  97. package/cli/agents/validator-epic-cloud.md +137 -0
  98. package/cli/agents/validator-epic-data.json +93 -0
  99. package/cli/agents/validator-epic-data.md +130 -0
  100. package/cli/agents/validator-epic-database.json +93 -0
  101. package/cli/agents/validator-epic-database.md +137 -0
  102. package/cli/agents/validator-epic-developer.json +74 -0
  103. package/cli/agents/validator-epic-developer.md +153 -0
  104. package/cli/agents/validator-epic-devops.json +74 -0
  105. package/cli/agents/validator-epic-devops.md +153 -0
  106. package/cli/agents/validator-epic-frontend.json +74 -0
  107. package/cli/agents/validator-epic-frontend.md +153 -0
  108. package/cli/agents/validator-epic-mobile.json +93 -0
  109. package/cli/agents/validator-epic-mobile.md +130 -0
  110. package/cli/agents/validator-epic-qa.json +93 -0
  111. package/cli/agents/validator-epic-qa.md +130 -0
  112. package/cli/agents/validator-epic-security.json +74 -0
  113. package/cli/agents/validator-epic-security.md +154 -0
  114. package/cli/agents/validator-epic-solution-architect.json +74 -0
  115. package/cli/agents/validator-epic-solution-architect.md +156 -0
  116. package/cli/agents/validator-epic-test-architect.json +93 -0
  117. package/cli/agents/validator-epic-test-architect.md +130 -0
  118. package/cli/agents/validator-epic-ui.json +93 -0
  119. package/cli/agents/validator-epic-ui.md +130 -0
  120. package/cli/agents/validator-epic-ux.json +93 -0
  121. package/cli/agents/validator-epic-ux.md +130 -0
  122. package/cli/agents/validator-selector.md +211 -0
  123. package/cli/agents/validator-story-api.json +104 -0
  124. package/cli/agents/validator-story-api.md +152 -0
  125. package/cli/agents/validator-story-backend.json +104 -0
  126. package/cli/agents/validator-story-backend.md +152 -0
  127. package/cli/agents/validator-story-cloud.json +104 -0
  128. package/cli/agents/validator-story-cloud.md +152 -0
  129. package/cli/agents/validator-story-data.json +104 -0
  130. package/cli/agents/validator-story-data.md +152 -0
  131. package/cli/agents/validator-story-database.json +104 -0
  132. package/cli/agents/validator-story-database.md +152 -0
  133. package/cli/agents/validator-story-developer.json +104 -0
  134. package/cli/agents/validator-story-developer.md +152 -0
  135. package/cli/agents/validator-story-devops.json +104 -0
  136. package/cli/agents/validator-story-devops.md +152 -0
  137. package/cli/agents/validator-story-frontend.json +104 -0
  138. package/cli/agents/validator-story-frontend.md +152 -0
  139. package/cli/agents/validator-story-mobile.json +104 -0
  140. package/cli/agents/validator-story-mobile.md +152 -0
  141. package/cli/agents/validator-story-qa.json +104 -0
  142. package/cli/agents/validator-story-qa.md +152 -0
  143. package/cli/agents/validator-story-security.json +104 -0
  144. package/cli/agents/validator-story-security.md +152 -0
  145. package/cli/agents/validator-story-solution-architect.json +104 -0
  146. package/cli/agents/validator-story-solution-architect.md +152 -0
  147. package/cli/agents/validator-story-test-architect.json +104 -0
  148. package/cli/agents/validator-story-test-architect.md +152 -0
  149. package/cli/agents/validator-story-ui.json +104 -0
  150. package/cli/agents/validator-story-ui.md +152 -0
  151. package/cli/agents/validator-story-ux.json +104 -0
  152. package/cli/agents/validator-story-ux.md +152 -0
  153. package/cli/ansi-colors.js +21 -0
  154. package/cli/build-docs.js +298 -0
  155. package/cli/ceremony-history.js +369 -0
  156. package/cli/command-logger.js +245 -0
  157. package/cli/components/static-output.js +63 -0
  158. package/cli/console-output-manager.js +94 -0
  159. package/cli/docs-sync.js +306 -0
  160. package/cli/epic-story-validator.js +1174 -0
  161. package/cli/evaluation-prompts.js +1008 -0
  162. package/cli/execution-context.js +195 -0
  163. package/cli/generate-summary-table.js +340 -0
  164. package/cli/index.js +3 -25
  165. package/cli/init-model-config.js +697 -0
  166. package/cli/init.js +1765 -100
  167. package/cli/kanban-server-manager.js +228 -0
  168. package/cli/llm-claude.js +109 -0
  169. package/cli/llm-gemini.js +115 -0
  170. package/cli/llm-mock.js +233 -0
  171. package/cli/llm-openai.js +233 -0
  172. package/cli/llm-provider.js +300 -0
  173. package/cli/llm-token-limits.js +102 -0
  174. package/cli/llm-verifier.js +454 -0
  175. package/cli/logger.js +32 -5
  176. package/cli/message-constants.js +58 -0
  177. package/cli/message-manager.js +334 -0
  178. package/cli/message-types.js +96 -0
  179. package/cli/messaging-api.js +297 -0
  180. package/cli/model-pricing.js +169 -0
  181. package/cli/model-query-engine.js +468 -0
  182. package/cli/model-recommendation-analyzer.js +495 -0
  183. package/cli/model-selector.js +269 -0
  184. package/cli/output-buffer.js +107 -0
  185. package/cli/process-manager.js +332 -0
  186. package/cli/repl-ink.js +5840 -504
  187. package/cli/repl-old.js +4 -4
  188. package/cli/seed-processor.js +792 -0
  189. package/cli/sprint-planning-processor.js +1813 -0
  190. package/cli/template-processor.js +2306 -108
  191. package/cli/templates/project.md +25 -8
  192. package/cli/templates/vitepress-config.mts.template +34 -0
  193. package/cli/token-tracker.js +520 -0
  194. package/cli/tools/generate-story-validators.js +317 -0
  195. package/cli/tools/generate-validators.js +669 -0
  196. package/cli/update-checker.js +19 -17
  197. package/cli/update-notifier.js +4 -4
  198. package/cli/validation-router.js +605 -0
  199. package/cli/verification-tracker.js +563 -0
  200. package/kanban/README.md +386 -0
  201. package/kanban/client/README.md +205 -0
  202. package/kanban/client/components.json +20 -0
  203. package/kanban/client/dist/assets/index-CiD8PS2e.js +306 -0
  204. package/kanban/client/dist/assets/index-nLh0m82Q.css +1 -0
  205. package/kanban/client/dist/index.html +16 -0
  206. package/kanban/client/dist/vite.svg +1 -0
  207. package/kanban/client/index.html +15 -0
  208. package/kanban/client/package-lock.json +9442 -0
  209. package/kanban/client/package.json +44 -0
  210. package/kanban/client/postcss.config.js +6 -0
  211. package/kanban/client/public/vite.svg +1 -0
  212. package/kanban/client/src/App.jsx +622 -0
  213. package/kanban/client/src/components/ProjectFileEditorPopup.jsx +117 -0
  214. package/kanban/client/src/components/ceremony/AskArchPopup.jsx +416 -0
  215. package/kanban/client/src/components/ceremony/AskModelPopup.jsx +616 -0
  216. package/kanban/client/src/components/ceremony/CeremonyWorkflowModal.jsx +946 -0
  217. package/kanban/client/src/components/ceremony/EpicStorySelectionModal.jsx +254 -0
  218. package/kanban/client/src/components/ceremony/SponsorCallModal.jsx +619 -0
  219. package/kanban/client/src/components/ceremony/SprintPlanningModal.jsx +704 -0
  220. package/kanban/client/src/components/ceremony/steps/ArchitectureStep.jsx +150 -0
  221. package/kanban/client/src/components/ceremony/steps/CompleteStep.jsx +154 -0
  222. package/kanban/client/src/components/ceremony/steps/DatabaseStep.jsx +202 -0
  223. package/kanban/client/src/components/ceremony/steps/DeploymentStep.jsx +123 -0
  224. package/kanban/client/src/components/ceremony/steps/MissionStep.jsx +106 -0
  225. package/kanban/client/src/components/ceremony/steps/ReviewAnswersStep.jsx +125 -0
  226. package/kanban/client/src/components/ceremony/steps/RunningStep.jsx +228 -0
  227. package/kanban/client/src/components/kanban/CardDetailModal.jsx +559 -0
  228. package/kanban/client/src/components/kanban/EpicSection.jsx +146 -0
  229. package/kanban/client/src/components/kanban/FilterToolbar.jsx +222 -0
  230. package/kanban/client/src/components/kanban/GroupingSelector.jsx +57 -0
  231. package/kanban/client/src/components/kanban/KanbanBoard.jsx +211 -0
  232. package/kanban/client/src/components/kanban/KanbanCard.jsx +138 -0
  233. package/kanban/client/src/components/kanban/KanbanColumn.jsx +90 -0
  234. package/kanban/client/src/components/kanban/RefineWorkItemPopup.jsx +789 -0
  235. package/kanban/client/src/components/layout/LoadingScreen.jsx +82 -0
  236. package/kanban/client/src/components/process/ProcessMonitorBar.jsx +80 -0
  237. package/kanban/client/src/components/settings/AgentEditorPopup.jsx +171 -0
  238. package/kanban/client/src/components/settings/AgentsTab.jsx +353 -0
  239. package/kanban/client/src/components/settings/ApiKeysTab.jsx +113 -0
  240. package/kanban/client/src/components/settings/CeremonyModelsTab.jsx +98 -0
  241. package/kanban/client/src/components/settings/CostThresholdsTab.jsx +94 -0
  242. package/kanban/client/src/components/settings/ModelPricingTab.jsx +204 -0
  243. package/kanban/client/src/components/settings/ServersTab.jsx +121 -0
  244. package/kanban/client/src/components/settings/SettingsModal.jsx +84 -0
  245. package/kanban/client/src/components/stats/CostModal.jsx +353 -0
  246. package/kanban/client/src/components/ui/badge.jsx +27 -0
  247. package/kanban/client/src/components/ui/dialog.jsx +121 -0
  248. package/kanban/client/src/components/ui/tabs.jsx +85 -0
  249. package/kanban/client/src/hooks/__tests__/useGrouping.test.js +232 -0
  250. package/kanban/client/src/hooks/useGrouping.js +118 -0
  251. package/kanban/client/src/hooks/useWebSocket.js +120 -0
  252. package/kanban/client/src/lib/__tests__/api.test.js +196 -0
  253. package/kanban/client/src/lib/__tests__/status-grouping.test.js +94 -0
  254. package/kanban/client/src/lib/api.js +401 -0
  255. package/kanban/client/src/lib/status-grouping.js +144 -0
  256. package/kanban/client/src/lib/utils.js +11 -0
  257. package/kanban/client/src/main.jsx +10 -0
  258. package/kanban/client/src/store/__tests__/kanbanStore.test.js +164 -0
  259. package/kanban/client/src/store/ceremonyStore.js +172 -0
  260. package/kanban/client/src/store/filterStore.js +201 -0
  261. package/kanban/client/src/store/kanbanStore.js +115 -0
  262. package/kanban/client/src/store/processStore.js +65 -0
  263. package/kanban/client/src/store/sprintPlanningStore.js +33 -0
  264. package/kanban/client/src/styles/globals.css +59 -0
  265. package/kanban/client/tailwind.config.js +77 -0
  266. package/kanban/client/vite.config.js +28 -0
  267. package/kanban/client/vitest.config.js +28 -0
  268. package/kanban/dev-start.sh +47 -0
  269. package/kanban/package.json +12 -0
  270. package/kanban/server/index.js +516 -0
  271. package/kanban/server/routes/ceremony.js +305 -0
  272. package/kanban/server/routes/costs.js +157 -0
  273. package/kanban/server/routes/processes.js +50 -0
  274. package/kanban/server/routes/settings.js +303 -0
  275. package/kanban/server/routes/websocket.js +276 -0
  276. package/kanban/server/routes/work-items.js +347 -0
  277. package/kanban/server/services/CeremonyService.js +1190 -0
  278. package/kanban/server/services/FileSystemScanner.js +95 -0
  279. package/kanban/server/services/FileWatcher.js +144 -0
  280. package/kanban/server/services/HierarchyBuilder.js +196 -0
  281. package/kanban/server/services/ProcessRegistry.js +122 -0
  282. package/kanban/server/services/WorkItemReader.js +123 -0
  283. package/kanban/server/services/WorkItemRefineService.js +510 -0
  284. package/kanban/server/start.js +49 -0
  285. package/kanban/server/utils/kanban-logger.js +132 -0
  286. package/kanban/server/utils/markdown.js +91 -0
  287. package/kanban/server/utils/status-grouping.js +107 -0
  288. package/kanban/server/workers/sponsor-call-worker.js +84 -0
  289. package/kanban/server/workers/sprint-planning-worker.js +130 -0
  290. package/package.json +34 -7
@@ -0,0 +1,269 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Model Selector - Multi-Provider Model Evaluation Tool
5
+ * Queries Claude, OpenAI, and Gemini with evaluation prompts
6
+ * to collect model recommendations for each AVC stage
7
+ */
8
+
9
+ import fs from 'fs';
10
+ import path from 'path';
11
+ import { fileURLToPath } from 'url';
12
+ import { EVALUATION_PROMPTS, getPromptStats } from './evaluation-prompts.js';
13
+ import { ModelQueryEngine } from './model-query-engine.js';
14
+ import { ModelRecommendationAnalyzer } from './model-recommendation-analyzer.js';
15
+ import { sendError, sendWarning, sendSuccess, sendInfo, sendOutput, sendIndented, sendSectionHeader } from './messaging-api.js';
16
+
17
+ const __filename = fileURLToPath(import.meta.url);
18
+ const __dirname = path.dirname(__filename);
19
+
20
+ /**
21
+ * Ensure _temp directory exists
22
+ */
23
+ function ensureTempDir() {
24
+ const tempDir = path.join(process.cwd(), '_temp');
25
+ if (!fs.existsSync(tempDir)) {
26
+ fs.mkdirSync(tempDir, { recursive: true });
27
+ }
28
+ return tempDir;
29
+ }
30
+
31
+ /**
32
+ * Display banner
33
+ */
34
+ function displayBanner() {
35
+ console.log('\n╔══════════════════════════════════════════════════════════════╗');
36
+ console.log('║ Multi-Provider Model Selection Evaluator ║');
37
+ console.log('║ Queries Claude, OpenAI, and Gemini for recommendations ║');
38
+ console.log('╚══════════════════════════════════════════════════════════════╝\n');
39
+ }
40
+
41
+ /**
42
+ * Display environment check
43
+ */
44
+ function displayEnvironmentCheck() {
45
+ sendSectionHeader('Checking API keys');
46
+
47
+ const keys = {
48
+ 'ANTHROPIC_API_KEY': !!process.env.ANTHROPIC_API_KEY,
49
+ 'OPENAI_API_KEY': !!process.env.OPENAI_API_KEY,
50
+ 'GEMINI_API_KEY': !!process.env.GEMINI_API_KEY
51
+ };
52
+
53
+ for (const [key, available] of Object.entries(keys)) {
54
+ if (available) {
55
+ sendIndented(`${key}: Found`, 1);
56
+ } else {
57
+ sendIndented(`${key}: Not found`, 1);
58
+ }
59
+ }
60
+
61
+ const availableCount = Object.values(keys).filter(v => v).length;
62
+
63
+ sendOutput(`\n ${availableCount}/3 providers available`);
64
+
65
+ if (availableCount === 0) {
66
+ sendError('No API keys found. Please add at least one to your .env file.');
67
+ process.exit(1);
68
+ }
69
+
70
+ return availableCount;
71
+ }
72
+
73
+ /**
74
+ * Display prompt statistics
75
+ */
76
+ function displayPromptStats() {
77
+ const stats = getPromptStats();
78
+
79
+ sendSectionHeader('Evaluation Overview');
80
+ sendIndented(`Total prompts: ${stats.totalPrompts}`, 1);
81
+ sendIndented(`Ceremonies: ${stats.ceremonies} (${stats.ceremonyList.join(', ')})`, 1);
82
+ sendIndented(`Estimated total API calls: ${stats.estimatedTotalCalls} per provider`, 1);
83
+ sendIndented('Impact distribution:', 1);
84
+ sendIndented(`- CRITICAL: ${stats.impactDistribution.CRITICAL}`, 2);
85
+ sendIndented(`- VERY HIGH: ${stats.impactDistribution['VERY HIGH']}`, 2);
86
+ sendIndented(`- HIGH: ${stats.impactDistribution.HIGH}`, 2);
87
+ sendIndented(`- MEDIUM: ${stats.impactDistribution.MEDIUM}`, 2);
88
+ }
89
+
90
+ /**
91
+ * Progress callback for query engine
92
+ */
93
+ function createProgressCallback() {
94
+ let currentPrompt = 0;
95
+
96
+ return (progress) => {
97
+ if (progress.type === 'prompt-start') {
98
+ currentPrompt = progress.current;
99
+ sendOutput(`\n[${progress.current}/${progress.total}] Processing: ${progress.ceremony}/${progress.stage}`);
100
+ } else if (progress.type === 'prompt-complete') {
101
+ const { successCount, failureCount, totalTime } = progress;
102
+ sendIndented(`Complete: ${successCount} success, ${failureCount} failed (${totalTime.toFixed(1)}s)`, 1);
103
+ }
104
+ };
105
+ }
106
+
107
+ /**
108
+ * Write JSON output file
109
+ */
110
+ function writeJSONOutput(data, outputPath) {
111
+ sendInfo(`Writing JSON output to ${outputPath}...`);
112
+ fs.writeFileSync(outputPath, JSON.stringify(data, null, 2), 'utf-8');
113
+ sendIndented('JSON file written', 1);
114
+ }
115
+
116
+ /**
117
+ * Write Markdown output file
118
+ */
119
+ function writeMarkdownOutput(content, outputPath) {
120
+ sendInfo(`Writing Markdown report to ${outputPath}...`);
121
+ fs.writeFileSync(outputPath, content, 'utf-8');
122
+ sendIndented('Markdown report written', 1);
123
+ }
124
+
125
+ /**
126
+ * Display summary
127
+ */
128
+ function displaySummary(summary) {
129
+ console.log('\n╔══════════════════════════════════════════════════════════════╗');
130
+ console.log('║ Execution Summary ║');
131
+ console.log('╚══════════════════════════════════════════════════════════════╝\n');
132
+
133
+ console.log(`Total prompts evaluated: ${summary.totalPrompts}`);
134
+ console.log(`Successful queries: ${summary.successfulQueries}`);
135
+ console.log(`Failed queries: ${summary.failedQueries}`);
136
+ console.log(`\nExecution time: ${summary.executionTime}`);
137
+
138
+ console.log('\nCost breakdown:');
139
+ for (const [provider, cost] of Object.entries(summary.totalCost)) {
140
+ if (provider !== 'total') {
141
+ console.log(` ${provider}: $${cost.toFixed(4)}`);
142
+ }
143
+ }
144
+ console.log(` TOTAL: $${summary.totalCost.total.toFixed(4)}`);
145
+ }
146
+
147
+ /**
148
+ * Display analysis summary
149
+ */
150
+ function displayAnalysisSummary(statistics) {
151
+ console.log('\n╔══════════════════════════════════════════════════════════════╗');
152
+ console.log('║ Analysis Summary ║');
153
+ console.log('╚══════════════════════════════════════════════════════════════╝\n');
154
+
155
+ console.log('Consensus Statistics:');
156
+ console.log(` Full consensus: ${statistics.consensus.full} (${statistics.consensusRate.full})`);
157
+ console.log(` Partial consensus: ${statistics.consensus.partial} (${statistics.consensusRate.partial})`);
158
+ console.log(` No consensus: ${statistics.consensus.none} (${statistics.consensusRate.none})`);
159
+
160
+ console.log('\nDefault Alignment:');
161
+ console.log(` Perfect: ${statistics.defaultAlignment.perfect} stages`);
162
+ console.log(` Partial: ${statistics.defaultAlignment.partial} stages`);
163
+ console.log(` None: ${statistics.defaultAlignment.none} stages`);
164
+
165
+ console.log('\nRecommendations:');
166
+ console.log(` Upgrade suggestions: ${statistics.upgradeRecommendations} stages`);
167
+ console.log(` Downgrade suggestions: ${statistics.downgradeRecommendations} stages`);
168
+ }
169
+
170
+ /**
171
+ * Main execution function
172
+ */
173
+ async function main() {
174
+ try {
175
+ displayBanner();
176
+
177
+ // Check environment
178
+ const availableProviders = displayEnvironmentCheck();
179
+
180
+ // Display stats
181
+ displayPromptStats();
182
+
183
+ // Confirm execution
184
+ sendWarning('This will execute multiple API calls to all available providers.');
185
+ sendIndented('Estimated cost: $0.15-0.30 depending on available providers.', 1);
186
+ sendIndented('Press Ctrl+C to cancel, or wait 5 seconds to continue...', 1);
187
+
188
+ // Wait 5 seconds for user to cancel
189
+ await new Promise(resolve => setTimeout(resolve, 5000));
190
+
191
+ sendInfo('Starting evaluation...');
192
+
193
+ // Initialize query engine
194
+ const engine = new ModelQueryEngine();
195
+
196
+ sendInfo('Initializing providers...');
197
+ const initResult = await engine.initializeProviders();
198
+
199
+ sendIndented(`${initResult.readyCount} provider(s) initialized`, 1);
200
+
201
+ if (initResult.errors) {
202
+ sendWarning('Some providers failed to initialize:');
203
+ for (const error of initResult.errors) {
204
+ sendIndented(`- ${error.provider}: ${error.error}`, 1);
205
+ }
206
+ }
207
+
208
+ // Run evaluation
209
+ sendInfo('Querying providers (this may take several minutes)...');
210
+
211
+ const progressCallback = createProgressCallback();
212
+ const results = await engine.evaluateAll(EVALUATION_PROMPTS, progressCallback);
213
+
214
+ sendSuccess('All evaluations complete!');
215
+
216
+ // Analyze results
217
+ sendInfo('Analyzing results...');
218
+ const analyzer = new ModelRecommendationAnalyzer(results.evaluations);
219
+ const analysis = analyzer.analyze();
220
+ sendIndented('Analysis complete', 1);
221
+
222
+ // Ensure _temp directory exists
223
+ const tempDir = ensureTempDir();
224
+
225
+ // Write JSON output
226
+ const jsonPath = path.join(tempDir, 'model-recommendations.json');
227
+ const jsonOutput = {
228
+ ...results,
229
+ analysis: analysis.analyses,
230
+ statistics: analysis.statistics
231
+ };
232
+ writeJSONOutput(jsonOutput, jsonPath);
233
+
234
+ // Write Markdown report
235
+ const markdownPath = path.join(tempDir, 'MODEL_RECOMMENDATIONS_REPORT.md');
236
+ const markdownReport = analyzer.generateMarkdownReport();
237
+ writeMarkdownOutput(markdownReport, markdownPath);
238
+
239
+ // Display summaries
240
+ displaySummary(results.summary);
241
+ displayAnalysisSummary(analysis.statistics);
242
+
243
+ // Final message
244
+ sendOutput('\n╔══════════════════════════════════════════════════════════════╗');
245
+ sendOutput('║ Evaluation Complete! ║');
246
+ sendOutput('╚══════════════════════════════════════════════════════════════╝');
247
+ sendSectionHeader('Output files');
248
+ sendIndented(`- JSON: ${jsonPath}`, 1);
249
+ sendIndented(`- Report: ${markdownPath}`, 1);
250
+
251
+ sendSectionHeader('Next steps');
252
+ sendIndented('1. Review the markdown report for provider recommendations', 1);
253
+ sendIndented('2. Compare consensus vs current defaults', 1);
254
+ sendIndented('3. Consider cost vs quality trade-offs', 1);
255
+ sendIndented('4. Update model configurations in AVC if desired', 1);
256
+
257
+ } catch (error) {
258
+ sendError(`ERROR: ${error.message}`);
259
+ sendOutput(`\nStack trace: ${error.stack}`);
260
+ process.exit(1);
261
+ }
262
+ }
263
+
264
+ // Run if executed directly
265
+ if (import.meta.url === `file://${process.argv[1]}`) {
266
+ main();
267
+ }
268
+
269
+ export { main };
@@ -0,0 +1,107 @@
1
+ /**
2
+ * OutputBuffer - Manages accumulated output as item array for Static rendering
3
+ *
4
+ * Each append() call creates one item with a unique ID, compatible with
5
+ * Ink's <Static> component which requires stable keys and never erases items.
6
+ *
7
+ * Items accumulate across commands (like a natural terminal log).
8
+ * The clear() method inserts a blank separator line between commands
9
+ * instead of erasing — Static committed content cannot be removed.
10
+ */
11
+ export class OutputBuffer {
12
+ constructor() {
13
+ this.items = [];
14
+ this.itemCounter = 0;
15
+ this.listeners = [];
16
+ }
17
+
18
+ /**
19
+ * Append content as a new item
20
+ * @param {string} content - Content to append (may contain newlines)
21
+ * @param {string|null} type - Optional message type: 'ERROR'|'WARNING'|'SUCCESS'|'INFO'|null
22
+ */
23
+ append(content, type = null) {
24
+ if (!content) return;
25
+
26
+ this.items.push({ id: ++this.itemCounter, content, type });
27
+ this.notifyListeners();
28
+ }
29
+
30
+ /**
31
+ * Insert a blank separator between commands.
32
+ * Items cannot be removed from Static rendering, so this adds a
33
+ * visual separator instead of erasing previous content.
34
+ */
35
+ clear() {
36
+ if (this.items.length > 0) {
37
+ this.items.push({ id: ++this.itemCounter, content: '' });
38
+ this.notifyListeners();
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Get all items (for Static rendering)
44
+ * @returns {{id: number, content: string}[]} Copy of items array
45
+ */
46
+ getItems() {
47
+ return [...this.items];
48
+ }
49
+
50
+ /**
51
+ * Get item count
52
+ * @returns {number} Number of items
53
+ */
54
+ getItemCount() {
55
+ return this.items.length;
56
+ }
57
+
58
+ /**
59
+ * Get all lines as flat string array (for test compatibility)
60
+ * @returns {string[]} All content split by newlines
61
+ */
62
+ getLines() {
63
+ return this.items.flatMap(item => item.content.split('\n'));
64
+ }
65
+
66
+ /**
67
+ * Get line count (for backward compatibility)
68
+ * @returns {number} Total number of lines across all items
69
+ */
70
+ getLineCount() {
71
+ return this.getLines().length;
72
+ }
73
+
74
+ /**
75
+ * Reset buffer completely - removes all items.
76
+ * Used in tests for isolation between test cases.
77
+ */
78
+ reset() {
79
+ this.items = [];
80
+ this.itemCounter = 0;
81
+ this.notifyListeners();
82
+ }
83
+
84
+ /**
85
+ * Subscribe to buffer changes
86
+ * @param {Function} listener - Callback function (items) => void
87
+ * @returns {Function} Unsubscribe function
88
+ */
89
+ subscribe(listener) {
90
+ this.listeners.push(listener);
91
+ return () => {
92
+ this.listeners = this.listeners.filter(l => l !== listener);
93
+ };
94
+ }
95
+
96
+ /**
97
+ * Notify all listeners of buffer change
98
+ * @private
99
+ */
100
+ notifyListeners() {
101
+ const items = this.getItems();
102
+ this.listeners.forEach(listener => listener(items));
103
+ }
104
+ }
105
+
106
+ // Singleton instance
107
+ export const outputBuffer = new OutputBuffer();
@@ -0,0 +1,332 @@
1
+ import { spawn, fork } from 'child_process';
2
+ import { EventEmitter } from 'events';
3
+
4
+ /**
5
+ * Background Process Manager
6
+ * Manages lifecycle of long-running background processes
7
+ */
8
+ export class BackgroundProcessManager extends EventEmitter {
9
+ constructor() {
10
+ super();
11
+ this.processes = new Map(); // id -> processMetadata
12
+ this.maxOutputLines = 500; // Keep last 500 lines per process
13
+ }
14
+
15
+ /**
16
+ * Start a background process
17
+ * @param {Object} options
18
+ * @param {string} options.name - Human-readable name
19
+ * @param {string} options.command - Command to execute
20
+ * @param {string[]} options.args - Command arguments
21
+ * @param {string} options.cwd - Working directory
22
+ * @param {Object} options.env - Environment variables
23
+ * @returns {string} Process ID
24
+ */
25
+ startProcess({ name, command, args = [], cwd, env = process.env }) {
26
+ const id = `${this.sanitizeName(name)}-${Date.now()}`;
27
+
28
+ const childProcess = spawn(command, args, {
29
+ cwd,
30
+ env,
31
+ stdio: 'pipe',
32
+ detached: false
33
+ });
34
+
35
+ const metadata = {
36
+ id,
37
+ name,
38
+ command: `${command} ${args.join(' ')}`,
39
+ cwd,
40
+ pid: childProcess.pid,
41
+ status: 'running',
42
+ startTime: new Date().toISOString(),
43
+ exitCode: null,
44
+ exitSignal: null,
45
+ output: [],
46
+ process: childProcess
47
+ };
48
+
49
+ this.processes.set(id, metadata);
50
+
51
+ // Capture stdout
52
+ childProcess.stdout.on('data', (data) => {
53
+ this.appendOutput(id, 'stdout', data.toString());
54
+ });
55
+
56
+ // Capture stderr
57
+ childProcess.stderr.on('data', (data) => {
58
+ this.appendOutput(id, 'stderr', data.toString());
59
+ });
60
+
61
+ // Handle process exit
62
+ childProcess.on('exit', (code, signal) => {
63
+ this.handleProcessExit(id, code, signal);
64
+ });
65
+
66
+ // Handle process error
67
+ childProcess.on('error', (error) => {
68
+ this.handleProcessError(id, error);
69
+ });
70
+
71
+ this.emit('process-started', { id, name });
72
+
73
+ return id;
74
+ }
75
+
76
+ /**
77
+ * Fork a background process with an IPC channel.
78
+ * Identical metadata schema to startProcess(); also registers an IPC message handler.
79
+ * @param {string} name - Human-readable name
80
+ * @param {string} modulePath - Absolute path to the Node.js module to fork
81
+ * @param {string[]} args - Module arguments
82
+ * @param {Function} onMessage - Called with each IPC message from the child
83
+ * @returns {{ id: string, child: ChildProcess }}
84
+ */
85
+ forkProcess(name, modulePath, args = [], onMessage) {
86
+ const id = `${this.sanitizeName(name)}-${Date.now()}`;
87
+
88
+ const child = fork(modulePath, args, {
89
+ stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
90
+ });
91
+
92
+ const metadata = {
93
+ id,
94
+ name,
95
+ command: modulePath,
96
+ pid: child.pid,
97
+ status: 'running',
98
+ startTime: new Date().toISOString(),
99
+ exitCode: null,
100
+ exitSignal: null,
101
+ output: [],
102
+ process: child
103
+ };
104
+
105
+ this.processes.set(id, metadata);
106
+
107
+ // Capture stdout / stderr (same as startProcess)
108
+ child.stdout?.on('data', (data) => {
109
+ this.appendOutput(id, 'stdout', data.toString());
110
+ });
111
+
112
+ child.stderr?.on('data', (data) => {
113
+ this.appendOutput(id, 'stderr', data.toString());
114
+ });
115
+
116
+ // Handle process exit / error
117
+ child.on('exit', (code, signal) => {
118
+ this.handleProcessExit(id, code, signal);
119
+ });
120
+
121
+ child.on('error', (error) => {
122
+ this.handleProcessError(id, error);
123
+ });
124
+
125
+ // Register IPC message handler
126
+ if (onMessage) child.on('message', onMessage);
127
+
128
+ this.emit('process-started', { id, name });
129
+
130
+ return { id, child };
131
+ }
132
+
133
+ /**
134
+ * Stop a running process
135
+ */
136
+ stopProcess(id) {
137
+ const metadata = this.processes.get(id);
138
+ if (!metadata) return false;
139
+
140
+ if (metadata.status === 'running' && metadata.process) {
141
+ metadata.process.kill('SIGTERM');
142
+ metadata.status = 'stopped';
143
+ this.emit('process-stopped', { id, name: metadata.name });
144
+ return true;
145
+ }
146
+
147
+ return false;
148
+ }
149
+
150
+ /**
151
+ * Find process by PID
152
+ * @param {number} pid - Process ID to search for
153
+ * @returns {Object|null} Process metadata or null if not found
154
+ */
155
+ findProcessByPid(pid) {
156
+ for (const metadata of this.processes.values()) {
157
+ if (metadata.pid === pid) {
158
+ return metadata;
159
+ }
160
+ }
161
+ return null;
162
+ }
163
+
164
+ /**
165
+ * Remove process from manager by PID
166
+ * Used when external kill is performed on a managed process
167
+ * @param {number} pid - Process ID to remove
168
+ * @returns {boolean} True if process was found and removed
169
+ */
170
+ removeProcessByPid(pid) {
171
+ const process = this.findProcessByPid(pid);
172
+ if (process) {
173
+ this.processes.delete(process.id);
174
+ this.emit('process-removed', { id: process.id, name: process.name, pid });
175
+ return true;
176
+ }
177
+ return false;
178
+ }
179
+
180
+ /**
181
+ * Get process metadata
182
+ */
183
+ getProcess(id) {
184
+ return this.processes.get(id);
185
+ }
186
+
187
+ /**
188
+ * Get all processes
189
+ */
190
+ getAllProcesses() {
191
+ return Array.from(this.processes.values());
192
+ }
193
+
194
+ /**
195
+ * Get running processes only
196
+ */
197
+ getRunningProcesses() {
198
+ return this.getAllProcesses().filter(p => p.status === 'running');
199
+ }
200
+
201
+ /**
202
+ * Append output to process buffer
203
+ */
204
+ appendOutput(id, type, text) {
205
+ const metadata = this.processes.get(id);
206
+ if (!metadata) return;
207
+
208
+ const lines = text.split('\n').filter(line => line.trim());
209
+
210
+ for (const line of lines) {
211
+ metadata.output.push({
212
+ timestamp: new Date().toISOString(),
213
+ type,
214
+ text: line
215
+ });
216
+ }
217
+
218
+ // Trim to max lines
219
+ if (metadata.output.length > this.maxOutputLines) {
220
+ metadata.output = metadata.output.slice(-this.maxOutputLines);
221
+ }
222
+
223
+ this.emit('output', { id, type, text });
224
+ }
225
+
226
+ /**
227
+ * Handle process exit
228
+ */
229
+ handleProcessExit(id, code, signal) {
230
+ const metadata = this.processes.get(id);
231
+ if (!metadata) return;
232
+
233
+ metadata.exitCode = code;
234
+ metadata.exitSignal = signal;
235
+
236
+ // Don't overwrite 'stopped' status (intentional termination via stopProcess)
237
+ // Only set crashed/exited if process wasn't manually stopped
238
+ if (metadata.status !== 'stopped') {
239
+ metadata.status = code === 0 ? 'exited' : 'crashed';
240
+ }
241
+
242
+ this.emit('process-exited', {
243
+ id,
244
+ name: metadata.name,
245
+ code,
246
+ signal,
247
+ status: metadata.status
248
+ });
249
+
250
+ // Auto-cleanup finished processes after 3 seconds
251
+ // Gives user time to see final status before removal
252
+ setTimeout(() => {
253
+ if (this.processes.has(id)) {
254
+ this.processes.delete(id);
255
+ this.emit('process-removed', { id, name: metadata.name });
256
+ }
257
+ }, 3000);
258
+ }
259
+
260
+ /**
261
+ * Handle process error
262
+ */
263
+ handleProcessError(id, error) {
264
+ const metadata = this.processes.get(id);
265
+ if (!metadata) return;
266
+
267
+ metadata.status = 'crashed';
268
+ this.appendOutput(id, 'stderr', `Process error: ${error.message}`);
269
+
270
+ this.emit('process-error', { id, name: metadata.name, error });
271
+ }
272
+
273
+ /**
274
+ * Clean up finished processes
275
+ * Removes processes with status: exited, crashed, or stopped
276
+ */
277
+ cleanupFinished() {
278
+ const finished = this.getAllProcesses().filter(
279
+ p => p.status === 'exited' || p.status === 'crashed' || p.status === 'stopped'
280
+ );
281
+
282
+ for (const process of finished) {
283
+ this.processes.delete(process.id);
284
+ }
285
+
286
+ return finished.length;
287
+ }
288
+
289
+ /**
290
+ * Stop all running processes
291
+ */
292
+ stopAll() {
293
+ const running = this.getRunningProcesses();
294
+
295
+ for (const process of running) {
296
+ this.stopProcess(process.id);
297
+ }
298
+
299
+ return running.length;
300
+ }
301
+
302
+ /**
303
+ * Get process uptime in seconds
304
+ */
305
+ getUptime(id) {
306
+ const metadata = this.processes.get(id);
307
+ if (!metadata) return 0;
308
+
309
+ const start = new Date(metadata.startTime);
310
+ const now = new Date();
311
+ return Math.floor((now - start) / 1000);
312
+ }
313
+
314
+ /**
315
+ * Format uptime as human-readable string
316
+ */
317
+ formatUptime(seconds) {
318
+ if (seconds < 60) return `${seconds}s`;
319
+ if (seconds < 3600) return `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
320
+
321
+ const hours = Math.floor(seconds / 3600);
322
+ const minutes = Math.floor((seconds % 3600) / 60);
323
+ return `${hours}h ${minutes}m`;
324
+ }
325
+
326
+ /**
327
+ * Sanitize name for use as ID prefix
328
+ */
329
+ sanitizeName(name) {
330
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
331
+ }
332
+ }