@agile-vibe-coding/avc 0.2.3 → 0.3.2
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 +475 -3
- package/cli/agents/agent-selector.md +23 -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/doc-writer-epic.md +42 -0
- package/cli/agents/doc-writer-story.md +43 -0
- package/cli/agents/duplicate-detector.md +110 -0
- package/cli/agents/epic-story-decomposer.md +318 -39
- package/cli/agents/mission-scope-generator.md +68 -4
- package/cli/agents/mission-scope-validator.md +40 -6
- package/cli/agents/project-context-extractor.md +21 -6
- package/cli/agents/scaffolding-generator.md +99 -0
- package/cli/agents/seed-validator.md +71 -0
- package/cli/agents/story-scope-reviewer.md +147 -0
- package/cli/agents/story-splitter.md +83 -0
- package/cli/agents/validator-documentation.json +31 -0
- package/cli/agents/validator-documentation.md +3 -1
- package/cli/api-reference-tool.js +368 -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/dependency-checker.js +72 -0
- package/cli/epic-story-validator.js +284 -799
- package/cli/index.js +0 -0
- package/cli/init-model-config.js +17 -10
- package/cli/init.js +514 -92
- package/cli/kanban-server-manager.js +1 -2
- package/cli/llm-claude.js +98 -31
- package/cli/llm-gemini.js +29 -5
- package/cli/llm-local.js +493 -0
- package/cli/llm-openai.js +262 -41
- package/cli/llm-provider.js +147 -8
- package/cli/llm-token-limits.js +113 -4
- package/cli/llm-verifier.js +209 -1
- package/cli/llm-xiaomi.js +143 -0
- package/cli/message-constants.js +3 -12
- package/cli/messaging-api.js +6 -12
- 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 +23 -0
- package/cli/model-selector.js +3 -2
- package/cli/prompt-logger.js +57 -0
- package/cli/repl-ink.js +106 -346
- package/cli/repl-old.js +1 -2
- package/cli/seed-processor.js +194 -24
- package/cli/sprint-planning-processor.js +2638 -289
- package/cli/template-processor.js +50 -3
- package/cli/token-tracker.js +50 -23
- package/cli/tools/generate-story-validators.js +1 -1
- package/cli/validation-router.js +70 -8
- package/cli/worktree-runner.js +654 -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 +2 -2
- package/kanban/client/src/App.jsx +43 -14
- package/kanban/client/src/components/ceremony/AskArchPopup.jsx +7 -3
- package/kanban/client/src/components/ceremony/AskModelPopup.jsx +23 -10
- package/kanban/client/src/components/ceremony/CeremonyWorkflowModal.jsx +320 -133
- package/kanban/client/src/components/ceremony/ProviderSwitcherButton.jsx +290 -0
- package/kanban/client/src/components/ceremony/SponsorCallModal.jsx +80 -13
- package/kanban/client/src/components/ceremony/SprintPlanningModal.jsx +156 -22
- package/kanban/client/src/components/ceremony/steps/ArchitectureStep.jsx +11 -11
- package/kanban/client/src/components/ceremony/steps/CompleteStep.jsx +3 -21
- package/kanban/client/src/components/ceremony/steps/ReviewAnswersStep.jsx +214 -10
- package/kanban/client/src/components/ceremony/steps/RunningStep.jsx +23 -2
- package/kanban/client/src/components/kanban/CardDetailModal.jsx +97 -10
- package/kanban/client/src/components/kanban/GroupingSelector.jsx +7 -1
- package/kanban/client/src/components/kanban/KanbanCard.jsx +23 -14
- package/kanban/client/src/components/kanban/RefineWorkItemPopup.jsx +9 -14
- 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/settings/AgentsTab.jsx +103 -75
- package/kanban/client/src/components/settings/ApiKeysTab.jsx +31 -2
- package/kanban/client/src/components/settings/CeremonyModelsTab.jsx +9 -2
- package/kanban/client/src/components/settings/CheckEditorPopup.jsx +507 -0
- package/kanban/client/src/components/settings/CostThresholdsTab.jsx +3 -2
- package/kanban/client/src/components/settings/ModelPricingTab.jsx +72 -7
- package/kanban/client/src/components/settings/OpenAIAuthSection.jsx +412 -0
- package/kanban/client/src/components/settings/SettingsModal.jsx +4 -4
- package/kanban/client/src/components/stats/CostModal.jsx +34 -3
- package/kanban/client/src/hooks/useGrouping.js +59 -0
- package/kanban/client/src/lib/api.js +118 -4
- package/kanban/client/src/lib/status-grouping.js +10 -0
- package/kanban/client/src/store/kanbanStore.js +8 -0
- package/kanban/server/index.js +23 -2
- package/kanban/server/routes/ceremony.js +153 -4
- package/kanban/server/routes/costs.js +9 -3
- package/kanban/server/routes/openai-oauth.js +366 -0
- package/kanban/server/routes/settings.js +447 -14
- package/kanban/server/routes/websocket.js +7 -2
- package/kanban/server/routes/work-items.js +141 -1
- package/kanban/server/services/CeremonyService.js +275 -24
- package/kanban/server/services/TaskRunnerService.js +261 -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 +14 -6
- package/kanban/server/workers/sprint-planning-worker.js +94 -12
- package/package.json +2 -3
- package/cli/agents/solver-epic-api.json +0 -15
- package/cli/agents/solver-epic-api.md +0 -39
- package/cli/agents/solver-epic-backend.json +0 -15
- package/cli/agents/solver-epic-backend.md +0 -39
- package/cli/agents/solver-epic-cloud.json +0 -15
- package/cli/agents/solver-epic-cloud.md +0 -39
- package/cli/agents/solver-epic-data.json +0 -15
- package/cli/agents/solver-epic-data.md +0 -39
- package/cli/agents/solver-epic-database.json +0 -15
- package/cli/agents/solver-epic-database.md +0 -39
- package/cli/agents/solver-epic-developer.json +0 -15
- package/cli/agents/solver-epic-developer.md +0 -39
- package/cli/agents/solver-epic-devops.json +0 -15
- package/cli/agents/solver-epic-devops.md +0 -39
- package/cli/agents/solver-epic-frontend.json +0 -15
- package/cli/agents/solver-epic-frontend.md +0 -39
- package/cli/agents/solver-epic-mobile.json +0 -15
- package/cli/agents/solver-epic-mobile.md +0 -39
- package/cli/agents/solver-epic-qa.json +0 -15
- package/cli/agents/solver-epic-qa.md +0 -39
- package/cli/agents/solver-epic-security.json +0 -15
- package/cli/agents/solver-epic-security.md +0 -39
- package/cli/agents/solver-epic-solution-architect.json +0 -15
- package/cli/agents/solver-epic-solution-architect.md +0 -39
- package/cli/agents/solver-epic-test-architect.json +0 -15
- package/cli/agents/solver-epic-test-architect.md +0 -39
- package/cli/agents/solver-epic-ui.json +0 -15
- package/cli/agents/solver-epic-ui.md +0 -39
- package/cli/agents/solver-epic-ux.json +0 -15
- package/cli/agents/solver-epic-ux.md +0 -39
- package/cli/agents/solver-story-api.json +0 -15
- package/cli/agents/solver-story-api.md +0 -39
- package/cli/agents/solver-story-backend.json +0 -15
- package/cli/agents/solver-story-backend.md +0 -39
- package/cli/agents/solver-story-cloud.json +0 -15
- package/cli/agents/solver-story-cloud.md +0 -39
- package/cli/agents/solver-story-data.json +0 -15
- package/cli/agents/solver-story-data.md +0 -39
- package/cli/agents/solver-story-database.json +0 -15
- package/cli/agents/solver-story-database.md +0 -39
- package/cli/agents/solver-story-developer.json +0 -15
- package/cli/agents/solver-story-developer.md +0 -39
- package/cli/agents/solver-story-devops.json +0 -15
- package/cli/agents/solver-story-devops.md +0 -39
- package/cli/agents/solver-story-frontend.json +0 -15
- package/cli/agents/solver-story-frontend.md +0 -39
- package/cli/agents/solver-story-mobile.json +0 -15
- package/cli/agents/solver-story-mobile.md +0 -39
- package/cli/agents/solver-story-qa.json +0 -15
- package/cli/agents/solver-story-qa.md +0 -39
- package/cli/agents/solver-story-security.json +0 -15
- package/cli/agents/solver-story-security.md +0 -39
- package/cli/agents/solver-story-solution-architect.json +0 -15
- package/cli/agents/solver-story-solution-architect.md +0 -39
- package/cli/agents/solver-story-test-architect.json +0 -15
- package/cli/agents/solver-story-test-architect.md +0 -39
- package/cli/agents/solver-story-ui.json +0 -15
- package/cli/agents/solver-story-ui.md +0 -39
- package/cli/agents/solver-story-ux.json +0 -15
- package/cli/agents/solver-story-ux.md +0 -39
- package/cli/agents/validator-epic-api.json +0 -93
- package/cli/agents/validator-epic-api.md +0 -137
- package/cli/agents/validator-epic-backend.json +0 -93
- package/cli/agents/validator-epic-backend.md +0 -130
- package/cli/agents/validator-epic-cloud.json +0 -93
- package/cli/agents/validator-epic-cloud.md +0 -137
- package/cli/agents/validator-epic-data.json +0 -93
- package/cli/agents/validator-epic-data.md +0 -130
- package/cli/agents/validator-epic-database.json +0 -93
- package/cli/agents/validator-epic-database.md +0 -137
- package/cli/agents/validator-epic-developer.json +0 -74
- package/cli/agents/validator-epic-developer.md +0 -153
- package/cli/agents/validator-epic-devops.json +0 -74
- package/cli/agents/validator-epic-devops.md +0 -153
- package/cli/agents/validator-epic-frontend.json +0 -74
- package/cli/agents/validator-epic-frontend.md +0 -153
- package/cli/agents/validator-epic-mobile.json +0 -93
- package/cli/agents/validator-epic-mobile.md +0 -130
- package/cli/agents/validator-epic-qa.json +0 -93
- package/cli/agents/validator-epic-qa.md +0 -130
- package/cli/agents/validator-epic-security.json +0 -74
- package/cli/agents/validator-epic-security.md +0 -154
- package/cli/agents/validator-epic-solution-architect.json +0 -74
- package/cli/agents/validator-epic-solution-architect.md +0 -156
- package/cli/agents/validator-epic-test-architect.json +0 -93
- package/cli/agents/validator-epic-test-architect.md +0 -130
- package/cli/agents/validator-epic-ui.json +0 -93
- package/cli/agents/validator-epic-ui.md +0 -130
- package/cli/agents/validator-epic-ux.json +0 -93
- package/cli/agents/validator-epic-ux.md +0 -130
- package/cli/agents/validator-story-api.json +0 -104
- package/cli/agents/validator-story-api.md +0 -152
- package/cli/agents/validator-story-backend.json +0 -104
- package/cli/agents/validator-story-backend.md +0 -152
- package/cli/agents/validator-story-cloud.json +0 -104
- package/cli/agents/validator-story-cloud.md +0 -152
- package/cli/agents/validator-story-data.json +0 -104
- package/cli/agents/validator-story-data.md +0 -152
- package/cli/agents/validator-story-database.json +0 -104
- package/cli/agents/validator-story-database.md +0 -152
- package/cli/agents/validator-story-developer.json +0 -104
- package/cli/agents/validator-story-developer.md +0 -152
- package/cli/agents/validator-story-devops.json +0 -104
- package/cli/agents/validator-story-devops.md +0 -152
- package/cli/agents/validator-story-frontend.json +0 -104
- package/cli/agents/validator-story-frontend.md +0 -152
- package/cli/agents/validator-story-mobile.json +0 -104
- package/cli/agents/validator-story-mobile.md +0 -152
- package/cli/agents/validator-story-qa.json +0 -104
- package/cli/agents/validator-story-qa.md +0 -152
- package/cli/agents/validator-story-security.json +0 -104
- package/cli/agents/validator-story-security.md +0 -152
- package/cli/agents/validator-story-solution-architect.json +0 -104
- package/cli/agents/validator-story-solution-architect.md +0 -152
- package/cli/agents/validator-story-test-architect.json +0 -104
- package/cli/agents/validator-story-test-architect.md +0 -152
- package/cli/agents/validator-story-ui.json +0 -104
- package/cli/agents/validator-story-ui.md +0 -152
- package/cli/agents/validator-story-ux.json +0 -104
- package/cli/agents/validator-story-ux.md +0 -152
- package/kanban/client/dist/assets/index-CiD8PS2e.js +0 -306
- package/kanban/client/dist/assets/index-nLh0m82Q.css +0 -1
|
@@ -599,7 +599,16 @@ Please carefully follow the output format requirements to avoid these issues.
|
|
|
599
599
|
* @returns {Promise<LLMProvider>} LLM provider instance
|
|
600
600
|
*/
|
|
601
601
|
async getProviderForStageInstance(stageName) {
|
|
602
|
-
|
|
602
|
+
let { provider, model } = this.getProviderForStage(stageName);
|
|
603
|
+
|
|
604
|
+
// Resolve to an available provider if current one has no credentials
|
|
605
|
+
const resolved = await LLMProvider.resolveAvailableProvider(provider, model);
|
|
606
|
+
if (resolved.fellBack) {
|
|
607
|
+
debug(`Provider fallback for ${stageName}: ${provider}→${resolved.provider} (${resolved.model})`);
|
|
608
|
+
console.warn(`[WARN] ${provider} has no API key — falling back to ${resolved.provider} for stage "${stageName}"`);
|
|
609
|
+
provider = resolved.provider;
|
|
610
|
+
model = resolved.model;
|
|
611
|
+
}
|
|
603
612
|
|
|
604
613
|
// Check if we already have a provider for this stage
|
|
605
614
|
const cacheKey = `${stageName}:${provider}:${model}`;
|
|
@@ -627,7 +636,16 @@ Please carefully follow the output format requirements to avoid these issues.
|
|
|
627
636
|
* @returns {Promise<LLMProvider>} LLM provider instance
|
|
628
637
|
*/
|
|
629
638
|
async getValidationProviderForTypeInstance(validationType) {
|
|
630
|
-
|
|
639
|
+
let { provider, model } = this.getValidationProviderForType(validationType);
|
|
640
|
+
|
|
641
|
+
// Resolve to an available provider if current one has no credentials
|
|
642
|
+
const resolved = await LLMProvider.resolveAvailableProvider(provider, model);
|
|
643
|
+
if (resolved.fellBack) {
|
|
644
|
+
debug(`Validation provider fallback for ${validationType}: ${provider}→${resolved.provider} (${resolved.model})`);
|
|
645
|
+
console.warn(`[WARN] ${provider} has no API key — falling back to ${resolved.provider} for validation "${validationType}"`);
|
|
646
|
+
provider = resolved.provider;
|
|
647
|
+
model = resolved.model;
|
|
648
|
+
}
|
|
631
649
|
|
|
632
650
|
// Check if we already have a provider for this validation type
|
|
633
651
|
const cacheKey = `validation:${validationType}:${provider}:${model}`;
|
|
@@ -1531,7 +1549,7 @@ ${TECHNICAL_CONSIDERATIONS}
|
|
|
1531
1549
|
**Security and Compliance Requirements:**
|
|
1532
1550
|
${SECURITY_AND_COMPLIANCE_REQUIREMENTS}
|
|
1533
1551
|
|
|
1534
|
-
Decompose this project into Epics (
|
|
1552
|
+
Decompose this project into Epics (domain-based groupings) and Stories (user-facing capabilities per Epic) — create as many as needed to fully cover the scope.
|
|
1535
1553
|
|
|
1536
1554
|
Return your response as JSON following the exact structure specified in your instructions.`;
|
|
1537
1555
|
}
|
|
@@ -2564,6 +2582,35 @@ Return your response as JSON following the exact structure specified in your ins
|
|
|
2564
2582
|
'question prefilling'
|
|
2565
2583
|
);
|
|
2566
2584
|
|
|
2585
|
+
// Normalize common field name variants that local LLMs sometimes produce.
|
|
2586
|
+
// Maps known abbreviations/alternatives → canonical field names.
|
|
2587
|
+
const FIELD_ALIASES = {
|
|
2588
|
+
DEPLOY_TARGET: 'DEPLOYMENT_TARGET',
|
|
2589
|
+
DEPLOY: 'DEPLOYMENT_TARGET',
|
|
2590
|
+
DEPLOYMENT: 'DEPLOYMENT_TARGET',
|
|
2591
|
+
TARGET_DEPLOYMENT: 'DEPLOYMENT_TARGET',
|
|
2592
|
+
SECURITY_REQUIREMENTS: 'SECURITY_AND_COMPLIANCE_REQUIREMENTS',
|
|
2593
|
+
SECURITY_COMPLIANCE: 'SECURITY_AND_COMPLIANCE_REQUIREMENTS',
|
|
2594
|
+
COMPLIANCE_REQUIREMENTS: 'SECURITY_AND_COMPLIANCE_REQUIREMENTS',
|
|
2595
|
+
SECURITY: 'SECURITY_AND_COMPLIANCE_REQUIREMENTS',
|
|
2596
|
+
TECHNICAL: 'TECHNICAL_CONSIDERATIONS',
|
|
2597
|
+
TECH_CONSIDERATIONS: 'TECHNICAL_CONSIDERATIONS',
|
|
2598
|
+
TECH_STACK: 'TECHNICAL_CONSIDERATIONS',
|
|
2599
|
+
USERS: 'TARGET_USERS',
|
|
2600
|
+
TARGET_USER: 'TARGET_USERS',
|
|
2601
|
+
};
|
|
2602
|
+
|
|
2603
|
+
for (const [alias, canonical] of Object.entries(FIELD_ALIASES)) {
|
|
2604
|
+
if (result[alias] !== undefined) {
|
|
2605
|
+
if (!result[canonical] && result[alias]) {
|
|
2606
|
+
// Canonical missing or empty — use alias value
|
|
2607
|
+
debug(`Normalizing field name: ${alias} → ${canonical}`);
|
|
2608
|
+
result[canonical] = result[alias];
|
|
2609
|
+
}
|
|
2610
|
+
delete result[alias];
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
|
|
2567
2614
|
debug('Question prefilling received', {
|
|
2568
2615
|
hasTargetUsers: !!result.TARGET_USERS,
|
|
2569
2616
|
hasDeploymentTarget: !!result.DEPLOYMENT_TARGET,
|
package/cli/token-tracker.js
CHANGED
|
@@ -92,31 +92,38 @@ export class TokenTracker {
|
|
|
92
92
|
|
|
93
93
|
/**
|
|
94
94
|
* Calculate cost for token usage based on model pricing
|
|
95
|
-
* @param {number} inputTokens - Input tokens consumed
|
|
95
|
+
* @param {number} inputTokens - Input tokens consumed (includes cached tokens)
|
|
96
96
|
* @param {number} outputTokens - Output tokens consumed
|
|
97
97
|
* @param {string} modelId - Model identifier (e.g., 'claude-sonnet-4-5-20250929')
|
|
98
|
-
* @
|
|
98
|
+
* @param {number} cachedTokens - Subset of inputTokens served from cache (billed at inputCached rate)
|
|
99
|
+
* @returns {Object} Cost breakdown { input, output, total, saved }
|
|
99
100
|
*/
|
|
100
|
-
calculateCost(inputTokens, outputTokens, modelId) {
|
|
101
|
+
calculateCost(inputTokens, outputTokens, modelId, cachedTokens = 0) {
|
|
101
102
|
const config = this.readConfig();
|
|
102
103
|
const modelConfig = config.settings?.models?.[modelId];
|
|
103
104
|
|
|
104
105
|
if (!modelConfig || !modelConfig.pricing) {
|
|
105
106
|
// Model not found or no pricing - return zero cost
|
|
106
|
-
return { input: 0, output: 0, total: 0 };
|
|
107
|
+
return { input: 0, output: 0, total: 0, saved: 0 };
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
const pricing = modelConfig.pricing;
|
|
110
111
|
const divisor = pricing.unit === 'million' ? 1_000_000 : 1_000;
|
|
111
112
|
|
|
112
|
-
//
|
|
113
|
-
const
|
|
113
|
+
// Cached tokens billed at reduced rate; fall back to full input rate if not configured
|
|
114
|
+
const cachedRate = pricing.inputCached ?? pricing.input;
|
|
115
|
+
const nonCachedInput = Math.max(0, inputTokens - cachedTokens);
|
|
116
|
+
|
|
117
|
+
const inputCost = (pricing.input * nonCachedInput + cachedRate * cachedTokens) / divisor;
|
|
114
118
|
const outputCost = (pricing.output * outputTokens) / divisor;
|
|
119
|
+
// Savings vs. paying full rate for cached tokens
|
|
120
|
+
const saved = ((pricing.input - cachedRate) * cachedTokens) / divisor;
|
|
115
121
|
|
|
116
122
|
return {
|
|
117
123
|
input: inputCost,
|
|
118
124
|
output: outputCost,
|
|
119
|
-
total: inputCost + outputCost
|
|
125
|
+
total: inputCost + outputCost,
|
|
126
|
+
saved,
|
|
120
127
|
};
|
|
121
128
|
}
|
|
122
129
|
|
|
@@ -139,20 +146,25 @@ export class TokenTracker {
|
|
|
139
146
|
const tokenData = {
|
|
140
147
|
input: tokens.input || 0,
|
|
141
148
|
output: tokens.output || 0,
|
|
149
|
+
cached: tokens.cached || 0,
|
|
150
|
+
cacheWrite: tokens.cacheWrite || 0,
|
|
142
151
|
total: (tokens.input || 0) + (tokens.output || 0),
|
|
143
152
|
provider: tokens.provider || 'unknown',
|
|
144
153
|
model: tokens.model || modelId || 'unknown'
|
|
145
154
|
};
|
|
146
155
|
|
|
147
156
|
// Calculate cost: prefer per-model pricing from avc.json, fall back to provider estimate
|
|
157
|
+
// Skip cost calculation for OAuth calls (flat-rate subscription — no per-token billing)
|
|
148
158
|
let costData = null;
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
costData
|
|
159
|
+
if (!tokens.skipCost) {
|
|
160
|
+
const effectiveModelId = tokens.model || modelId;
|
|
161
|
+
if (effectiveModelId) {
|
|
162
|
+
costData = this.calculateCost(tokenData.input, tokenData.output, effectiveModelId, tokenData.cached);
|
|
163
|
+
}
|
|
164
|
+
// If no per-model pricing configured but provider sent a pre-computed estimate, use it
|
|
165
|
+
if ((!costData || costData.total === 0) && tokens.estimatedCost) {
|
|
166
|
+
costData = { input: 0, output: 0, total: tokens.estimatedCost };
|
|
167
|
+
}
|
|
156
168
|
}
|
|
157
169
|
|
|
158
170
|
console.log(` → Tracking tokens for ${ceremonyType}: ${tokenData.input} input, ${tokenData.output} output (${tokenData.provider})`);
|
|
@@ -211,17 +223,21 @@ export class TokenTracker {
|
|
|
211
223
|
const tokenData = {
|
|
212
224
|
input: delta.input || 0,
|
|
213
225
|
output: delta.output || 0,
|
|
226
|
+
cached: delta.cached || 0,
|
|
227
|
+
cacheWrite: delta.cacheWrite || 0,
|
|
214
228
|
total: (delta.input || 0) + (delta.output || 0),
|
|
215
229
|
provider: delta.provider || 'unknown',
|
|
216
230
|
model: delta.model || 'unknown'
|
|
217
231
|
};
|
|
218
232
|
|
|
219
233
|
let costData = null;
|
|
220
|
-
if (delta.
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
costData
|
|
234
|
+
if (!delta.skipCost) {
|
|
235
|
+
if (delta.model) {
|
|
236
|
+
costData = this.calculateCost(tokenData.input, tokenData.output, delta.model, tokenData.cached);
|
|
237
|
+
}
|
|
238
|
+
if ((!costData || costData.total === 0) && delta.estimatedCost) {
|
|
239
|
+
costData = { input: 0, output: 0, total: delta.estimatedCost };
|
|
240
|
+
}
|
|
225
241
|
}
|
|
226
242
|
|
|
227
243
|
if (!this.data[ceremonyType]) {
|
|
@@ -285,19 +301,22 @@ export class TokenTracker {
|
|
|
285
301
|
date: dateKey,
|
|
286
302
|
input: 0,
|
|
287
303
|
output: 0,
|
|
304
|
+
cached: 0,
|
|
288
305
|
total: 0,
|
|
289
306
|
executions: 0,
|
|
290
|
-
cost: { input: 0, output: 0, total: 0 }
|
|
307
|
+
cost: { input: 0, output: 0, total: 0, saved: 0 }
|
|
291
308
|
};
|
|
292
309
|
}
|
|
293
310
|
scope.daily[dateKey].input += tokenData.input;
|
|
294
311
|
scope.daily[dateKey].output += tokenData.output;
|
|
312
|
+
scope.daily[dateKey].cached = (scope.daily[dateKey].cached || 0) + (tokenData.cached || 0);
|
|
295
313
|
scope.daily[dateKey].total += tokenData.total;
|
|
296
314
|
if (incExec) scope.daily[dateKey].executions++;
|
|
297
315
|
if (costData) {
|
|
298
316
|
scope.daily[dateKey].cost.input += costData.input;
|
|
299
317
|
scope.daily[dateKey].cost.output += costData.output;
|
|
300
318
|
scope.daily[dateKey].cost.total += costData.total;
|
|
319
|
+
scope.daily[dateKey].cost.saved = (scope.daily[dateKey].cost.saved || 0) + (costData.saved || 0);
|
|
301
320
|
}
|
|
302
321
|
|
|
303
322
|
// Update weekly
|
|
@@ -306,19 +325,22 @@ export class TokenTracker {
|
|
|
306
325
|
week: weekKey,
|
|
307
326
|
input: 0,
|
|
308
327
|
output: 0,
|
|
328
|
+
cached: 0,
|
|
309
329
|
total: 0,
|
|
310
330
|
executions: 0,
|
|
311
|
-
cost: { input: 0, output: 0, total: 0 }
|
|
331
|
+
cost: { input: 0, output: 0, total: 0, saved: 0 }
|
|
312
332
|
};
|
|
313
333
|
}
|
|
314
334
|
scope.weekly[weekKey].input += tokenData.input;
|
|
315
335
|
scope.weekly[weekKey].output += tokenData.output;
|
|
336
|
+
scope.weekly[weekKey].cached = (scope.weekly[weekKey].cached || 0) + (tokenData.cached || 0);
|
|
316
337
|
scope.weekly[weekKey].total += tokenData.total;
|
|
317
338
|
if (incExec) scope.weekly[weekKey].executions++;
|
|
318
339
|
if (costData) {
|
|
319
340
|
scope.weekly[weekKey].cost.input += costData.input;
|
|
320
341
|
scope.weekly[weekKey].cost.output += costData.output;
|
|
321
342
|
scope.weekly[weekKey].cost.total += costData.total;
|
|
343
|
+
scope.weekly[weekKey].cost.saved = (scope.weekly[weekKey].cost.saved || 0) + (costData.saved || 0);
|
|
322
344
|
}
|
|
323
345
|
|
|
324
346
|
// Update monthly
|
|
@@ -327,35 +349,40 @@ export class TokenTracker {
|
|
|
327
349
|
month: monthKey,
|
|
328
350
|
input: 0,
|
|
329
351
|
output: 0,
|
|
352
|
+
cached: 0,
|
|
330
353
|
total: 0,
|
|
331
354
|
executions: 0,
|
|
332
|
-
cost: { input: 0, output: 0, total: 0 }
|
|
355
|
+
cost: { input: 0, output: 0, total: 0, saved: 0 }
|
|
333
356
|
};
|
|
334
357
|
}
|
|
335
358
|
scope.monthly[monthKey].input += tokenData.input;
|
|
336
359
|
scope.monthly[monthKey].output += tokenData.output;
|
|
360
|
+
scope.monthly[monthKey].cached = (scope.monthly[monthKey].cached || 0) + (tokenData.cached || 0);
|
|
337
361
|
scope.monthly[monthKey].total += tokenData.total;
|
|
338
362
|
if (incExec) scope.monthly[monthKey].executions++;
|
|
339
363
|
if (costData) {
|
|
340
364
|
scope.monthly[monthKey].cost.input += costData.input;
|
|
341
365
|
scope.monthly[monthKey].cost.output += costData.output;
|
|
342
366
|
scope.monthly[monthKey].cost.total += costData.total;
|
|
367
|
+
scope.monthly[monthKey].cost.saved = (scope.monthly[monthKey].cost.saved || 0) + (costData.saved || 0);
|
|
343
368
|
}
|
|
344
369
|
|
|
345
370
|
// Update all-time
|
|
346
371
|
scope.allTime.input += tokenData.input;
|
|
347
372
|
scope.allTime.output += tokenData.output;
|
|
373
|
+
scope.allTime.cached = (scope.allTime.cached || 0) + (tokenData.cached || 0);
|
|
348
374
|
scope.allTime.total += tokenData.total;
|
|
349
375
|
if (incExec) scope.allTime.executions++;
|
|
350
376
|
|
|
351
377
|
// Initialize cost tracking if not present
|
|
352
378
|
if (!scope.allTime.cost) {
|
|
353
|
-
scope.allTime.cost = { input: 0, output: 0, total: 0 };
|
|
379
|
+
scope.allTime.cost = { input: 0, output: 0, total: 0, saved: 0 };
|
|
354
380
|
}
|
|
355
381
|
if (costData) {
|
|
356
382
|
scope.allTime.cost.input += costData.input;
|
|
357
383
|
scope.allTime.cost.output += costData.output;
|
|
358
384
|
scope.allTime.cost.total += costData.total;
|
|
385
|
+
scope.allTime.cost.saved = (scope.allTime.cost.saved || 0) + (costData.saved || 0);
|
|
359
386
|
}
|
|
360
387
|
|
|
361
388
|
if (!scope.allTime.firstExecution) {
|
|
@@ -70,7 +70,7 @@ You are an expert ${domainTitle.toLowerCase()} reviewing user story implementati
|
|
|
70
70
|
- [ ] Expected outcomes are precisely defined
|
|
71
71
|
|
|
72
72
|
### Scope & Dependencies (10 points)
|
|
73
|
-
- [ ] Story is appropriately
|
|
73
|
+
- [ ] Story is appropriately scoped (single cohesive capability, 3-8 ACs)
|
|
74
74
|
- [ ] Dependencies on other stories are explicit
|
|
75
75
|
- [ ] Story is independent enough to be delivered incrementally
|
|
76
76
|
|
package/cli/validation-router.js
CHANGED
|
@@ -199,8 +199,8 @@ class ValidationRouter {
|
|
|
199
199
|
featureValidators.forEach(v => validators.add(v));
|
|
200
200
|
});
|
|
201
201
|
|
|
202
|
-
// Return unique validators (2-6 agents typically)
|
|
203
|
-
return Array.from(validators);
|
|
202
|
+
// Return unique validators (2-6 agents typically), filtered by project context
|
|
203
|
+
return this.filterByProjectContext(Array.from(validators), 'epic');
|
|
204
204
|
}
|
|
205
205
|
|
|
206
206
|
/**
|
|
@@ -233,8 +233,8 @@ class ValidationRouter {
|
|
|
233
233
|
featureValidators.forEach(v => validators.add(v));
|
|
234
234
|
});
|
|
235
235
|
|
|
236
|
-
// Return unique validators (3-8 agents typically)
|
|
237
|
-
return Array.from(validators);
|
|
236
|
+
// Return unique validators (3-8 agents typically), filtered by project context
|
|
237
|
+
return this.filterByProjectContext(Array.from(validators), 'story');
|
|
238
238
|
}
|
|
239
239
|
|
|
240
240
|
/**
|
|
@@ -391,6 +391,68 @@ class ValidationRouter {
|
|
|
391
391
|
return validRoles.includes(role);
|
|
392
392
|
}
|
|
393
393
|
|
|
394
|
+
/**
|
|
395
|
+
* Filter perspectives against project context using deterministic rules.
|
|
396
|
+
* Removes perspectives that are provably irrelevant based on project flags,
|
|
397
|
+
* regardless of what the LLM selector chose. Applied as a final pass after
|
|
398
|
+
* any selection method (static routing, LLM, or contextual selection).
|
|
399
|
+
*
|
|
400
|
+
* @param {string[]} validators - Full validator agent names
|
|
401
|
+
* @param {string} type - 'epic' or 'story'
|
|
402
|
+
* @returns {string[]} Filtered validators
|
|
403
|
+
*/
|
|
404
|
+
filterByProjectContext(validators, type) {
|
|
405
|
+
const ctx = this.projectContext;
|
|
406
|
+
if (!ctx || Object.keys(ctx).length === 0) {
|
|
407
|
+
console.log(` [context-filter] Skipped: projectContext is ${ctx ? 'empty' : 'null/undefined'}`);
|
|
408
|
+
return validators;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const excluded = new Set();
|
|
412
|
+
|
|
413
|
+
// Cloud: only if project explicitly uses cloud services
|
|
414
|
+
if (ctx.hasCloud === false) {
|
|
415
|
+
excluded.add('cloud');
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// DevOps: only if project has CI/CD or uses Kubernetes/cloud orchestration
|
|
419
|
+
if (ctx.hasCI_CD === false && ctx.hasCloud === false) {
|
|
420
|
+
excluded.add('devops');
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Mobile: only if project has a mobile app
|
|
424
|
+
if (ctx.hasMobileApp === false) {
|
|
425
|
+
excluded.add('mobile');
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Frontend/UI/UX: only if project has a frontend
|
|
429
|
+
if (ctx.hasFrontend === false) {
|
|
430
|
+
excluded.add('frontend');
|
|
431
|
+
excluded.add('ui');
|
|
432
|
+
excluded.add('ux');
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (excluded.size === 0) {
|
|
436
|
+
console.log(` [context-filter] No exclusions matched (hasCloud=${ctx.hasCloud}, hasCI_CD=${ctx.hasCI_CD}, hasMobileApp=${ctx.hasMobileApp}, hasFrontend=${ctx.hasFrontend})`);
|
|
437
|
+
return validators;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const prefix = `validator-${type}-`;
|
|
441
|
+
const filtered = validators.filter(v => {
|
|
442
|
+
const role = v.startsWith(prefix) ? v.slice(prefix.length) : v;
|
|
443
|
+
return !excluded.has(role);
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
// Log exclusions
|
|
447
|
+
const removedCount = validators.length - filtered.length;
|
|
448
|
+
if (removedCount > 0) {
|
|
449
|
+
const removedRoles = validators.filter(v => !filtered.includes(v));
|
|
450
|
+
console.log(` [context-filter] Removed ${removedCount} irrelevant perspective(s): ${removedRoles.map(v => v.replace(prefix, '')).join(', ')}`);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return filtered;
|
|
454
|
+
}
|
|
455
|
+
|
|
394
456
|
/**
|
|
395
457
|
* Check if a domain is known (has predefined routing rules)
|
|
396
458
|
* @private
|
|
@@ -432,7 +494,7 @@ class ValidationRouter {
|
|
|
432
494
|
featureValidators.forEach(v => validators.add(v));
|
|
433
495
|
});
|
|
434
496
|
|
|
435
|
-
return Array.from(validators);
|
|
497
|
+
return this.filterByProjectContext(Array.from(validators), 'epic');
|
|
436
498
|
}
|
|
437
499
|
|
|
438
500
|
/**
|
|
@@ -475,7 +537,7 @@ class ValidationRouter {
|
|
|
475
537
|
featureValidators.forEach(v => validators.add(v));
|
|
476
538
|
});
|
|
477
539
|
|
|
478
|
-
return Array.from(validators);
|
|
540
|
+
return this.filterByProjectContext(Array.from(validators), 'story');
|
|
479
541
|
}
|
|
480
542
|
/**
|
|
481
543
|
* Select validators using project context + per-item LLM call (contextual selection).
|
|
@@ -535,8 +597,8 @@ class ValidationRouter {
|
|
|
535
597
|
console.log(` ✂ Excluding ${type} ${role}${reason}`);
|
|
536
598
|
});
|
|
537
599
|
|
|
538
|
-
// Map short role names to full validator agent names
|
|
539
|
-
return safeSelected.map(r => `validator-${type}-${r}`);
|
|
600
|
+
// Map short role names to full validator agent names, then filter by project context
|
|
601
|
+
return this.filterByProjectContext(safeSelected.map(r => `validator-${type}-${r}`), type);
|
|
540
602
|
} catch (err) {
|
|
541
603
|
console.warn(` ⚠ Contextual agent selection failed (${err.message}) — using static routing`);
|
|
542
604
|
return type === 'epic'
|