@agile-vibe-coding/avc 0.1.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +29 -8
- package/cli/ceremony-history.js +369 -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/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 +0 -0
- package/cli/init-model-config.js +697 -0
- package/cli/init.js +1311 -274
- package/cli/kanban-server-manager.js +228 -0
- package/cli/llm-claude.js +83 -1
- package/cli/llm-gemini.js +85 -0
- package/cli/llm-mock.js +233 -0
- package/cli/llm-openai.js +233 -0
- package/cli/llm-provider.js +240 -3
- package/cli/llm-token-limits.js +102 -0
- package/cli/llm-verifier.js +454 -0
- 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 +73 -2
- package/cli/repl-ink.js +4988 -1217
- 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 +2102 -105
- package/cli/templates/project.md +25 -8
- package/cli/templates/vitepress-config.mts.template +5 -4
- 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 +18 -5
- package/cli/agents/documentation.md +0 -302
package/cli/update-checker.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
2
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
@@ -71,28 +71,30 @@ export class UpdateChecker {
|
|
|
71
71
|
return packageJson.version;
|
|
72
72
|
} catch (error) {
|
|
73
73
|
updateLogger.error('Failed to read current version', error);
|
|
74
|
-
console.error
|
|
74
|
+
// Don't use console.error - it interferes with React Ink rendering during startup
|
|
75
75
|
return null;
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
// Check npm registry for latest version
|
|
79
|
+
// Check npm registry for latest version (truly async — does not block the event loop)
|
|
80
80
|
async getLatestVersion() {
|
|
81
|
-
|
|
81
|
+
return new Promise((resolve) => {
|
|
82
82
|
updateLogger.debug('Checking npm registry for latest version');
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
timeout: 10000,
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
83
|
+
exec(
|
|
84
|
+
`npm view ${this.packageName} version`,
|
|
85
|
+
{ timeout: 10000 },
|
|
86
|
+
(error, stdout) => {
|
|
87
|
+
if (error) {
|
|
88
|
+
updateLogger.error('Failed to check npm registry', error);
|
|
89
|
+
resolve(null);
|
|
90
|
+
} else {
|
|
91
|
+
const version = stdout.trim();
|
|
92
|
+
updateLogger.debug(`Latest version on npm: ${version}`);
|
|
93
|
+
resolve(version);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
});
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
// Compare versions (returns true if remote is newer)
|
package/cli/update-notifier.js
CHANGED
|
@@ -28,7 +28,7 @@ export const UpdateNotification = ({ onDismiss }) => {
|
|
|
28
28
|
},
|
|
29
29
|
React.createElement(Box, { flexDirection: 'column' },
|
|
30
30
|
React.createElement(Text, { bold: true, color: 'blue' },
|
|
31
|
-
|
|
31
|
+
`Downloading update v${state.latestVersion}...`
|
|
32
32
|
),
|
|
33
33
|
React.createElement(Text, { dimColor: true },
|
|
34
34
|
'This happens in the background. Continue working!'
|
|
@@ -48,7 +48,7 @@ export const UpdateNotification = ({ onDismiss }) => {
|
|
|
48
48
|
},
|
|
49
49
|
React.createElement(Box, { flexDirection: 'column' },
|
|
50
50
|
React.createElement(Text, { bold: true, color: 'green' },
|
|
51
|
-
|
|
51
|
+
`Update v${state.downloadedVersion} ready!`
|
|
52
52
|
),
|
|
53
53
|
React.createElement(Text, null,
|
|
54
54
|
'Restart to use the new version'
|
|
@@ -95,7 +95,7 @@ export const UpdateNotification = ({ onDismiss }) => {
|
|
|
95
95
|
},
|
|
96
96
|
React.createElement(Box, { flexDirection: 'column' },
|
|
97
97
|
React.createElement(Text, { bold: true, color: 'red' },
|
|
98
|
-
'
|
|
98
|
+
'Update failed'
|
|
99
99
|
),
|
|
100
100
|
React.createElement(Text, null,
|
|
101
101
|
state.errorMessage || 'Unknown error'
|
|
@@ -126,7 +126,7 @@ export const UpdateNotification = ({ onDismiss }) => {
|
|
|
126
126
|
},
|
|
127
127
|
React.createElement(Box, { flexDirection: 'column' },
|
|
128
128
|
React.createElement(Text, { bold: true, color: 'yellow' },
|
|
129
|
-
|
|
129
|
+
`Update available: v${state.latestVersion}`
|
|
130
130
|
),
|
|
131
131
|
React.createElement(Text, null,
|
|
132
132
|
'Installing in background...'
|
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { loadAgent } from './agent-loader.js';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Routes epics and stories to appropriate domain validators
|
|
11
|
+
*
|
|
12
|
+
* This router determines which specialized validator agents should review
|
|
13
|
+
* each epic and story based on domain, features, and inferred characteristics.
|
|
14
|
+
*
|
|
15
|
+
* Hybrid Approach:
|
|
16
|
+
* - Uses static rule-based routing for known domains (fast path)
|
|
17
|
+
* - Falls back to LLM-based selection for unknown/novel domains (slow path)
|
|
18
|
+
*/
|
|
19
|
+
class ValidationRouter {
|
|
20
|
+
constructor(llmProvider = null, useSmartSelection = false, projectContext = null) {
|
|
21
|
+
this.epicMatrix = this.buildEpicValidationMatrix();
|
|
22
|
+
this.storyMatrix = this.buildStoryValidationMatrix();
|
|
23
|
+
this.llmProvider = llmProvider;
|
|
24
|
+
this.useSmartSelection = useSmartSelection;
|
|
25
|
+
this.projectContext = projectContext;
|
|
26
|
+
this.agentsPath = path.join(__dirname, 'agents');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Build routing matrix for epic validation
|
|
31
|
+
* Maps domains and features to validator agent names
|
|
32
|
+
*/
|
|
33
|
+
buildEpicValidationMatrix() {
|
|
34
|
+
return {
|
|
35
|
+
// Universal validators - always check all epics
|
|
36
|
+
universal: [
|
|
37
|
+
'validator-epic-solution-architect',
|
|
38
|
+
'validator-epic-developer',
|
|
39
|
+
'validator-epic-security'
|
|
40
|
+
],
|
|
41
|
+
|
|
42
|
+
// Domain-specific validators
|
|
43
|
+
domains: {
|
|
44
|
+
'infrastructure': [
|
|
45
|
+
'validator-epic-devops',
|
|
46
|
+
'validator-epic-cloud',
|
|
47
|
+
'validator-epic-backend'
|
|
48
|
+
],
|
|
49
|
+
'user-management': [
|
|
50
|
+
'validator-epic-backend',
|
|
51
|
+
'validator-epic-database',
|
|
52
|
+
'validator-epic-security',
|
|
53
|
+
'validator-epic-api'
|
|
54
|
+
],
|
|
55
|
+
'frontend': [
|
|
56
|
+
'validator-epic-frontend',
|
|
57
|
+
'validator-epic-ui',
|
|
58
|
+
'validator-epic-ux'
|
|
59
|
+
],
|
|
60
|
+
'mobile': [
|
|
61
|
+
'validator-epic-mobile',
|
|
62
|
+
'validator-epic-ui',
|
|
63
|
+
'validator-epic-ux',
|
|
64
|
+
'validator-epic-api'
|
|
65
|
+
],
|
|
66
|
+
'data-processing': [
|
|
67
|
+
'validator-epic-data',
|
|
68
|
+
'validator-epic-database',
|
|
69
|
+
'validator-epic-backend'
|
|
70
|
+
],
|
|
71
|
+
'api': [
|
|
72
|
+
'validator-epic-api',
|
|
73
|
+
'validator-epic-backend',
|
|
74
|
+
'validator-epic-security'
|
|
75
|
+
],
|
|
76
|
+
'analytics': [
|
|
77
|
+
'validator-epic-data',
|
|
78
|
+
'validator-epic-backend',
|
|
79
|
+
'validator-epic-database'
|
|
80
|
+
],
|
|
81
|
+
'communication': [
|
|
82
|
+
'validator-epic-backend',
|
|
83
|
+
'validator-epic-api',
|
|
84
|
+
'validator-epic-security'
|
|
85
|
+
]
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
// Feature-specific validators
|
|
89
|
+
features: {
|
|
90
|
+
'authentication': ['validator-epic-security'],
|
|
91
|
+
'authorization': ['validator-epic-security'],
|
|
92
|
+
'database': ['validator-epic-database'],
|
|
93
|
+
'testing': ['validator-epic-qa', 'validator-epic-test-architect'],
|
|
94
|
+
'deployment': ['validator-epic-devops', 'validator-epic-cloud'],
|
|
95
|
+
'api': ['validator-epic-api'],
|
|
96
|
+
'ui': ['validator-epic-ui', 'validator-epic-ux'],
|
|
97
|
+
'mobile': ['validator-epic-mobile'],
|
|
98
|
+
'real-time': ['validator-epic-backend', 'validator-epic-api'],
|
|
99
|
+
'data-storage': ['validator-epic-database', 'validator-epic-data'],
|
|
100
|
+
'logging': ['validator-epic-devops'],
|
|
101
|
+
'monitoring': ['validator-epic-devops'],
|
|
102
|
+
'security': ['validator-epic-security']
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Build routing matrix for story validation
|
|
109
|
+
* Similar to epic matrix but includes QA and testing focus
|
|
110
|
+
*/
|
|
111
|
+
buildStoryValidationMatrix() {
|
|
112
|
+
return {
|
|
113
|
+
// Universal validators - always check all stories
|
|
114
|
+
universal: [
|
|
115
|
+
'validator-story-developer',
|
|
116
|
+
'validator-story-qa',
|
|
117
|
+
'validator-story-test-architect'
|
|
118
|
+
],
|
|
119
|
+
|
|
120
|
+
// Domain-specific validators
|
|
121
|
+
domains: {
|
|
122
|
+
'infrastructure': [
|
|
123
|
+
'validator-story-devops',
|
|
124
|
+
'validator-story-cloud',
|
|
125
|
+
'validator-story-backend'
|
|
126
|
+
],
|
|
127
|
+
'user-management': [
|
|
128
|
+
'validator-story-backend',
|
|
129
|
+
'validator-story-database',
|
|
130
|
+
'validator-story-security',
|
|
131
|
+
'validator-story-api',
|
|
132
|
+
'validator-story-ux'
|
|
133
|
+
],
|
|
134
|
+
'frontend': [
|
|
135
|
+
'validator-story-frontend',
|
|
136
|
+
'validator-story-ui',
|
|
137
|
+
'validator-story-ux'
|
|
138
|
+
],
|
|
139
|
+
'mobile': [
|
|
140
|
+
'validator-story-mobile',
|
|
141
|
+
'validator-story-ui',
|
|
142
|
+
'validator-story-ux'
|
|
143
|
+
],
|
|
144
|
+
'data-processing': [
|
|
145
|
+
'validator-story-data',
|
|
146
|
+
'validator-story-database',
|
|
147
|
+
'validator-story-backend'
|
|
148
|
+
],
|
|
149
|
+
'api': [
|
|
150
|
+
'validator-story-api',
|
|
151
|
+
'validator-story-backend',
|
|
152
|
+
'validator-story-security'
|
|
153
|
+
],
|
|
154
|
+
'analytics': [
|
|
155
|
+
'validator-story-data',
|
|
156
|
+
'validator-story-backend',
|
|
157
|
+
'validator-story-database'
|
|
158
|
+
],
|
|
159
|
+
'communication': [
|
|
160
|
+
'validator-story-backend',
|
|
161
|
+
'validator-story-api',
|
|
162
|
+
'validator-story-security'
|
|
163
|
+
]
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
// Feature-specific validators
|
|
167
|
+
features: {
|
|
168
|
+
'authentication': ['validator-story-security'],
|
|
169
|
+
'crud-operations': ['validator-story-database', 'validator-story-api'],
|
|
170
|
+
'search': ['validator-story-database', 'validator-story-backend'],
|
|
171
|
+
'real-time': ['validator-story-api', 'validator-story-backend'],
|
|
172
|
+
'responsive-design': ['validator-story-ui', 'validator-story-frontend'],
|
|
173
|
+
'file-upload': ['validator-story-backend', 'validator-story-api'],
|
|
174
|
+
'notifications': ['validator-story-backend', 'validator-story-api'],
|
|
175
|
+
'reporting': ['validator-story-data', 'validator-story-backend']
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Get list of validator agents for an epic
|
|
182
|
+
* @param {Object} epic - Epic work item with domain and features
|
|
183
|
+
* @returns {string[]} Array of validator agent names
|
|
184
|
+
*/
|
|
185
|
+
getValidatorsForEpic(epic) {
|
|
186
|
+
const validators = new Set();
|
|
187
|
+
|
|
188
|
+
// 1. Add universal validators (always check)
|
|
189
|
+
this.epicMatrix.universal.forEach(v => validators.add(v));
|
|
190
|
+
|
|
191
|
+
// 2. Add domain-specific validators
|
|
192
|
+
const domainValidators = this.epicMatrix.domains[epic.domain] || [];
|
|
193
|
+
domainValidators.forEach(v => validators.add(v));
|
|
194
|
+
|
|
195
|
+
// 3. Add feature-specific validators
|
|
196
|
+
(epic.features || []).forEach(feature => {
|
|
197
|
+
const featureNormalized = feature.toLowerCase().replace(/\s+/g, '-');
|
|
198
|
+
const featureValidators = this.epicMatrix.features[featureNormalized] || [];
|
|
199
|
+
featureValidators.forEach(v => validators.add(v));
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// Return unique validators (2-6 agents typically)
|
|
203
|
+
return Array.from(validators);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Get list of validator agents for a story
|
|
208
|
+
* @param {Object} story - Story work item
|
|
209
|
+
* @param {Object} epic - Parent epic for domain/feature context
|
|
210
|
+
* @returns {string[]} Array of validator agent names
|
|
211
|
+
*/
|
|
212
|
+
getValidatorsForStory(story, epic) {
|
|
213
|
+
const validators = new Set();
|
|
214
|
+
|
|
215
|
+
// 1. Add universal validators
|
|
216
|
+
this.storyMatrix.universal.forEach(v => validators.add(v));
|
|
217
|
+
|
|
218
|
+
// 2. Inherit domain validators from epic
|
|
219
|
+
const domainValidators = this.storyMatrix.domains[epic.domain] || [];
|
|
220
|
+
domainValidators.forEach(v => validators.add(v));
|
|
221
|
+
|
|
222
|
+
// 3. Add feature-specific validators (from epic features)
|
|
223
|
+
(epic.features || []).forEach(feature => {
|
|
224
|
+
const featureNormalized = feature.toLowerCase().replace(/\s+/g, '-');
|
|
225
|
+
const featureValidators = this.storyMatrix.features[featureNormalized] || [];
|
|
226
|
+
featureValidators.forEach(v => validators.add(v));
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// 4. Infer features from story acceptance criteria
|
|
230
|
+
const inferredFeatures = this.inferFeaturesFromAcceptance(story.acceptance);
|
|
231
|
+
inferredFeatures.forEach(feature => {
|
|
232
|
+
const featureValidators = this.storyMatrix.features[feature] || [];
|
|
233
|
+
featureValidators.forEach(v => validators.add(v));
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// Return unique validators (3-8 agents typically)
|
|
237
|
+
return Array.from(validators);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Infer features from story acceptance criteria text
|
|
242
|
+
* Uses keyword matching to detect common patterns
|
|
243
|
+
* @param {string[]} acceptanceCriteria - Array of acceptance criteria
|
|
244
|
+
* @returns {string[]} Array of inferred feature names
|
|
245
|
+
*/
|
|
246
|
+
inferFeaturesFromAcceptance(acceptanceCriteria) {
|
|
247
|
+
const features = [];
|
|
248
|
+
const text = (acceptanceCriteria || []).join(' ').toLowerCase();
|
|
249
|
+
|
|
250
|
+
// Authentication patterns
|
|
251
|
+
if (text.includes('login') || text.includes('authenticate') || text.includes('sign in')) {
|
|
252
|
+
features.push('authentication');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// CRUD patterns
|
|
256
|
+
if (text.includes('create') || text.includes('update') || text.includes('delete') || text.includes('edit')) {
|
|
257
|
+
features.push('crud-operations');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Search patterns
|
|
261
|
+
if (text.includes('search') || text.includes('filter') || text.includes('find')) {
|
|
262
|
+
features.push('search');
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Real-time patterns
|
|
266
|
+
if (text.includes('real-time') || text.includes('websocket') || text.includes('live')) {
|
|
267
|
+
features.push('real-time');
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Responsive design patterns
|
|
271
|
+
if (text.includes('mobile') || text.includes('responsive') || text.includes('tablet')) {
|
|
272
|
+
features.push('responsive-design');
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// File upload patterns
|
|
276
|
+
if (text.includes('upload') || text.includes('file') || text.includes('attachment')) {
|
|
277
|
+
features.push('file-upload');
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Notification patterns
|
|
281
|
+
if (text.includes('notify') || text.includes('notification') || text.includes('alert')) {
|
|
282
|
+
features.push('notifications');
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Reporting patterns
|
|
286
|
+
if (text.includes('report') || text.includes('analytics') || text.includes('dashboard')) {
|
|
287
|
+
features.push('reporting');
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return features;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Use LLM to select validators for an epic (fallback for unknown domains)
|
|
295
|
+
* @param {Object} epic - Epic work item
|
|
296
|
+
* @param {string} type - 'epic' or 'story'
|
|
297
|
+
* @returns {Promise<string[]>} Array of validator names
|
|
298
|
+
*/
|
|
299
|
+
async llmSelectValidators(workItem, type = 'epic') {
|
|
300
|
+
if (!this.llmProvider || !this.useSmartSelection) {
|
|
301
|
+
return [];
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Load validator selector agent
|
|
305
|
+
let agentInstructions;
|
|
306
|
+
try {
|
|
307
|
+
agentInstructions = loadAgent('validator-selector.md');
|
|
308
|
+
} catch (error) {
|
|
309
|
+
console.warn(`Could not load validator-selector agent: ${error.message}`);
|
|
310
|
+
return [];
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Build prompt
|
|
314
|
+
const prompt = this.buildValidatorSelectionPrompt(workItem, type);
|
|
315
|
+
|
|
316
|
+
try {
|
|
317
|
+
// Call LLM
|
|
318
|
+
const response = await this.llmProvider.generateJSON(prompt, agentInstructions);
|
|
319
|
+
|
|
320
|
+
// Validate response structure
|
|
321
|
+
if (!response.validators || !Array.isArray(response.validators)) {
|
|
322
|
+
console.warn(`Invalid LLM response: missing validators array`);
|
|
323
|
+
return [];
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Validate validator names exist
|
|
327
|
+
const validValidators = response.validators.filter(v => this.isValidValidatorName(v, type));
|
|
328
|
+
|
|
329
|
+
if (validValidators.length < response.validators.length) {
|
|
330
|
+
const invalid = response.validators.filter(v => !this.isValidValidatorName(v, type));
|
|
331
|
+
console.warn(`LLM returned invalid validator names: ${invalid.join(', ')}`);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Log selection reasoning
|
|
335
|
+
if (response.reasoning) {
|
|
336
|
+
console.log(` LLM selection reasoning: ${response.reasoning}`);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return validValidators;
|
|
340
|
+
} catch (error) {
|
|
341
|
+
console.warn(`LLM validator selection failed: ${error.message}`);
|
|
342
|
+
return [];
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Build prompt for LLM validator selection
|
|
348
|
+
* @private
|
|
349
|
+
*/
|
|
350
|
+
buildValidatorSelectionPrompt(workItem, type) {
|
|
351
|
+
const workItemType = type.charAt(0).toUpperCase() + type.slice(1);
|
|
352
|
+
|
|
353
|
+
let prompt = `Select the most relevant validators for the following ${workItemType}:\n\n`;
|
|
354
|
+
prompt += `**${workItemType} Name:** ${workItem.name}\n`;
|
|
355
|
+
prompt += `**Domain:** ${workItem.domain}\n`;
|
|
356
|
+
|
|
357
|
+
if (type === 'epic') {
|
|
358
|
+
prompt += `**Description:** ${workItem.description}\n`;
|
|
359
|
+
prompt += `**Features:** ${(workItem.features || []).join(', ')}\n`;
|
|
360
|
+
} else {
|
|
361
|
+
prompt += `**Description:** ${workItem.description}\n`;
|
|
362
|
+
prompt += `**User Type:** ${workItem.userType}\n`;
|
|
363
|
+
prompt += `**Acceptance Criteria:**\n`;
|
|
364
|
+
(workItem.acceptance || []).forEach((ac, i) => {
|
|
365
|
+
prompt += `${i + 1}. ${ac}\n`;
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
prompt += `\nSelect 5-8 relevant validators from the available list and return as JSON.`;
|
|
370
|
+
|
|
371
|
+
return prompt;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Check if a validator name is valid
|
|
376
|
+
* @private
|
|
377
|
+
*/
|
|
378
|
+
isValidValidatorName(validatorName, type) {
|
|
379
|
+
const prefix = `validator-${type}-`;
|
|
380
|
+
if (!validatorName.startsWith(prefix)) {
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const role = validatorName.replace(prefix, '');
|
|
385
|
+
const validRoles = [
|
|
386
|
+
'solution-architect', 'developer', 'security', 'devops', 'cloud',
|
|
387
|
+
'backend', 'database', 'api', 'frontend', 'ui', 'ux', 'mobile',
|
|
388
|
+
'data', 'qa', 'test-architect'
|
|
389
|
+
];
|
|
390
|
+
|
|
391
|
+
return validRoles.includes(role);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Check if a domain is known (has predefined routing rules)
|
|
396
|
+
* @private
|
|
397
|
+
*/
|
|
398
|
+
isDomainKnown(domain, type = 'epic') {
|
|
399
|
+
const matrix = type === 'epic' ? this.epicMatrix : this.storyMatrix;
|
|
400
|
+
return !!matrix.domains[domain];
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Get validators for epic with hybrid approach (static + LLM fallback)
|
|
405
|
+
* @param {Object} epic - Epic work item
|
|
406
|
+
* @returns {Promise<string[]>} Array of validator names
|
|
407
|
+
*/
|
|
408
|
+
async getValidatorsForEpicWithLLM(epic) {
|
|
409
|
+
const validators = new Set();
|
|
410
|
+
|
|
411
|
+
// 1. Always add universal validators (static)
|
|
412
|
+
this.epicMatrix.universal.forEach(v => validators.add(v));
|
|
413
|
+
|
|
414
|
+
// 2. Check if domain is known
|
|
415
|
+
const domainKnown = this.isDomainKnown(epic.domain, 'epic');
|
|
416
|
+
|
|
417
|
+
if (domainKnown) {
|
|
418
|
+
// Fast path: Use static routing for known domains
|
|
419
|
+
const domainValidators = this.epicMatrix.domains[epic.domain];
|
|
420
|
+
domainValidators.forEach(v => validators.add(v));
|
|
421
|
+
} else if (this.useSmartSelection && this.llmProvider) {
|
|
422
|
+
// Slow path: Use LLM for unknown domains
|
|
423
|
+
console.log(` 📡 Unknown domain "${epic.domain}" - using LLM selection...`);
|
|
424
|
+
const llmValidators = await this.llmSelectValidators(epic, 'epic');
|
|
425
|
+
llmValidators.forEach(v => validators.add(v));
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// 3. Add feature-specific validators (static)
|
|
429
|
+
(epic.features || []).forEach(feature => {
|
|
430
|
+
const featureNormalized = feature.toLowerCase().replace(/\s+/g, '-');
|
|
431
|
+
const featureValidators = this.epicMatrix.features[featureNormalized] || [];
|
|
432
|
+
featureValidators.forEach(v => validators.add(v));
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
return Array.from(validators);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Get validators for story with hybrid approach (static + LLM fallback)
|
|
440
|
+
* @param {Object} story - Story work item
|
|
441
|
+
* @param {Object} epic - Parent epic for context
|
|
442
|
+
* @returns {Promise<string[]>} Array of validator names
|
|
443
|
+
*/
|
|
444
|
+
async getValidatorsForStoryWithLLM(story, epic) {
|
|
445
|
+
const validators = new Set();
|
|
446
|
+
|
|
447
|
+
// 1. Add universal validators (static)
|
|
448
|
+
this.storyMatrix.universal.forEach(v => validators.add(v));
|
|
449
|
+
|
|
450
|
+
// 2. Check if domain is known
|
|
451
|
+
const domainKnown = this.isDomainKnown(epic.domain, 'story');
|
|
452
|
+
|
|
453
|
+
if (domainKnown) {
|
|
454
|
+
// Fast path: Use static routing for known domains
|
|
455
|
+
const domainValidators = this.storyMatrix.domains[epic.domain];
|
|
456
|
+
domainValidators.forEach(v => validators.add(v));
|
|
457
|
+
} else if (this.useSmartSelection && this.llmProvider) {
|
|
458
|
+
// Slow path: Use LLM for unknown domains
|
|
459
|
+
console.log(` 📡 Unknown domain "${epic.domain}" - using LLM selection...`);
|
|
460
|
+
const llmValidators = await this.llmSelectValidators(story, 'story');
|
|
461
|
+
llmValidators.forEach(v => validators.add(v));
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// 3. Add feature-specific validators from epic (static)
|
|
465
|
+
(epic.features || []).forEach(feature => {
|
|
466
|
+
const featureNormalized = feature.toLowerCase().replace(/\s+/g, '-');
|
|
467
|
+
const featureValidators = this.storyMatrix.features[featureNormalized] || [];
|
|
468
|
+
featureValidators.forEach(v => validators.add(v));
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
// 4. Infer features from acceptance criteria (static)
|
|
472
|
+
const inferredFeatures = this.inferFeaturesFromAcceptance(story.acceptance);
|
|
473
|
+
inferredFeatures.forEach(feature => {
|
|
474
|
+
const featureValidators = this.storyMatrix.features[feature] || [];
|
|
475
|
+
featureValidators.forEach(v => validators.add(v));
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
return Array.from(validators);
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Select validators using project context + per-item LLM call (contextual selection).
|
|
482
|
+
* Falls back to static routing if LLM call fails or returns < 2 validators.
|
|
483
|
+
* @param {Object} item - Epic or Story work item
|
|
484
|
+
* @param {string} type - 'epic' or 'story'
|
|
485
|
+
* @param {Object} llmProvider - LLM provider instance to use for selection
|
|
486
|
+
* @param {Object} [parentEpic] - Parent epic (required when type='story')
|
|
487
|
+
* @returns {Promise<string[]>} Array of full validator agent names
|
|
488
|
+
*/
|
|
489
|
+
async selectValidatorsWithContext(item, type, llmProvider, parentEpic = null) {
|
|
490
|
+
const validRoles = [
|
|
491
|
+
'solution-architect', 'developer', 'security', 'devops', 'cloud',
|
|
492
|
+
'backend', 'database', 'api', 'frontend', 'ui', 'ux', 'mobile',
|
|
493
|
+
'data', 'qa', 'test-architect'
|
|
494
|
+
];
|
|
495
|
+
|
|
496
|
+
let selectorAgent;
|
|
497
|
+
try {
|
|
498
|
+
selectorAgent = loadAgent('agent-selector.md');
|
|
499
|
+
} catch (err) {
|
|
500
|
+
console.warn(` ⚠ Could not load agent-selector.md (${err.message}) — using static routing`);
|
|
501
|
+
return type === 'epic'
|
|
502
|
+
? await this.getValidatorsForEpicWithLLM(item)
|
|
503
|
+
: await this.getValidatorsForStoryWithLLM(item, parentEpic ?? item);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (!llmProvider) {
|
|
507
|
+
console.warn(` ⚠ Contextual agent selection skipped (no LLM provider) — using static routing`);
|
|
508
|
+
return type === 'epic'
|
|
509
|
+
? await this.getValidatorsForEpicWithLLM(item)
|
|
510
|
+
: await this.getValidatorsForStoryWithLLM(item, parentEpic ?? item);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const prompt = this.buildAgentSelectionPrompt(item, type, this.projectContext || {}, parentEpic);
|
|
514
|
+
console.log(`[DEBUG] selectValidatorsWithContext: type=${type}, item="${item.name}", projectContext keys=${Object.keys(this.projectContext || {}).join(',') || 'none'}`);
|
|
515
|
+
|
|
516
|
+
try {
|
|
517
|
+
const response = await llmProvider.generateJSON(prompt, selectorAgent);
|
|
518
|
+
|
|
519
|
+
if (!response?.selected || !Array.isArray(response.selected)) {
|
|
520
|
+
throw new Error('missing selected array in response');
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// Filter to known role names only
|
|
524
|
+
const safeSelected = response.selected.filter(r => validRoles.includes(r));
|
|
525
|
+
|
|
526
|
+
// Safety floor — always include these two
|
|
527
|
+
if (!safeSelected.includes('solution-architect')) safeSelected.push('solution-architect');
|
|
528
|
+
if (!safeSelected.includes('developer')) safeSelected.push('developer');
|
|
529
|
+
|
|
530
|
+
// Log excluded roles with reasons
|
|
531
|
+
const excluded = response.excluded || [];
|
|
532
|
+
const reasons = response.reasons || {};
|
|
533
|
+
excluded.forEach(role => {
|
|
534
|
+
const reason = reasons[role] ? `: ${reasons[role]}` : '';
|
|
535
|
+
console.log(` ✂ Excluding ${type} ${role}${reason}`);
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
// Map short role names to full validator agent names
|
|
539
|
+
return safeSelected.map(r => `validator-${type}-${r}`);
|
|
540
|
+
} catch (err) {
|
|
541
|
+
console.warn(` ⚠ Contextual agent selection failed (${err.message}) — using static routing`);
|
|
542
|
+
return type === 'epic'
|
|
543
|
+
? await this.getValidatorsForEpicWithLLM(item)
|
|
544
|
+
: await this.getValidatorsForStoryWithLLM(item, parentEpic ?? item);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Build the prompt for contextual agent selection.
|
|
550
|
+
* @param {Object} item - Epic or Story
|
|
551
|
+
* @param {string} type - 'epic' or 'story'
|
|
552
|
+
* @param {Object} projectContext - Extracted project context
|
|
553
|
+
* @param {Object} [parentEpic] - Parent epic for stories
|
|
554
|
+
* @returns {string} Prompt text
|
|
555
|
+
*/
|
|
556
|
+
buildAgentSelectionPrompt(item, type, projectContext, parentEpic = null) {
|
|
557
|
+
const ctx = projectContext || {};
|
|
558
|
+
const lines = [];
|
|
559
|
+
|
|
560
|
+
// Project context section
|
|
561
|
+
if (Object.keys(ctx).length > 0) {
|
|
562
|
+
lines.push('PROJECT CONTEXT:');
|
|
563
|
+
if (ctx.deploymentType) lines.push(`- Deployment: ${ctx.deploymentType}${ctx.hasCloud ? ' (cloud services present)' : ' (no cloud services)'}`);
|
|
564
|
+
if (ctx.techStack?.length) lines.push(`- Tech stack: ${ctx.techStack.join(', ')}`);
|
|
565
|
+
lines.push(`- CI/CD pipeline: ${ctx.hasCI_CD ? 'yes' : 'no'}`);
|
|
566
|
+
lines.push(`- Mobile app: ${ctx.hasMobileApp ? 'yes' : 'no'}`);
|
|
567
|
+
lines.push(`- Frontend: ${ctx.hasFrontend ? 'yes' : 'no'}`);
|
|
568
|
+
lines.push(`- Public API: ${ctx.hasPublicAPI ? 'yes' : 'no'}`);
|
|
569
|
+
if (ctx.projectType) lines.push(`- Project type: ${ctx.projectType}`);
|
|
570
|
+
if (ctx.teamContext) lines.push(`- Team: ${ctx.teamContext}`);
|
|
571
|
+
lines.push('');
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// Work item section
|
|
575
|
+
if (type === 'epic') {
|
|
576
|
+
lines.push('ITEM TO VALIDATE (Epic):');
|
|
577
|
+
lines.push(`- Name: ${item.name}`);
|
|
578
|
+
lines.push(`- Domain: ${item.domain}`);
|
|
579
|
+
if (item.description) lines.push(`- Description: ${item.description}`);
|
|
580
|
+
if (item.features?.length) {
|
|
581
|
+
lines.push(`- Features:`);
|
|
582
|
+
item.features.forEach(f => lines.push(` * ${f}`));
|
|
583
|
+
}
|
|
584
|
+
} else {
|
|
585
|
+
lines.push('ITEM TO VALIDATE (Story):');
|
|
586
|
+
lines.push(`- Name: ${item.name}`);
|
|
587
|
+
if (item.userType) lines.push(`- User type: ${item.userType}`);
|
|
588
|
+
if (item.description) lines.push(`- Description: ${item.description}`);
|
|
589
|
+
if (parentEpic) {
|
|
590
|
+
lines.push(`- Parent Epic: ${parentEpic.name} (domain: ${parentEpic.domain})`);
|
|
591
|
+
}
|
|
592
|
+
if (item.acceptance?.length) {
|
|
593
|
+
lines.push(`- Acceptance Criteria:`);
|
|
594
|
+
item.acceptance.forEach((ac, i) => lines.push(` ${i + 1}. ${ac}`));
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
lines.push('');
|
|
599
|
+
lines.push('Select which validator roles are relevant for this specific item given the project context above.');
|
|
600
|
+
|
|
601
|
+
return lines.join('\n');
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
export { ValidationRouter };
|