@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.
- package/README.md +2 -0
- package/cli/agent-loader.js +21 -0
- package/cli/agents/agent-selector.md +129 -0
- package/cli/agents/architecture-recommender.md +418 -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/documentation-updater.md +203 -0
- package/cli/agents/epic-story-decomposer.md +280 -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 +79 -0
- package/cli/agents/mission-scope-validator.md +112 -0
- package/cli/agents/project-context-extractor.md +107 -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/solver-epic-api.json +15 -0
- package/cli/agents/solver-epic-api.md +39 -0
- package/cli/agents/solver-epic-backend.json +15 -0
- package/cli/agents/solver-epic-backend.md +39 -0
- package/cli/agents/solver-epic-cloud.json +15 -0
- package/cli/agents/solver-epic-cloud.md +39 -0
- package/cli/agents/solver-epic-data.json +15 -0
- package/cli/agents/solver-epic-data.md +39 -0
- package/cli/agents/solver-epic-database.json +15 -0
- package/cli/agents/solver-epic-database.md +39 -0
- package/cli/agents/solver-epic-developer.json +15 -0
- package/cli/agents/solver-epic-developer.md +39 -0
- package/cli/agents/solver-epic-devops.json +15 -0
- package/cli/agents/solver-epic-devops.md +39 -0
- package/cli/agents/solver-epic-frontend.json +15 -0
- package/cli/agents/solver-epic-frontend.md +39 -0
- package/cli/agents/solver-epic-mobile.json +15 -0
- package/cli/agents/solver-epic-mobile.md +39 -0
- package/cli/agents/solver-epic-qa.json +15 -0
- package/cli/agents/solver-epic-qa.md +39 -0
- package/cli/agents/solver-epic-security.json +15 -0
- package/cli/agents/solver-epic-security.md +39 -0
- package/cli/agents/solver-epic-solution-architect.json +15 -0
- package/cli/agents/solver-epic-solution-architect.md +39 -0
- package/cli/agents/solver-epic-test-architect.json +15 -0
- package/cli/agents/solver-epic-test-architect.md +39 -0
- package/cli/agents/solver-epic-ui.json +15 -0
- package/cli/agents/solver-epic-ui.md +39 -0
- package/cli/agents/solver-epic-ux.json +15 -0
- package/cli/agents/solver-epic-ux.md +39 -0
- package/cli/agents/solver-story-api.json +15 -0
- package/cli/agents/solver-story-api.md +39 -0
- package/cli/agents/solver-story-backend.json +15 -0
- package/cli/agents/solver-story-backend.md +39 -0
- package/cli/agents/solver-story-cloud.json +15 -0
- package/cli/agents/solver-story-cloud.md +39 -0
- package/cli/agents/solver-story-data.json +15 -0
- package/cli/agents/solver-story-data.md +39 -0
- package/cli/agents/solver-story-database.json +15 -0
- package/cli/agents/solver-story-database.md +39 -0
- package/cli/agents/solver-story-developer.json +15 -0
- package/cli/agents/solver-story-developer.md +39 -0
- package/cli/agents/solver-story-devops.json +15 -0
- package/cli/agents/solver-story-devops.md +39 -0
- package/cli/agents/solver-story-frontend.json +15 -0
- package/cli/agents/solver-story-frontend.md +39 -0
- package/cli/agents/solver-story-mobile.json +15 -0
- package/cli/agents/solver-story-mobile.md +39 -0
- package/cli/agents/solver-story-qa.json +15 -0
- package/cli/agents/solver-story-qa.md +39 -0
- package/cli/agents/solver-story-security.json +15 -0
- package/cli/agents/solver-story-security.md +39 -0
- package/cli/agents/solver-story-solution-architect.json +15 -0
- package/cli/agents/solver-story-solution-architect.md +39 -0
- package/cli/agents/solver-story-test-architect.json +15 -0
- package/cli/agents/solver-story-test-architect.md +39 -0
- package/cli/agents/solver-story-ui.json +15 -0
- package/cli/agents/solver-story-ui.md +39 -0
- package/cli/agents/solver-story-ux.json +15 -0
- package/cli/agents/solver-story-ux.md +39 -0
- package/cli/agents/story-doc-enricher.md +133 -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 +152 -0
- package/cli/agents/validator-documentation.md +453 -0
- package/cli/agents/validator-epic-api.json +93 -0
- package/cli/agents/validator-epic-api.md +137 -0
- package/cli/agents/validator-epic-backend.json +93 -0
- package/cli/agents/validator-epic-backend.md +130 -0
- package/cli/agents/validator-epic-cloud.json +93 -0
- package/cli/agents/validator-epic-cloud.md +137 -0
- package/cli/agents/validator-epic-data.json +93 -0
- package/cli/agents/validator-epic-data.md +130 -0
- package/cli/agents/validator-epic-database.json +93 -0
- package/cli/agents/validator-epic-database.md +137 -0
- package/cli/agents/validator-epic-developer.json +74 -0
- package/cli/agents/validator-epic-developer.md +153 -0
- package/cli/agents/validator-epic-devops.json +74 -0
- package/cli/agents/validator-epic-devops.md +153 -0
- package/cli/agents/validator-epic-frontend.json +74 -0
- package/cli/agents/validator-epic-frontend.md +153 -0
- package/cli/agents/validator-epic-mobile.json +93 -0
- package/cli/agents/validator-epic-mobile.md +130 -0
- package/cli/agents/validator-epic-qa.json +93 -0
- package/cli/agents/validator-epic-qa.md +130 -0
- package/cli/agents/validator-epic-security.json +74 -0
- package/cli/agents/validator-epic-security.md +154 -0
- package/cli/agents/validator-epic-solution-architect.json +74 -0
- package/cli/agents/validator-epic-solution-architect.md +156 -0
- package/cli/agents/validator-epic-test-architect.json +93 -0
- package/cli/agents/validator-epic-test-architect.md +130 -0
- package/cli/agents/validator-epic-ui.json +93 -0
- package/cli/agents/validator-epic-ui.md +130 -0
- package/cli/agents/validator-epic-ux.json +93 -0
- package/cli/agents/validator-epic-ux.md +130 -0
- package/cli/agents/validator-selector.md +211 -0
- package/cli/agents/validator-story-api.json +104 -0
- package/cli/agents/validator-story-api.md +152 -0
- package/cli/agents/validator-story-backend.json +104 -0
- package/cli/agents/validator-story-backend.md +152 -0
- package/cli/agents/validator-story-cloud.json +104 -0
- package/cli/agents/validator-story-cloud.md +152 -0
- package/cli/agents/validator-story-data.json +104 -0
- package/cli/agents/validator-story-data.md +152 -0
- package/cli/agents/validator-story-database.json +104 -0
- package/cli/agents/validator-story-database.md +152 -0
- package/cli/agents/validator-story-developer.json +104 -0
- package/cli/agents/validator-story-developer.md +152 -0
- package/cli/agents/validator-story-devops.json +104 -0
- package/cli/agents/validator-story-devops.md +152 -0
- package/cli/agents/validator-story-frontend.json +104 -0
- package/cli/agents/validator-story-frontend.md +152 -0
- package/cli/agents/validator-story-mobile.json +104 -0
- package/cli/agents/validator-story-mobile.md +152 -0
- package/cli/agents/validator-story-qa.json +104 -0
- package/cli/agents/validator-story-qa.md +152 -0
- package/cli/agents/validator-story-security.json +104 -0
- package/cli/agents/validator-story-security.md +152 -0
- package/cli/agents/validator-story-solution-architect.json +104 -0
- package/cli/agents/validator-story-solution-architect.md +152 -0
- package/cli/agents/validator-story-test-architect.json +104 -0
- package/cli/agents/validator-story-test-architect.md +152 -0
- package/cli/agents/validator-story-ui.json +104 -0
- package/cli/agents/validator-story-ui.md +152 -0
- package/cli/agents/validator-story-ux.json +104 -0
- package/cli/agents/validator-story-ux.md +152 -0
- package/cli/ansi-colors.js +21 -0
- package/cli/build-docs.js +298 -0
- package/cli/ceremony-history.js +369 -0
- package/cli/command-logger.js +245 -0
- package/cli/components/static-output.js +63 -0
- package/cli/console-output-manager.js +94 -0
- package/cli/docs-sync.js +306 -0
- package/cli/epic-story-validator.js +1174 -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/index.js +3 -25
- package/cli/init-model-config.js +697 -0
- package/cli/init.js +1765 -100
- package/cli/kanban-server-manager.js +228 -0
- package/cli/llm-claude.js +109 -0
- package/cli/llm-gemini.js +115 -0
- package/cli/llm-mock.js +233 -0
- package/cli/llm-openai.js +233 -0
- package/cli/llm-provider.js +300 -0
- package/cli/llm-token-limits.js +102 -0
- package/cli/llm-verifier.js +454 -0
- package/cli/logger.js +32 -5
- package/cli/message-constants.js +58 -0
- package/cli/message-manager.js +334 -0
- package/cli/message-types.js +96 -0
- package/cli/messaging-api.js +297 -0
- package/cli/model-pricing.js +169 -0
- package/cli/model-query-engine.js +468 -0
- package/cli/model-recommendation-analyzer.js +495 -0
- package/cli/model-selector.js +269 -0
- package/cli/output-buffer.js +107 -0
- package/cli/process-manager.js +332 -0
- package/cli/repl-ink.js +5840 -504
- package/cli/repl-old.js +4 -4
- package/cli/seed-processor.js +792 -0
- package/cli/sprint-planning-processor.js +1813 -0
- package/cli/template-processor.js +2306 -108
- package/cli/templates/project.md +25 -8
- package/cli/templates/vitepress-config.mts.template +34 -0
- package/cli/token-tracker.js +520 -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 +605 -0
- package/cli/verification-tracker.js +563 -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-CiD8PS2e.js +306 -0
- package/kanban/client/dist/assets/index-nLh0m82Q.css +1 -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 +622 -0
- package/kanban/client/src/components/ProjectFileEditorPopup.jsx +117 -0
- package/kanban/client/src/components/ceremony/AskArchPopup.jsx +416 -0
- package/kanban/client/src/components/ceremony/AskModelPopup.jsx +616 -0
- package/kanban/client/src/components/ceremony/CeremonyWorkflowModal.jsx +946 -0
- package/kanban/client/src/components/ceremony/EpicStorySelectionModal.jsx +254 -0
- package/kanban/client/src/components/ceremony/SponsorCallModal.jsx +619 -0
- package/kanban/client/src/components/ceremony/SprintPlanningModal.jsx +704 -0
- package/kanban/client/src/components/ceremony/steps/ArchitectureStep.jsx +150 -0
- package/kanban/client/src/components/ceremony/steps/CompleteStep.jsx +154 -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 +125 -0
- package/kanban/client/src/components/ceremony/steps/RunningStep.jsx +228 -0
- package/kanban/client/src/components/kanban/CardDetailModal.jsx +559 -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 +57 -0
- package/kanban/client/src/components/kanban/KanbanBoard.jsx +211 -0
- package/kanban/client/src/components/kanban/KanbanCard.jsx +138 -0
- package/kanban/client/src/components/kanban/KanbanColumn.jsx +90 -0
- package/kanban/client/src/components/kanban/RefineWorkItemPopup.jsx +789 -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 +353 -0
- package/kanban/client/src/components/settings/ApiKeysTab.jsx +113 -0
- package/kanban/client/src/components/settings/CeremonyModelsTab.jsx +98 -0
- package/kanban/client/src/components/settings/CostThresholdsTab.jsx +94 -0
- package/kanban/client/src/components/settings/ModelPricingTab.jsx +204 -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 +353 -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 +118 -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 +401 -0
- package/kanban/client/src/lib/status-grouping.js +144 -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 +115 -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 +516 -0
- package/kanban/server/routes/ceremony.js +305 -0
- package/kanban/server/routes/costs.js +157 -0
- package/kanban/server/routes/processes.js +50 -0
- package/kanban/server/routes/settings.js +303 -0
- package/kanban/server/routes/websocket.js +276 -0
- package/kanban/server/routes/work-items.js +347 -0
- package/kanban/server/services/CeremonyService.js +1190 -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/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/sponsor-call-worker.js +84 -0
- package/kanban/server/workers/sprint-planning-worker.js +130 -0
- package/package.json +34 -7
|
@@ -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
|
+
}
|