@agile-vibe-coding/avc 0.1.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/agent-loader.js +21 -0
- package/cli/agents/agent-selector.md +152 -0
- package/cli/agents/architecture-recommender.md +418 -0
- package/cli/agents/code-implementer.md +117 -0
- package/cli/agents/code-validator.md +80 -0
- package/cli/agents/context-reviewer-epic.md +101 -0
- package/cli/agents/context-reviewer-story.md +92 -0
- package/cli/agents/context-writer-epic.md +145 -0
- package/cli/agents/context-writer-story.md +111 -0
- package/cli/agents/database-deep-dive.md +470 -0
- package/cli/agents/database-recommender.md +634 -0
- package/cli/agents/doc-distributor.md +176 -0
- package/cli/agents/doc-writer-epic.md +42 -0
- package/cli/agents/doc-writer-story.md +43 -0
- package/cli/agents/documentation-updater.md +203 -0
- package/cli/agents/duplicate-detector.md +110 -0
- package/cli/agents/epic-story-decomposer.md +559 -0
- package/cli/agents/feature-context-generator.md +91 -0
- package/cli/agents/gap-checker-epic.md +52 -0
- package/cli/agents/impact-checker-story.md +51 -0
- package/cli/agents/migration-guide-generator.md +305 -0
- package/cli/agents/mission-scope-generator.md +143 -0
- package/cli/agents/mission-scope-validator.md +146 -0
- package/cli/agents/project-context-extractor.md +122 -0
- package/cli/agents/project-documentation-creator.json +226 -0
- package/cli/agents/project-documentation-creator.md +595 -0
- package/cli/agents/question-prefiller.md +269 -0
- package/cli/agents/refiner-epic.md +39 -0
- package/cli/agents/refiner-story.md +42 -0
- package/cli/agents/scaffolding-generator.md +99 -0
- package/cli/agents/seed-validator.md +71 -0
- package/cli/agents/story-doc-enricher.md +133 -0
- package/cli/agents/story-scope-reviewer.md +147 -0
- package/cli/agents/story-splitter.md +83 -0
- package/cli/agents/suggestion-business-analyst.md +88 -0
- package/cli/agents/suggestion-deployment-architect.md +263 -0
- package/cli/agents/suggestion-product-manager.md +129 -0
- package/cli/agents/suggestion-security-specialist.md +156 -0
- package/cli/agents/suggestion-technical-architect.md +269 -0
- package/cli/agents/suggestion-ux-researcher.md +93 -0
- package/cli/agents/task-subtask-decomposer.md +188 -0
- package/cli/agents/validator-documentation.json +183 -0
- package/cli/agents/validator-documentation.md +455 -0
- package/cli/agents/validator-selector.md +211 -0
- package/cli/ansi-colors.js +21 -0
- package/cli/api-reference-tool.js +368 -0
- package/cli/build-docs.js +29 -8
- package/cli/ceremony-history.js +369 -0
- package/cli/checks/catalog.json +76 -0
- package/cli/checks/code/quality.json +26 -0
- package/cli/checks/code/testing.json +14 -0
- package/cli/checks/code/traceability.json +26 -0
- package/cli/checks/cross-refs/epic.json +171 -0
- package/cli/checks/cross-refs/story.json +149 -0
- package/cli/checks/epic/api.json +114 -0
- package/cli/checks/epic/backend.json +126 -0
- package/cli/checks/epic/cloud.json +126 -0
- package/cli/checks/epic/data.json +102 -0
- package/cli/checks/epic/database.json +114 -0
- package/cli/checks/epic/developer.json +182 -0
- package/cli/checks/epic/devops.json +174 -0
- package/cli/checks/epic/frontend.json +162 -0
- package/cli/checks/epic/mobile.json +102 -0
- package/cli/checks/epic/qa.json +90 -0
- package/cli/checks/epic/security.json +184 -0
- package/cli/checks/epic/solution-architect.json +192 -0
- package/cli/checks/epic/test-architect.json +90 -0
- package/cli/checks/epic/ui.json +102 -0
- package/cli/checks/epic/ux.json +90 -0
- package/cli/checks/fixes/epic-fix-template.md +10 -0
- package/cli/checks/fixes/story-fix-template.md +10 -0
- package/cli/checks/story/api.json +186 -0
- package/cli/checks/story/backend.json +102 -0
- package/cli/checks/story/cloud.json +102 -0
- package/cli/checks/story/data.json +210 -0
- package/cli/checks/story/database.json +102 -0
- package/cli/checks/story/developer.json +168 -0
- package/cli/checks/story/devops.json +102 -0
- package/cli/checks/story/frontend.json +174 -0
- package/cli/checks/story/mobile.json +102 -0
- package/cli/checks/story/qa.json +210 -0
- package/cli/checks/story/security.json +198 -0
- package/cli/checks/story/solution-architect.json +230 -0
- package/cli/checks/story/test-architect.json +210 -0
- package/cli/checks/story/ui.json +102 -0
- package/cli/checks/story/ux.json +102 -0
- package/cli/coding-order.js +401 -0
- package/cli/command-logger.js +49 -12
- package/cli/components/static-output.js +63 -0
- package/cli/console-output-manager.js +94 -0
- package/cli/dependency-checker.js +72 -0
- package/cli/docs-sync.js +306 -0
- package/cli/epic-story-validator.js +659 -0
- package/cli/evaluation-prompts.js +1008 -0
- package/cli/execution-context.js +195 -0
- package/cli/generate-summary-table.js +340 -0
- package/cli/init-model-config.js +704 -0
- package/cli/init.js +1737 -278
- package/cli/kanban-server-manager.js +227 -0
- package/cli/llm-claude.js +150 -1
- package/cli/llm-gemini.js +109 -0
- package/cli/llm-local.js +493 -0
- package/cli/llm-mock.js +233 -0
- package/cli/llm-openai.js +454 -0
- package/cli/llm-provider.js +379 -3
- package/cli/llm-token-limits.js +211 -0
- package/cli/llm-verifier.js +662 -0
- package/cli/llm-xiaomi.js +143 -0
- package/cli/message-constants.js +49 -0
- package/cli/message-manager.js +334 -0
- package/cli/message-types.js +96 -0
- package/cli/messaging-api.js +291 -0
- package/cli/micro-check-fixer.js +335 -0
- package/cli/micro-check-runner.js +449 -0
- package/cli/micro-check-scorer.js +148 -0
- package/cli/micro-check-validator.js +538 -0
- package/cli/model-pricing.js +192 -0
- package/cli/model-query-engine.js +468 -0
- package/cli/model-recommendation-analyzer.js +495 -0
- package/cli/model-selector.js +270 -0
- package/cli/output-buffer.js +107 -0
- package/cli/process-manager.js +73 -2
- package/cli/prompt-logger.js +57 -0
- package/cli/repl-ink.js +4625 -1094
- package/cli/repl-old.js +3 -4
- package/cli/seed-processor.js +962 -0
- package/cli/sprint-planning-processor.js +4162 -0
- package/cli/template-processor.js +2149 -105
- package/cli/templates/project.md +25 -8
- package/cli/templates/vitepress-config.mts.template +5 -4
- package/cli/token-tracker.js +547 -0
- package/cli/tools/generate-story-validators.js +317 -0
- package/cli/tools/generate-validators.js +669 -0
- package/cli/update-checker.js +19 -17
- package/cli/update-notifier.js +4 -4
- package/cli/validation-router.js +667 -0
- package/cli/verification-tracker.js +563 -0
- package/cli/worktree-runner.js +654 -0
- package/kanban/README.md +386 -0
- package/kanban/client/README.md +205 -0
- package/kanban/client/components.json +20 -0
- package/kanban/client/dist/assets/index-D_KC5EQT.css +1 -0
- package/kanban/client/dist/assets/index-DjY5zqW7.js +351 -0
- package/kanban/client/dist/index.html +16 -0
- package/kanban/client/dist/vite.svg +1 -0
- package/kanban/client/index.html +15 -0
- package/kanban/client/package-lock.json +9442 -0
- package/kanban/client/package.json +44 -0
- package/kanban/client/postcss.config.js +6 -0
- package/kanban/client/public/vite.svg +1 -0
- package/kanban/client/src/App.jsx +651 -0
- package/kanban/client/src/components/ProjectFileEditorPopup.jsx +117 -0
- package/kanban/client/src/components/ceremony/AskArchPopup.jsx +420 -0
- package/kanban/client/src/components/ceremony/AskModelPopup.jsx +629 -0
- package/kanban/client/src/components/ceremony/CeremonyWorkflowModal.jsx +1133 -0
- package/kanban/client/src/components/ceremony/EpicStorySelectionModal.jsx +254 -0
- package/kanban/client/src/components/ceremony/ProviderSwitcherButton.jsx +290 -0
- package/kanban/client/src/components/ceremony/SponsorCallModal.jsx +686 -0
- package/kanban/client/src/components/ceremony/SprintPlanningModal.jsx +838 -0
- package/kanban/client/src/components/ceremony/steps/ArchitectureStep.jsx +150 -0
- package/kanban/client/src/components/ceremony/steps/CompleteStep.jsx +136 -0
- package/kanban/client/src/components/ceremony/steps/DatabaseStep.jsx +202 -0
- package/kanban/client/src/components/ceremony/steps/DeploymentStep.jsx +123 -0
- package/kanban/client/src/components/ceremony/steps/MissionStep.jsx +106 -0
- package/kanban/client/src/components/ceremony/steps/ReviewAnswersStep.jsx +329 -0
- package/kanban/client/src/components/ceremony/steps/RunningStep.jsx +249 -0
- package/kanban/client/src/components/kanban/CardDetailModal.jsx +646 -0
- package/kanban/client/src/components/kanban/EpicSection.jsx +146 -0
- package/kanban/client/src/components/kanban/FilterToolbar.jsx +222 -0
- package/kanban/client/src/components/kanban/GroupingSelector.jsx +63 -0
- package/kanban/client/src/components/kanban/KanbanBoard.jsx +211 -0
- package/kanban/client/src/components/kanban/KanbanCard.jsx +147 -0
- package/kanban/client/src/components/kanban/KanbanColumn.jsx +90 -0
- package/kanban/client/src/components/kanban/RefineWorkItemPopup.jsx +784 -0
- package/kanban/client/src/components/kanban/RunButton.jsx +162 -0
- package/kanban/client/src/components/kanban/SeedButton.jsx +176 -0
- package/kanban/client/src/components/layout/LoadingScreen.jsx +82 -0
- package/kanban/client/src/components/process/ProcessMonitorBar.jsx +80 -0
- package/kanban/client/src/components/settings/AgentEditorPopup.jsx +171 -0
- package/kanban/client/src/components/settings/AgentsTab.jsx +381 -0
- package/kanban/client/src/components/settings/ApiKeysTab.jsx +142 -0
- package/kanban/client/src/components/settings/CeremonyModelsTab.jsx +105 -0
- package/kanban/client/src/components/settings/CheckEditorPopup.jsx +507 -0
- package/kanban/client/src/components/settings/CostThresholdsTab.jsx +95 -0
- package/kanban/client/src/components/settings/ModelPricingTab.jsx +269 -0
- package/kanban/client/src/components/settings/OpenAIAuthSection.jsx +412 -0
- package/kanban/client/src/components/settings/ServersTab.jsx +121 -0
- package/kanban/client/src/components/settings/SettingsModal.jsx +84 -0
- package/kanban/client/src/components/stats/CostModal.jsx +384 -0
- package/kanban/client/src/components/ui/badge.jsx +27 -0
- package/kanban/client/src/components/ui/dialog.jsx +121 -0
- package/kanban/client/src/components/ui/tabs.jsx +85 -0
- package/kanban/client/src/hooks/__tests__/useGrouping.test.js +232 -0
- package/kanban/client/src/hooks/useGrouping.js +177 -0
- package/kanban/client/src/hooks/useWebSocket.js +120 -0
- package/kanban/client/src/lib/__tests__/api.test.js +196 -0
- package/kanban/client/src/lib/__tests__/status-grouping.test.js +94 -0
- package/kanban/client/src/lib/api.js +515 -0
- package/kanban/client/src/lib/status-grouping.js +154 -0
- package/kanban/client/src/lib/utils.js +11 -0
- package/kanban/client/src/main.jsx +10 -0
- package/kanban/client/src/store/__tests__/kanbanStore.test.js +164 -0
- package/kanban/client/src/store/ceremonyStore.js +172 -0
- package/kanban/client/src/store/filterStore.js +201 -0
- package/kanban/client/src/store/kanbanStore.js +123 -0
- package/kanban/client/src/store/processStore.js +65 -0
- package/kanban/client/src/store/sprintPlanningStore.js +33 -0
- package/kanban/client/src/styles/globals.css +59 -0
- package/kanban/client/tailwind.config.js +77 -0
- package/kanban/client/vite.config.js +28 -0
- package/kanban/client/vitest.config.js +28 -0
- package/kanban/dev-start.sh +47 -0
- package/kanban/package.json +12 -0
- package/kanban/server/index.js +537 -0
- package/kanban/server/routes/ceremony.js +454 -0
- package/kanban/server/routes/costs.js +163 -0
- package/kanban/server/routes/openai-oauth.js +366 -0
- package/kanban/server/routes/processes.js +50 -0
- package/kanban/server/routes/settings.js +736 -0
- package/kanban/server/routes/websocket.js +281 -0
- package/kanban/server/routes/work-items.js +487 -0
- package/kanban/server/services/CeremonyService.js +1441 -0
- package/kanban/server/services/FileSystemScanner.js +95 -0
- package/kanban/server/services/FileWatcher.js +144 -0
- package/kanban/server/services/HierarchyBuilder.js +196 -0
- package/kanban/server/services/ProcessRegistry.js +122 -0
- package/kanban/server/services/TaskRunnerService.js +261 -0
- package/kanban/server/services/WorkItemReader.js +123 -0
- package/kanban/server/services/WorkItemRefineService.js +510 -0
- package/kanban/server/start.js +49 -0
- package/kanban/server/utils/kanban-logger.js +132 -0
- package/kanban/server/utils/markdown.js +91 -0
- package/kanban/server/utils/status-grouping.js +107 -0
- package/kanban/server/workers/run-task-worker.js +121 -0
- package/kanban/server/workers/seed-worker.js +94 -0
- package/kanban/server/workers/sponsor-call-worker.js +92 -0
- package/kanban/server/workers/sprint-planning-worker.js +212 -0
- package/package.json +19 -7
- package/cli/agents/documentation.md +0 -302
|
@@ -0,0 +1,667 @@
|
|
|
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), filtered by project context
|
|
203
|
+
return this.filterByProjectContext(Array.from(validators), 'epic');
|
|
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), filtered by project context
|
|
237
|
+
return this.filterByProjectContext(Array.from(validators), 'story');
|
|
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
|
+
* 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
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Check if a domain is known (has predefined routing rules)
|
|
458
|
+
* @private
|
|
459
|
+
*/
|
|
460
|
+
isDomainKnown(domain, type = 'epic') {
|
|
461
|
+
const matrix = type === 'epic' ? this.epicMatrix : this.storyMatrix;
|
|
462
|
+
return !!matrix.domains[domain];
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Get validators for epic with hybrid approach (static + LLM fallback)
|
|
467
|
+
* @param {Object} epic - Epic work item
|
|
468
|
+
* @returns {Promise<string[]>} Array of validator names
|
|
469
|
+
*/
|
|
470
|
+
async getValidatorsForEpicWithLLM(epic) {
|
|
471
|
+
const validators = new Set();
|
|
472
|
+
|
|
473
|
+
// 1. Always add universal validators (static)
|
|
474
|
+
this.epicMatrix.universal.forEach(v => validators.add(v));
|
|
475
|
+
|
|
476
|
+
// 2. Check if domain is known
|
|
477
|
+
const domainKnown = this.isDomainKnown(epic.domain, 'epic');
|
|
478
|
+
|
|
479
|
+
if (domainKnown) {
|
|
480
|
+
// Fast path: Use static routing for known domains
|
|
481
|
+
const domainValidators = this.epicMatrix.domains[epic.domain];
|
|
482
|
+
domainValidators.forEach(v => validators.add(v));
|
|
483
|
+
} else if (this.useSmartSelection && this.llmProvider) {
|
|
484
|
+
// Slow path: Use LLM for unknown domains
|
|
485
|
+
console.log(` 📡 Unknown domain "${epic.domain}" - using LLM selection...`);
|
|
486
|
+
const llmValidators = await this.llmSelectValidators(epic, 'epic');
|
|
487
|
+
llmValidators.forEach(v => validators.add(v));
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// 3. Add feature-specific validators (static)
|
|
491
|
+
(epic.features || []).forEach(feature => {
|
|
492
|
+
const featureNormalized = feature.toLowerCase().replace(/\s+/g, '-');
|
|
493
|
+
const featureValidators = this.epicMatrix.features[featureNormalized] || [];
|
|
494
|
+
featureValidators.forEach(v => validators.add(v));
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
return this.filterByProjectContext(Array.from(validators), 'epic');
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Get validators for story with hybrid approach (static + LLM fallback)
|
|
502
|
+
* @param {Object} story - Story work item
|
|
503
|
+
* @param {Object} epic - Parent epic for context
|
|
504
|
+
* @returns {Promise<string[]>} Array of validator names
|
|
505
|
+
*/
|
|
506
|
+
async getValidatorsForStoryWithLLM(story, epic) {
|
|
507
|
+
const validators = new Set();
|
|
508
|
+
|
|
509
|
+
// 1. Add universal validators (static)
|
|
510
|
+
this.storyMatrix.universal.forEach(v => validators.add(v));
|
|
511
|
+
|
|
512
|
+
// 2. Check if domain is known
|
|
513
|
+
const domainKnown = this.isDomainKnown(epic.domain, 'story');
|
|
514
|
+
|
|
515
|
+
if (domainKnown) {
|
|
516
|
+
// Fast path: Use static routing for known domains
|
|
517
|
+
const domainValidators = this.storyMatrix.domains[epic.domain];
|
|
518
|
+
domainValidators.forEach(v => validators.add(v));
|
|
519
|
+
} else if (this.useSmartSelection && this.llmProvider) {
|
|
520
|
+
// Slow path: Use LLM for unknown domains
|
|
521
|
+
console.log(` 📡 Unknown domain "${epic.domain}" - using LLM selection...`);
|
|
522
|
+
const llmValidators = await this.llmSelectValidators(story, 'story');
|
|
523
|
+
llmValidators.forEach(v => validators.add(v));
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// 3. Add feature-specific validators from epic (static)
|
|
527
|
+
(epic.features || []).forEach(feature => {
|
|
528
|
+
const featureNormalized = feature.toLowerCase().replace(/\s+/g, '-');
|
|
529
|
+
const featureValidators = this.storyMatrix.features[featureNormalized] || [];
|
|
530
|
+
featureValidators.forEach(v => validators.add(v));
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
// 4. Infer features from acceptance criteria (static)
|
|
534
|
+
const inferredFeatures = this.inferFeaturesFromAcceptance(story.acceptance);
|
|
535
|
+
inferredFeatures.forEach(feature => {
|
|
536
|
+
const featureValidators = this.storyMatrix.features[feature] || [];
|
|
537
|
+
featureValidators.forEach(v => validators.add(v));
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
return this.filterByProjectContext(Array.from(validators), 'story');
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Select validators using project context + per-item LLM call (contextual selection).
|
|
544
|
+
* Falls back to static routing if LLM call fails or returns < 2 validators.
|
|
545
|
+
* @param {Object} item - Epic or Story work item
|
|
546
|
+
* @param {string} type - 'epic' or 'story'
|
|
547
|
+
* @param {Object} llmProvider - LLM provider instance to use for selection
|
|
548
|
+
* @param {Object} [parentEpic] - Parent epic (required when type='story')
|
|
549
|
+
* @returns {Promise<string[]>} Array of full validator agent names
|
|
550
|
+
*/
|
|
551
|
+
async selectValidatorsWithContext(item, type, llmProvider, parentEpic = null) {
|
|
552
|
+
const validRoles = [
|
|
553
|
+
'solution-architect', 'developer', 'security', 'devops', 'cloud',
|
|
554
|
+
'backend', 'database', 'api', 'frontend', 'ui', 'ux', 'mobile',
|
|
555
|
+
'data', 'qa', 'test-architect'
|
|
556
|
+
];
|
|
557
|
+
|
|
558
|
+
let selectorAgent;
|
|
559
|
+
try {
|
|
560
|
+
selectorAgent = loadAgent('agent-selector.md');
|
|
561
|
+
} catch (err) {
|
|
562
|
+
console.warn(` ⚠ Could not load agent-selector.md (${err.message}) — using static routing`);
|
|
563
|
+
return type === 'epic'
|
|
564
|
+
? await this.getValidatorsForEpicWithLLM(item)
|
|
565
|
+
: await this.getValidatorsForStoryWithLLM(item, parentEpic ?? item);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
if (!llmProvider) {
|
|
569
|
+
console.warn(` ⚠ Contextual agent selection skipped (no LLM provider) — using static routing`);
|
|
570
|
+
return type === 'epic'
|
|
571
|
+
? await this.getValidatorsForEpicWithLLM(item)
|
|
572
|
+
: await this.getValidatorsForStoryWithLLM(item, parentEpic ?? item);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
const prompt = this.buildAgentSelectionPrompt(item, type, this.projectContext || {}, parentEpic);
|
|
576
|
+
console.log(`[DEBUG] selectValidatorsWithContext: type=${type}, item="${item.name}", projectContext keys=${Object.keys(this.projectContext || {}).join(',') || 'none'}`);
|
|
577
|
+
|
|
578
|
+
try {
|
|
579
|
+
const response = await llmProvider.generateJSON(prompt, selectorAgent);
|
|
580
|
+
|
|
581
|
+
if (!response?.selected || !Array.isArray(response.selected)) {
|
|
582
|
+
throw new Error('missing selected array in response');
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Filter to known role names only
|
|
586
|
+
const safeSelected = response.selected.filter(r => validRoles.includes(r));
|
|
587
|
+
|
|
588
|
+
// Safety floor — always include these two
|
|
589
|
+
if (!safeSelected.includes('solution-architect')) safeSelected.push('solution-architect');
|
|
590
|
+
if (!safeSelected.includes('developer')) safeSelected.push('developer');
|
|
591
|
+
|
|
592
|
+
// Log excluded roles with reasons
|
|
593
|
+
const excluded = response.excluded || [];
|
|
594
|
+
const reasons = response.reasons || {};
|
|
595
|
+
excluded.forEach(role => {
|
|
596
|
+
const reason = reasons[role] ? `: ${reasons[role]}` : '';
|
|
597
|
+
console.log(` ✂ Excluding ${type} ${role}${reason}`);
|
|
598
|
+
});
|
|
599
|
+
|
|
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);
|
|
602
|
+
} catch (err) {
|
|
603
|
+
console.warn(` ⚠ Contextual agent selection failed (${err.message}) — using static routing`);
|
|
604
|
+
return type === 'epic'
|
|
605
|
+
? await this.getValidatorsForEpicWithLLM(item)
|
|
606
|
+
: await this.getValidatorsForStoryWithLLM(item, parentEpic ?? item);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Build the prompt for contextual agent selection.
|
|
612
|
+
* @param {Object} item - Epic or Story
|
|
613
|
+
* @param {string} type - 'epic' or 'story'
|
|
614
|
+
* @param {Object} projectContext - Extracted project context
|
|
615
|
+
* @param {Object} [parentEpic] - Parent epic for stories
|
|
616
|
+
* @returns {string} Prompt text
|
|
617
|
+
*/
|
|
618
|
+
buildAgentSelectionPrompt(item, type, projectContext, parentEpic = null) {
|
|
619
|
+
const ctx = projectContext || {};
|
|
620
|
+
const lines = [];
|
|
621
|
+
|
|
622
|
+
// Project context section
|
|
623
|
+
if (Object.keys(ctx).length > 0) {
|
|
624
|
+
lines.push('PROJECT CONTEXT:');
|
|
625
|
+
if (ctx.deploymentType) lines.push(`- Deployment: ${ctx.deploymentType}${ctx.hasCloud ? ' (cloud services present)' : ' (no cloud services)'}`);
|
|
626
|
+
if (ctx.techStack?.length) lines.push(`- Tech stack: ${ctx.techStack.join(', ')}`);
|
|
627
|
+
lines.push(`- CI/CD pipeline: ${ctx.hasCI_CD ? 'yes' : 'no'}`);
|
|
628
|
+
lines.push(`- Mobile app: ${ctx.hasMobileApp ? 'yes' : 'no'}`);
|
|
629
|
+
lines.push(`- Frontend: ${ctx.hasFrontend ? 'yes' : 'no'}`);
|
|
630
|
+
lines.push(`- Public API: ${ctx.hasPublicAPI ? 'yes' : 'no'}`);
|
|
631
|
+
if (ctx.projectType) lines.push(`- Project type: ${ctx.projectType}`);
|
|
632
|
+
if (ctx.teamContext) lines.push(`- Team: ${ctx.teamContext}`);
|
|
633
|
+
lines.push('');
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// Work item section
|
|
637
|
+
if (type === 'epic') {
|
|
638
|
+
lines.push('ITEM TO VALIDATE (Epic):');
|
|
639
|
+
lines.push(`- Name: ${item.name}`);
|
|
640
|
+
lines.push(`- Domain: ${item.domain}`);
|
|
641
|
+
if (item.description) lines.push(`- Description: ${item.description}`);
|
|
642
|
+
if (item.features?.length) {
|
|
643
|
+
lines.push(`- Features:`);
|
|
644
|
+
item.features.forEach(f => lines.push(` * ${f}`));
|
|
645
|
+
}
|
|
646
|
+
} else {
|
|
647
|
+
lines.push('ITEM TO VALIDATE (Story):');
|
|
648
|
+
lines.push(`- Name: ${item.name}`);
|
|
649
|
+
if (item.userType) lines.push(`- User type: ${item.userType}`);
|
|
650
|
+
if (item.description) lines.push(`- Description: ${item.description}`);
|
|
651
|
+
if (parentEpic) {
|
|
652
|
+
lines.push(`- Parent Epic: ${parentEpic.name} (domain: ${parentEpic.domain})`);
|
|
653
|
+
}
|
|
654
|
+
if (item.acceptance?.length) {
|
|
655
|
+
lines.push(`- Acceptance Criteria:`);
|
|
656
|
+
item.acceptance.forEach((ac, i) => lines.push(` ${i + 1}. ${ac}`));
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
lines.push('');
|
|
661
|
+
lines.push('Select which validator roles are relevant for this specific item given the project context above.');
|
|
662
|
+
|
|
663
|
+
return lines.join('\n');
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
export { ValidationRouter };
|