@agile-vibe-coding/avc 0.1.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/agent-loader.js +21 -0
- package/cli/agents/agent-selector.md +152 -0
- package/cli/agents/architecture-recommender.md +418 -0
- package/cli/agents/code-implementer.md +117 -0
- package/cli/agents/code-validator.md +80 -0
- package/cli/agents/context-reviewer-epic.md +101 -0
- package/cli/agents/context-reviewer-story.md +92 -0
- package/cli/agents/context-writer-epic.md +145 -0
- package/cli/agents/context-writer-story.md +111 -0
- package/cli/agents/database-deep-dive.md +470 -0
- package/cli/agents/database-recommender.md +634 -0
- package/cli/agents/doc-distributor.md +176 -0
- package/cli/agents/doc-writer-epic.md +42 -0
- package/cli/agents/doc-writer-story.md +43 -0
- package/cli/agents/documentation-updater.md +203 -0
- package/cli/agents/duplicate-detector.md +110 -0
- package/cli/agents/epic-story-decomposer.md +559 -0
- package/cli/agents/feature-context-generator.md +91 -0
- package/cli/agents/gap-checker-epic.md +52 -0
- package/cli/agents/impact-checker-story.md +51 -0
- package/cli/agents/migration-guide-generator.md +305 -0
- package/cli/agents/mission-scope-generator.md +143 -0
- package/cli/agents/mission-scope-validator.md +146 -0
- package/cli/agents/project-context-extractor.md +122 -0
- package/cli/agents/project-documentation-creator.json +226 -0
- package/cli/agents/project-documentation-creator.md +595 -0
- package/cli/agents/question-prefiller.md +269 -0
- package/cli/agents/refiner-epic.md +39 -0
- package/cli/agents/refiner-story.md +42 -0
- package/cli/agents/scaffolding-generator.md +99 -0
- package/cli/agents/seed-validator.md +71 -0
- package/cli/agents/story-doc-enricher.md +133 -0
- package/cli/agents/story-scope-reviewer.md +147 -0
- package/cli/agents/story-splitter.md +83 -0
- package/cli/agents/suggestion-business-analyst.md +88 -0
- package/cli/agents/suggestion-deployment-architect.md +263 -0
- package/cli/agents/suggestion-product-manager.md +129 -0
- package/cli/agents/suggestion-security-specialist.md +156 -0
- package/cli/agents/suggestion-technical-architect.md +269 -0
- package/cli/agents/suggestion-ux-researcher.md +93 -0
- package/cli/agents/task-subtask-decomposer.md +188 -0
- package/cli/agents/validator-documentation.json +183 -0
- package/cli/agents/validator-documentation.md +455 -0
- package/cli/agents/validator-selector.md +211 -0
- package/cli/ansi-colors.js +21 -0
- package/cli/api-reference-tool.js +368 -0
- package/cli/build-docs.js +29 -8
- package/cli/ceremony-history.js +369 -0
- package/cli/checks/catalog.json +76 -0
- package/cli/checks/code/quality.json +26 -0
- package/cli/checks/code/testing.json +14 -0
- package/cli/checks/code/traceability.json +26 -0
- package/cli/checks/cross-refs/epic.json +171 -0
- package/cli/checks/cross-refs/story.json +149 -0
- package/cli/checks/epic/api.json +114 -0
- package/cli/checks/epic/backend.json +126 -0
- package/cli/checks/epic/cloud.json +126 -0
- package/cli/checks/epic/data.json +102 -0
- package/cli/checks/epic/database.json +114 -0
- package/cli/checks/epic/developer.json +182 -0
- package/cli/checks/epic/devops.json +174 -0
- package/cli/checks/epic/frontend.json +162 -0
- package/cli/checks/epic/mobile.json +102 -0
- package/cli/checks/epic/qa.json +90 -0
- package/cli/checks/epic/security.json +184 -0
- package/cli/checks/epic/solution-architect.json +192 -0
- package/cli/checks/epic/test-architect.json +90 -0
- package/cli/checks/epic/ui.json +102 -0
- package/cli/checks/epic/ux.json +90 -0
- package/cli/checks/fixes/epic-fix-template.md +10 -0
- package/cli/checks/fixes/story-fix-template.md +10 -0
- package/cli/checks/story/api.json +186 -0
- package/cli/checks/story/backend.json +102 -0
- package/cli/checks/story/cloud.json +102 -0
- package/cli/checks/story/data.json +210 -0
- package/cli/checks/story/database.json +102 -0
- package/cli/checks/story/developer.json +168 -0
- package/cli/checks/story/devops.json +102 -0
- package/cli/checks/story/frontend.json +174 -0
- package/cli/checks/story/mobile.json +102 -0
- package/cli/checks/story/qa.json +210 -0
- package/cli/checks/story/security.json +198 -0
- package/cli/checks/story/solution-architect.json +230 -0
- package/cli/checks/story/test-architect.json +210 -0
- package/cli/checks/story/ui.json +102 -0
- package/cli/checks/story/ux.json +102 -0
- package/cli/coding-order.js +401 -0
- package/cli/command-logger.js +49 -12
- package/cli/components/static-output.js +63 -0
- package/cli/console-output-manager.js +94 -0
- package/cli/dependency-checker.js +72 -0
- package/cli/docs-sync.js +306 -0
- package/cli/epic-story-validator.js +659 -0
- package/cli/evaluation-prompts.js +1008 -0
- package/cli/execution-context.js +195 -0
- package/cli/generate-summary-table.js +340 -0
- package/cli/init-model-config.js +704 -0
- package/cli/init.js +1737 -278
- package/cli/kanban-server-manager.js +227 -0
- package/cli/llm-claude.js +150 -1
- package/cli/llm-gemini.js +109 -0
- package/cli/llm-local.js +493 -0
- package/cli/llm-mock.js +233 -0
- package/cli/llm-openai.js +454 -0
- package/cli/llm-provider.js +379 -3
- package/cli/llm-token-limits.js +211 -0
- package/cli/llm-verifier.js +662 -0
- package/cli/llm-xiaomi.js +143 -0
- package/cli/message-constants.js +49 -0
- package/cli/message-manager.js +334 -0
- package/cli/message-types.js +96 -0
- package/cli/messaging-api.js +291 -0
- package/cli/micro-check-fixer.js +335 -0
- package/cli/micro-check-runner.js +449 -0
- package/cli/micro-check-scorer.js +148 -0
- package/cli/micro-check-validator.js +538 -0
- package/cli/model-pricing.js +192 -0
- package/cli/model-query-engine.js +468 -0
- package/cli/model-recommendation-analyzer.js +495 -0
- package/cli/model-selector.js +270 -0
- package/cli/output-buffer.js +107 -0
- package/cli/process-manager.js +73 -2
- package/cli/prompt-logger.js +57 -0
- package/cli/repl-ink.js +4625 -1094
- package/cli/repl-old.js +3 -4
- package/cli/seed-processor.js +962 -0
- package/cli/sprint-planning-processor.js +4162 -0
- package/cli/template-processor.js +2149 -105
- package/cli/templates/project.md +25 -8
- package/cli/templates/vitepress-config.mts.template +5 -4
- package/cli/token-tracker.js +547 -0
- package/cli/tools/generate-story-validators.js +317 -0
- package/cli/tools/generate-validators.js +669 -0
- package/cli/update-checker.js +19 -17
- package/cli/update-notifier.js +4 -4
- package/cli/validation-router.js +667 -0
- package/cli/verification-tracker.js +563 -0
- package/cli/worktree-runner.js +654 -0
- package/kanban/README.md +386 -0
- package/kanban/client/README.md +205 -0
- package/kanban/client/components.json +20 -0
- package/kanban/client/dist/assets/index-D_KC5EQT.css +1 -0
- package/kanban/client/dist/assets/index-DjY5zqW7.js +351 -0
- package/kanban/client/dist/index.html +16 -0
- package/kanban/client/dist/vite.svg +1 -0
- package/kanban/client/index.html +15 -0
- package/kanban/client/package-lock.json +9442 -0
- package/kanban/client/package.json +44 -0
- package/kanban/client/postcss.config.js +6 -0
- package/kanban/client/public/vite.svg +1 -0
- package/kanban/client/src/App.jsx +651 -0
- package/kanban/client/src/components/ProjectFileEditorPopup.jsx +117 -0
- package/kanban/client/src/components/ceremony/AskArchPopup.jsx +420 -0
- package/kanban/client/src/components/ceremony/AskModelPopup.jsx +629 -0
- package/kanban/client/src/components/ceremony/CeremonyWorkflowModal.jsx +1133 -0
- package/kanban/client/src/components/ceremony/EpicStorySelectionModal.jsx +254 -0
- package/kanban/client/src/components/ceremony/ProviderSwitcherButton.jsx +290 -0
- package/kanban/client/src/components/ceremony/SponsorCallModal.jsx +686 -0
- package/kanban/client/src/components/ceremony/SprintPlanningModal.jsx +838 -0
- package/kanban/client/src/components/ceremony/steps/ArchitectureStep.jsx +150 -0
- package/kanban/client/src/components/ceremony/steps/CompleteStep.jsx +136 -0
- package/kanban/client/src/components/ceremony/steps/DatabaseStep.jsx +202 -0
- package/kanban/client/src/components/ceremony/steps/DeploymentStep.jsx +123 -0
- package/kanban/client/src/components/ceremony/steps/MissionStep.jsx +106 -0
- package/kanban/client/src/components/ceremony/steps/ReviewAnswersStep.jsx +329 -0
- package/kanban/client/src/components/ceremony/steps/RunningStep.jsx +249 -0
- package/kanban/client/src/components/kanban/CardDetailModal.jsx +646 -0
- package/kanban/client/src/components/kanban/EpicSection.jsx +146 -0
- package/kanban/client/src/components/kanban/FilterToolbar.jsx +222 -0
- package/kanban/client/src/components/kanban/GroupingSelector.jsx +63 -0
- package/kanban/client/src/components/kanban/KanbanBoard.jsx +211 -0
- package/kanban/client/src/components/kanban/KanbanCard.jsx +147 -0
- package/kanban/client/src/components/kanban/KanbanColumn.jsx +90 -0
- package/kanban/client/src/components/kanban/RefineWorkItemPopup.jsx +784 -0
- package/kanban/client/src/components/kanban/RunButton.jsx +162 -0
- package/kanban/client/src/components/kanban/SeedButton.jsx +176 -0
- package/kanban/client/src/components/layout/LoadingScreen.jsx +82 -0
- package/kanban/client/src/components/process/ProcessMonitorBar.jsx +80 -0
- package/kanban/client/src/components/settings/AgentEditorPopup.jsx +171 -0
- package/kanban/client/src/components/settings/AgentsTab.jsx +381 -0
- package/kanban/client/src/components/settings/ApiKeysTab.jsx +142 -0
- package/kanban/client/src/components/settings/CeremonyModelsTab.jsx +105 -0
- package/kanban/client/src/components/settings/CheckEditorPopup.jsx +507 -0
- package/kanban/client/src/components/settings/CostThresholdsTab.jsx +95 -0
- package/kanban/client/src/components/settings/ModelPricingTab.jsx +269 -0
- package/kanban/client/src/components/settings/OpenAIAuthSection.jsx +412 -0
- package/kanban/client/src/components/settings/ServersTab.jsx +121 -0
- package/kanban/client/src/components/settings/SettingsModal.jsx +84 -0
- package/kanban/client/src/components/stats/CostModal.jsx +384 -0
- package/kanban/client/src/components/ui/badge.jsx +27 -0
- package/kanban/client/src/components/ui/dialog.jsx +121 -0
- package/kanban/client/src/components/ui/tabs.jsx +85 -0
- package/kanban/client/src/hooks/__tests__/useGrouping.test.js +232 -0
- package/kanban/client/src/hooks/useGrouping.js +177 -0
- package/kanban/client/src/hooks/useWebSocket.js +120 -0
- package/kanban/client/src/lib/__tests__/api.test.js +196 -0
- package/kanban/client/src/lib/__tests__/status-grouping.test.js +94 -0
- package/kanban/client/src/lib/api.js +515 -0
- package/kanban/client/src/lib/status-grouping.js +154 -0
- package/kanban/client/src/lib/utils.js +11 -0
- package/kanban/client/src/main.jsx +10 -0
- package/kanban/client/src/store/__tests__/kanbanStore.test.js +164 -0
- package/kanban/client/src/store/ceremonyStore.js +172 -0
- package/kanban/client/src/store/filterStore.js +201 -0
- package/kanban/client/src/store/kanbanStore.js +123 -0
- package/kanban/client/src/store/processStore.js +65 -0
- package/kanban/client/src/store/sprintPlanningStore.js +33 -0
- package/kanban/client/src/styles/globals.css +59 -0
- package/kanban/client/tailwind.config.js +77 -0
- package/kanban/client/vite.config.js +28 -0
- package/kanban/client/vitest.config.js +28 -0
- package/kanban/dev-start.sh +47 -0
- package/kanban/package.json +12 -0
- package/kanban/server/index.js +537 -0
- package/kanban/server/routes/ceremony.js +454 -0
- package/kanban/server/routes/costs.js +163 -0
- package/kanban/server/routes/openai-oauth.js +366 -0
- package/kanban/server/routes/processes.js +50 -0
- package/kanban/server/routes/settings.js +736 -0
- package/kanban/server/routes/websocket.js +281 -0
- package/kanban/server/routes/work-items.js +487 -0
- package/kanban/server/services/CeremonyService.js +1441 -0
- package/kanban/server/services/FileSystemScanner.js +95 -0
- package/kanban/server/services/FileWatcher.js +144 -0
- package/kanban/server/services/HierarchyBuilder.js +196 -0
- package/kanban/server/services/ProcessRegistry.js +122 -0
- package/kanban/server/services/TaskRunnerService.js +261 -0
- package/kanban/server/services/WorkItemReader.js +123 -0
- package/kanban/server/services/WorkItemRefineService.js +510 -0
- package/kanban/server/start.js +49 -0
- package/kanban/server/utils/kanban-logger.js +132 -0
- package/kanban/server/utils/markdown.js +91 -0
- package/kanban/server/utils/status-grouping.js +107 -0
- package/kanban/server/workers/run-task-worker.js +121 -0
- package/kanban/server/workers/seed-worker.js +94 -0
- package/kanban/server/workers/sponsor-call-worker.js +92 -0
- package/kanban/server/workers/sprint-planning-worker.js +212 -0
- package/package.json +19 -7
- package/cli/agents/documentation.md +0 -302
|
@@ -0,0 +1,270 @@
|
|
|
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 || process.env.OPENAI_OAUTH_TOKEN),
|
|
50
|
+
'GEMINI_API_KEY': !!process.env.GEMINI_API_KEY,
|
|
51
|
+
'XIAOMI_API_KEY': !!process.env.XIAOMI_API_KEY,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
for (const [key, available] of Object.entries(keys)) {
|
|
55
|
+
if (available) {
|
|
56
|
+
sendIndented(`${key}: Found`, 1);
|
|
57
|
+
} else {
|
|
58
|
+
sendIndented(`${key}: Not found`, 1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const availableCount = Object.values(keys).filter(v => v).length;
|
|
63
|
+
|
|
64
|
+
sendOutput(`\n ${availableCount}/3 providers available`);
|
|
65
|
+
|
|
66
|
+
if (availableCount === 0) {
|
|
67
|
+
sendError('No API keys found. Please add at least one to your .env file.');
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return availableCount;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Display prompt statistics
|
|
76
|
+
*/
|
|
77
|
+
function displayPromptStats() {
|
|
78
|
+
const stats = getPromptStats();
|
|
79
|
+
|
|
80
|
+
sendSectionHeader('Evaluation Overview');
|
|
81
|
+
sendIndented(`Total prompts: ${stats.totalPrompts}`, 1);
|
|
82
|
+
sendIndented(`Ceremonies: ${stats.ceremonies} (${stats.ceremonyList.join(', ')})`, 1);
|
|
83
|
+
sendIndented(`Estimated total API calls: ${stats.estimatedTotalCalls} per provider`, 1);
|
|
84
|
+
sendIndented('Impact distribution:', 1);
|
|
85
|
+
sendIndented(`- CRITICAL: ${stats.impactDistribution.CRITICAL}`, 2);
|
|
86
|
+
sendIndented(`- VERY HIGH: ${stats.impactDistribution['VERY HIGH']}`, 2);
|
|
87
|
+
sendIndented(`- HIGH: ${stats.impactDistribution.HIGH}`, 2);
|
|
88
|
+
sendIndented(`- MEDIUM: ${stats.impactDistribution.MEDIUM}`, 2);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Progress callback for query engine
|
|
93
|
+
*/
|
|
94
|
+
function createProgressCallback() {
|
|
95
|
+
let currentPrompt = 0;
|
|
96
|
+
|
|
97
|
+
return (progress) => {
|
|
98
|
+
if (progress.type === 'prompt-start') {
|
|
99
|
+
currentPrompt = progress.current;
|
|
100
|
+
sendOutput(`\n[${progress.current}/${progress.total}] Processing: ${progress.ceremony}/${progress.stage}`);
|
|
101
|
+
} else if (progress.type === 'prompt-complete') {
|
|
102
|
+
const { successCount, failureCount, totalTime } = progress;
|
|
103
|
+
sendIndented(`Complete: ${successCount} success, ${failureCount} failed (${totalTime.toFixed(1)}s)`, 1);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Write JSON output file
|
|
110
|
+
*/
|
|
111
|
+
function writeJSONOutput(data, outputPath) {
|
|
112
|
+
sendInfo(`Writing JSON output to ${outputPath}...`);
|
|
113
|
+
fs.writeFileSync(outputPath, JSON.stringify(data, null, 2), 'utf-8');
|
|
114
|
+
sendIndented('JSON file written', 1);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Write Markdown output file
|
|
119
|
+
*/
|
|
120
|
+
function writeMarkdownOutput(content, outputPath) {
|
|
121
|
+
sendInfo(`Writing Markdown report to ${outputPath}...`);
|
|
122
|
+
fs.writeFileSync(outputPath, content, 'utf-8');
|
|
123
|
+
sendIndented('Markdown report written', 1);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Display summary
|
|
128
|
+
*/
|
|
129
|
+
function displaySummary(summary) {
|
|
130
|
+
console.log('\n╔══════════════════════════════════════════════════════════════╗');
|
|
131
|
+
console.log('║ Execution Summary ║');
|
|
132
|
+
console.log('╚══════════════════════════════════════════════════════════════╝\n');
|
|
133
|
+
|
|
134
|
+
console.log(`Total prompts evaluated: ${summary.totalPrompts}`);
|
|
135
|
+
console.log(`Successful queries: ${summary.successfulQueries}`);
|
|
136
|
+
console.log(`Failed queries: ${summary.failedQueries}`);
|
|
137
|
+
console.log(`\nExecution time: ${summary.executionTime}`);
|
|
138
|
+
|
|
139
|
+
console.log('\nCost breakdown:');
|
|
140
|
+
for (const [provider, cost] of Object.entries(summary.totalCost)) {
|
|
141
|
+
if (provider !== 'total') {
|
|
142
|
+
console.log(` ${provider}: $${cost.toFixed(4)}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
console.log(` TOTAL: $${summary.totalCost.total.toFixed(4)}`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Display analysis summary
|
|
150
|
+
*/
|
|
151
|
+
function displayAnalysisSummary(statistics) {
|
|
152
|
+
console.log('\n╔══════════════════════════════════════════════════════════════╗');
|
|
153
|
+
console.log('║ Analysis Summary ║');
|
|
154
|
+
console.log('╚══════════════════════════════════════════════════════════════╝\n');
|
|
155
|
+
|
|
156
|
+
console.log('Consensus Statistics:');
|
|
157
|
+
console.log(` Full consensus: ${statistics.consensus.full} (${statistics.consensusRate.full})`);
|
|
158
|
+
console.log(` Partial consensus: ${statistics.consensus.partial} (${statistics.consensusRate.partial})`);
|
|
159
|
+
console.log(` No consensus: ${statistics.consensus.none} (${statistics.consensusRate.none})`);
|
|
160
|
+
|
|
161
|
+
console.log('\nDefault Alignment:');
|
|
162
|
+
console.log(` Perfect: ${statistics.defaultAlignment.perfect} stages`);
|
|
163
|
+
console.log(` Partial: ${statistics.defaultAlignment.partial} stages`);
|
|
164
|
+
console.log(` None: ${statistics.defaultAlignment.none} stages`);
|
|
165
|
+
|
|
166
|
+
console.log('\nRecommendations:');
|
|
167
|
+
console.log(` Upgrade suggestions: ${statistics.upgradeRecommendations} stages`);
|
|
168
|
+
console.log(` Downgrade suggestions: ${statistics.downgradeRecommendations} stages`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Main execution function
|
|
173
|
+
*/
|
|
174
|
+
async function main() {
|
|
175
|
+
try {
|
|
176
|
+
displayBanner();
|
|
177
|
+
|
|
178
|
+
// Check environment
|
|
179
|
+
const availableProviders = displayEnvironmentCheck();
|
|
180
|
+
|
|
181
|
+
// Display stats
|
|
182
|
+
displayPromptStats();
|
|
183
|
+
|
|
184
|
+
// Confirm execution
|
|
185
|
+
sendWarning('This will execute multiple API calls to all available providers.');
|
|
186
|
+
sendIndented('Estimated cost: $0.15-0.30 depending on available providers.', 1);
|
|
187
|
+
sendIndented('Press Ctrl+C to cancel, or wait 5 seconds to continue...', 1);
|
|
188
|
+
|
|
189
|
+
// Wait 5 seconds for user to cancel
|
|
190
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
191
|
+
|
|
192
|
+
sendInfo('Starting evaluation...');
|
|
193
|
+
|
|
194
|
+
// Initialize query engine
|
|
195
|
+
const engine = new ModelQueryEngine();
|
|
196
|
+
|
|
197
|
+
sendInfo('Initializing providers...');
|
|
198
|
+
const initResult = await engine.initializeProviders();
|
|
199
|
+
|
|
200
|
+
sendIndented(`${initResult.readyCount} provider(s) initialized`, 1);
|
|
201
|
+
|
|
202
|
+
if (initResult.errors) {
|
|
203
|
+
sendWarning('Some providers failed to initialize:');
|
|
204
|
+
for (const error of initResult.errors) {
|
|
205
|
+
sendIndented(`- ${error.provider}: ${error.error}`, 1);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Run evaluation
|
|
210
|
+
sendInfo('Querying providers (this may take several minutes)...');
|
|
211
|
+
|
|
212
|
+
const progressCallback = createProgressCallback();
|
|
213
|
+
const results = await engine.evaluateAll(EVALUATION_PROMPTS, progressCallback);
|
|
214
|
+
|
|
215
|
+
sendSuccess('All evaluations complete!');
|
|
216
|
+
|
|
217
|
+
// Analyze results
|
|
218
|
+
sendInfo('Analyzing results...');
|
|
219
|
+
const analyzer = new ModelRecommendationAnalyzer(results.evaluations);
|
|
220
|
+
const analysis = analyzer.analyze();
|
|
221
|
+
sendIndented('Analysis complete', 1);
|
|
222
|
+
|
|
223
|
+
// Ensure _temp directory exists
|
|
224
|
+
const tempDir = ensureTempDir();
|
|
225
|
+
|
|
226
|
+
// Write JSON output
|
|
227
|
+
const jsonPath = path.join(tempDir, 'model-recommendations.json');
|
|
228
|
+
const jsonOutput = {
|
|
229
|
+
...results,
|
|
230
|
+
analysis: analysis.analyses,
|
|
231
|
+
statistics: analysis.statistics
|
|
232
|
+
};
|
|
233
|
+
writeJSONOutput(jsonOutput, jsonPath);
|
|
234
|
+
|
|
235
|
+
// Write Markdown report
|
|
236
|
+
const markdownPath = path.join(tempDir, 'MODEL_RECOMMENDATIONS_REPORT.md');
|
|
237
|
+
const markdownReport = analyzer.generateMarkdownReport();
|
|
238
|
+
writeMarkdownOutput(markdownReport, markdownPath);
|
|
239
|
+
|
|
240
|
+
// Display summaries
|
|
241
|
+
displaySummary(results.summary);
|
|
242
|
+
displayAnalysisSummary(analysis.statistics);
|
|
243
|
+
|
|
244
|
+
// Final message
|
|
245
|
+
sendOutput('\n╔══════════════════════════════════════════════════════════════╗');
|
|
246
|
+
sendOutput('║ Evaluation Complete! ║');
|
|
247
|
+
sendOutput('╚══════════════════════════════════════════════════════════════╝');
|
|
248
|
+
sendSectionHeader('Output files');
|
|
249
|
+
sendIndented(`- JSON: ${jsonPath}`, 1);
|
|
250
|
+
sendIndented(`- Report: ${markdownPath}`, 1);
|
|
251
|
+
|
|
252
|
+
sendSectionHeader('Next steps');
|
|
253
|
+
sendIndented('1. Review the markdown report for provider recommendations', 1);
|
|
254
|
+
sendIndented('2. Compare consensus vs current defaults', 1);
|
|
255
|
+
sendIndented('3. Consider cost vs quality trade-offs', 1);
|
|
256
|
+
sendIndented('4. Update model configurations in AVC if desired', 1);
|
|
257
|
+
|
|
258
|
+
} catch (error) {
|
|
259
|
+
sendError(`ERROR: ${error.message}`);
|
|
260
|
+
sendOutput(`\nStack trace: ${error.stack}`);
|
|
261
|
+
process.exit(1);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Run if executed directly
|
|
266
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
267
|
+
main();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
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();
|
package/cli/process-manager.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spawn } from 'child_process';
|
|
1
|
+
import { spawn, fork } from 'child_process';
|
|
2
2
|
import { EventEmitter } from 'events';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -73,6 +73,63 @@ export class BackgroundProcessManager extends EventEmitter {
|
|
|
73
73
|
return id;
|
|
74
74
|
}
|
|
75
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
|
+
|
|
76
133
|
/**
|
|
77
134
|
* Stop a running process
|
|
78
135
|
*/
|
|
@@ -175,7 +232,12 @@ export class BackgroundProcessManager extends EventEmitter {
|
|
|
175
232
|
|
|
176
233
|
metadata.exitCode = code;
|
|
177
234
|
metadata.exitSignal = signal;
|
|
178
|
-
|
|
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
|
+
}
|
|
179
241
|
|
|
180
242
|
this.emit('process-exited', {
|
|
181
243
|
id,
|
|
@@ -184,6 +246,15 @@ export class BackgroundProcessManager extends EventEmitter {
|
|
|
184
246
|
signal,
|
|
185
247
|
status: metadata.status
|
|
186
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);
|
|
187
258
|
}
|
|
188
259
|
|
|
189
260
|
/**
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
export class PromptLogger {
|
|
5
|
+
/**
|
|
6
|
+
* @param {string} projectRoot - absolute path to .avc parent
|
|
7
|
+
* @param {string} ceremony - e.g. 'sprint-planning', 'sponsor-call'
|
|
8
|
+
*/
|
|
9
|
+
constructor(projectRoot, ceremony) {
|
|
10
|
+
this.ceremony = ceremony;
|
|
11
|
+
this.callCount = 0;
|
|
12
|
+
this.runDir = null;
|
|
13
|
+
|
|
14
|
+
const avcDir = path.join(projectRoot, '.avc');
|
|
15
|
+
if (!fs.existsSync(avcDir)) return; // not an AVC project — silently skip
|
|
16
|
+
|
|
17
|
+
const runTs = new Date().toISOString().replace(/:/g, '-').replace(/\..+/, '');
|
|
18
|
+
this.runDir = path.join(avcDir, 'logs', 'prompts', `${ceremony}-${runTs}`);
|
|
19
|
+
fs.mkdirSync(this.runDir, { recursive: true });
|
|
20
|
+
|
|
21
|
+
this._pruneOldRuns(path.join(avcDir, 'logs', 'prompts'), ceremony, 5);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Write one call record. Called by _trackTokens in LLMProvider.
|
|
26
|
+
* @param {object} payload
|
|
27
|
+
*/
|
|
28
|
+
write(payload) {
|
|
29
|
+
if (!this.runDir) return;
|
|
30
|
+
this.callCount += 1;
|
|
31
|
+
const seq = String(this.callCount).padStart(3, '0');
|
|
32
|
+
const stage = (payload.stage || 'unknown').replace(/\s+/g, '-');
|
|
33
|
+
const timeStr = new Date().toISOString().substring(11, 19).replace(/:/g, '-');
|
|
34
|
+
const filename = `${seq}-${stage}-${timeStr}.json`;
|
|
35
|
+
try {
|
|
36
|
+
fs.writeFileSync(
|
|
37
|
+
path.join(this.runDir, filename),
|
|
38
|
+
JSON.stringify(payload, null, 2),
|
|
39
|
+
'utf8'
|
|
40
|
+
);
|
|
41
|
+
} catch { /* non-fatal */ }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
_pruneOldRuns(promptsDir, ceremony, keep) {
|
|
45
|
+
if (!fs.existsSync(promptsDir)) return;
|
|
46
|
+
const dirs = fs.readdirSync(promptsDir)
|
|
47
|
+
.filter(d => d.startsWith(`${ceremony}-`))
|
|
48
|
+
.map(d => ({ name: d, full: path.join(promptsDir, d) }))
|
|
49
|
+
.sort((a, b) => a.name.localeCompare(b.name)); // oldest first
|
|
50
|
+
while (dirs.length >= keep) {
|
|
51
|
+
const oldest = dirs.shift();
|
|
52
|
+
try {
|
|
53
|
+
fs.rmSync(oldest.full, { recursive: true, force: true });
|
|
54
|
+
} catch { /* non-fatal */ }
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|