@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.
Files changed (289) hide show
  1. package/cli/agent-loader.js +21 -0
  2. package/cli/agents/agent-selector.md +129 -0
  3. package/cli/agents/architecture-recommender.md +418 -0
  4. package/cli/agents/database-deep-dive.md +470 -0
  5. package/cli/agents/database-recommender.md +634 -0
  6. package/cli/agents/doc-distributor.md +176 -0
  7. package/cli/agents/documentation-updater.md +203 -0
  8. package/cli/agents/epic-story-decomposer.md +280 -0
  9. package/cli/agents/feature-context-generator.md +91 -0
  10. package/cli/agents/gap-checker-epic.md +52 -0
  11. package/cli/agents/impact-checker-story.md +51 -0
  12. package/cli/agents/migration-guide-generator.md +305 -0
  13. package/cli/agents/mission-scope-generator.md +79 -0
  14. package/cli/agents/mission-scope-validator.md +112 -0
  15. package/cli/agents/project-context-extractor.md +107 -0
  16. package/cli/agents/project-documentation-creator.json +226 -0
  17. package/cli/agents/project-documentation-creator.md +595 -0
  18. package/cli/agents/question-prefiller.md +269 -0
  19. package/cli/agents/refiner-epic.md +39 -0
  20. package/cli/agents/refiner-story.md +42 -0
  21. package/cli/agents/solver-epic-api.json +15 -0
  22. package/cli/agents/solver-epic-api.md +39 -0
  23. package/cli/agents/solver-epic-backend.json +15 -0
  24. package/cli/agents/solver-epic-backend.md +39 -0
  25. package/cli/agents/solver-epic-cloud.json +15 -0
  26. package/cli/agents/solver-epic-cloud.md +39 -0
  27. package/cli/agents/solver-epic-data.json +15 -0
  28. package/cli/agents/solver-epic-data.md +39 -0
  29. package/cli/agents/solver-epic-database.json +15 -0
  30. package/cli/agents/solver-epic-database.md +39 -0
  31. package/cli/agents/solver-epic-developer.json +15 -0
  32. package/cli/agents/solver-epic-developer.md +39 -0
  33. package/cli/agents/solver-epic-devops.json +15 -0
  34. package/cli/agents/solver-epic-devops.md +39 -0
  35. package/cli/agents/solver-epic-frontend.json +15 -0
  36. package/cli/agents/solver-epic-frontend.md +39 -0
  37. package/cli/agents/solver-epic-mobile.json +15 -0
  38. package/cli/agents/solver-epic-mobile.md +39 -0
  39. package/cli/agents/solver-epic-qa.json +15 -0
  40. package/cli/agents/solver-epic-qa.md +39 -0
  41. package/cli/agents/solver-epic-security.json +15 -0
  42. package/cli/agents/solver-epic-security.md +39 -0
  43. package/cli/agents/solver-epic-solution-architect.json +15 -0
  44. package/cli/agents/solver-epic-solution-architect.md +39 -0
  45. package/cli/agents/solver-epic-test-architect.json +15 -0
  46. package/cli/agents/solver-epic-test-architect.md +39 -0
  47. package/cli/agents/solver-epic-ui.json +15 -0
  48. package/cli/agents/solver-epic-ui.md +39 -0
  49. package/cli/agents/solver-epic-ux.json +15 -0
  50. package/cli/agents/solver-epic-ux.md +39 -0
  51. package/cli/agents/solver-story-api.json +15 -0
  52. package/cli/agents/solver-story-api.md +39 -0
  53. package/cli/agents/solver-story-backend.json +15 -0
  54. package/cli/agents/solver-story-backend.md +39 -0
  55. package/cli/agents/solver-story-cloud.json +15 -0
  56. package/cli/agents/solver-story-cloud.md +39 -0
  57. package/cli/agents/solver-story-data.json +15 -0
  58. package/cli/agents/solver-story-data.md +39 -0
  59. package/cli/agents/solver-story-database.json +15 -0
  60. package/cli/agents/solver-story-database.md +39 -0
  61. package/cli/agents/solver-story-developer.json +15 -0
  62. package/cli/agents/solver-story-developer.md +39 -0
  63. package/cli/agents/solver-story-devops.json +15 -0
  64. package/cli/agents/solver-story-devops.md +39 -0
  65. package/cli/agents/solver-story-frontend.json +15 -0
  66. package/cli/agents/solver-story-frontend.md +39 -0
  67. package/cli/agents/solver-story-mobile.json +15 -0
  68. package/cli/agents/solver-story-mobile.md +39 -0
  69. package/cli/agents/solver-story-qa.json +15 -0
  70. package/cli/agents/solver-story-qa.md +39 -0
  71. package/cli/agents/solver-story-security.json +15 -0
  72. package/cli/agents/solver-story-security.md +39 -0
  73. package/cli/agents/solver-story-solution-architect.json +15 -0
  74. package/cli/agents/solver-story-solution-architect.md +39 -0
  75. package/cli/agents/solver-story-test-architect.json +15 -0
  76. package/cli/agents/solver-story-test-architect.md +39 -0
  77. package/cli/agents/solver-story-ui.json +15 -0
  78. package/cli/agents/solver-story-ui.md +39 -0
  79. package/cli/agents/solver-story-ux.json +15 -0
  80. package/cli/agents/solver-story-ux.md +39 -0
  81. package/cli/agents/story-doc-enricher.md +133 -0
  82. package/cli/agents/suggestion-business-analyst.md +88 -0
  83. package/cli/agents/suggestion-deployment-architect.md +263 -0
  84. package/cli/agents/suggestion-product-manager.md +129 -0
  85. package/cli/agents/suggestion-security-specialist.md +156 -0
  86. package/cli/agents/suggestion-technical-architect.md +269 -0
  87. package/cli/agents/suggestion-ux-researcher.md +93 -0
  88. package/cli/agents/task-subtask-decomposer.md +188 -0
  89. package/cli/agents/validator-documentation.json +152 -0
  90. package/cli/agents/validator-documentation.md +453 -0
  91. package/cli/agents/validator-epic-api.json +93 -0
  92. package/cli/agents/validator-epic-api.md +137 -0
  93. package/cli/agents/validator-epic-backend.json +93 -0
  94. package/cli/agents/validator-epic-backend.md +130 -0
  95. package/cli/agents/validator-epic-cloud.json +93 -0
  96. package/cli/agents/validator-epic-cloud.md +137 -0
  97. package/cli/agents/validator-epic-data.json +93 -0
  98. package/cli/agents/validator-epic-data.md +130 -0
  99. package/cli/agents/validator-epic-database.json +93 -0
  100. package/cli/agents/validator-epic-database.md +137 -0
  101. package/cli/agents/validator-epic-developer.json +74 -0
  102. package/cli/agents/validator-epic-developer.md +153 -0
  103. package/cli/agents/validator-epic-devops.json +74 -0
  104. package/cli/agents/validator-epic-devops.md +153 -0
  105. package/cli/agents/validator-epic-frontend.json +74 -0
  106. package/cli/agents/validator-epic-frontend.md +153 -0
  107. package/cli/agents/validator-epic-mobile.json +93 -0
  108. package/cli/agents/validator-epic-mobile.md +130 -0
  109. package/cli/agents/validator-epic-qa.json +93 -0
  110. package/cli/agents/validator-epic-qa.md +130 -0
  111. package/cli/agents/validator-epic-security.json +74 -0
  112. package/cli/agents/validator-epic-security.md +154 -0
  113. package/cli/agents/validator-epic-solution-architect.json +74 -0
  114. package/cli/agents/validator-epic-solution-architect.md +156 -0
  115. package/cli/agents/validator-epic-test-architect.json +93 -0
  116. package/cli/agents/validator-epic-test-architect.md +130 -0
  117. package/cli/agents/validator-epic-ui.json +93 -0
  118. package/cli/agents/validator-epic-ui.md +130 -0
  119. package/cli/agents/validator-epic-ux.json +93 -0
  120. package/cli/agents/validator-epic-ux.md +130 -0
  121. package/cli/agents/validator-selector.md +211 -0
  122. package/cli/agents/validator-story-api.json +104 -0
  123. package/cli/agents/validator-story-api.md +152 -0
  124. package/cli/agents/validator-story-backend.json +104 -0
  125. package/cli/agents/validator-story-backend.md +152 -0
  126. package/cli/agents/validator-story-cloud.json +104 -0
  127. package/cli/agents/validator-story-cloud.md +152 -0
  128. package/cli/agents/validator-story-data.json +104 -0
  129. package/cli/agents/validator-story-data.md +152 -0
  130. package/cli/agents/validator-story-database.json +104 -0
  131. package/cli/agents/validator-story-database.md +152 -0
  132. package/cli/agents/validator-story-developer.json +104 -0
  133. package/cli/agents/validator-story-developer.md +152 -0
  134. package/cli/agents/validator-story-devops.json +104 -0
  135. package/cli/agents/validator-story-devops.md +152 -0
  136. package/cli/agents/validator-story-frontend.json +104 -0
  137. package/cli/agents/validator-story-frontend.md +152 -0
  138. package/cli/agents/validator-story-mobile.json +104 -0
  139. package/cli/agents/validator-story-mobile.md +152 -0
  140. package/cli/agents/validator-story-qa.json +104 -0
  141. package/cli/agents/validator-story-qa.md +152 -0
  142. package/cli/agents/validator-story-security.json +104 -0
  143. package/cli/agents/validator-story-security.md +152 -0
  144. package/cli/agents/validator-story-solution-architect.json +104 -0
  145. package/cli/agents/validator-story-solution-architect.md +152 -0
  146. package/cli/agents/validator-story-test-architect.json +104 -0
  147. package/cli/agents/validator-story-test-architect.md +152 -0
  148. package/cli/agents/validator-story-ui.json +104 -0
  149. package/cli/agents/validator-story-ui.md +152 -0
  150. package/cli/agents/validator-story-ux.json +104 -0
  151. package/cli/agents/validator-story-ux.md +152 -0
  152. package/cli/ansi-colors.js +21 -0
  153. package/cli/build-docs.js +29 -8
  154. package/cli/ceremony-history.js +369 -0
  155. package/cli/command-logger.js +49 -12
  156. package/cli/components/static-output.js +63 -0
  157. package/cli/console-output-manager.js +94 -0
  158. package/cli/docs-sync.js +306 -0
  159. package/cli/epic-story-validator.js +1174 -0
  160. package/cli/evaluation-prompts.js +1008 -0
  161. package/cli/execution-context.js +195 -0
  162. package/cli/generate-summary-table.js +340 -0
  163. package/cli/index.js +0 -0
  164. package/cli/init-model-config.js +697 -0
  165. package/cli/init.js +1311 -274
  166. package/cli/kanban-server-manager.js +228 -0
  167. package/cli/llm-claude.js +83 -1
  168. package/cli/llm-gemini.js +85 -0
  169. package/cli/llm-mock.js +233 -0
  170. package/cli/llm-openai.js +233 -0
  171. package/cli/llm-provider.js +240 -3
  172. package/cli/llm-token-limits.js +102 -0
  173. package/cli/llm-verifier.js +454 -0
  174. package/cli/message-constants.js +58 -0
  175. package/cli/message-manager.js +334 -0
  176. package/cli/message-types.js +96 -0
  177. package/cli/messaging-api.js +297 -0
  178. package/cli/model-pricing.js +169 -0
  179. package/cli/model-query-engine.js +468 -0
  180. package/cli/model-recommendation-analyzer.js +495 -0
  181. package/cli/model-selector.js +269 -0
  182. package/cli/output-buffer.js +107 -0
  183. package/cli/process-manager.js +73 -2
  184. package/cli/repl-ink.js +4988 -1217
  185. package/cli/repl-old.js +4 -4
  186. package/cli/seed-processor.js +792 -0
  187. package/cli/sprint-planning-processor.js +1813 -0
  188. package/cli/template-processor.js +2102 -105
  189. package/cli/templates/project.md +25 -8
  190. package/cli/templates/vitepress-config.mts.template +5 -4
  191. package/cli/token-tracker.js +520 -0
  192. package/cli/tools/generate-story-validators.js +317 -0
  193. package/cli/tools/generate-validators.js +669 -0
  194. package/cli/update-checker.js +19 -17
  195. package/cli/update-notifier.js +4 -4
  196. package/cli/validation-router.js +605 -0
  197. package/cli/verification-tracker.js +563 -0
  198. package/kanban/README.md +386 -0
  199. package/kanban/client/README.md +205 -0
  200. package/kanban/client/components.json +20 -0
  201. package/kanban/client/dist/assets/index-CiD8PS2e.js +306 -0
  202. package/kanban/client/dist/assets/index-nLh0m82Q.css +1 -0
  203. package/kanban/client/dist/index.html +16 -0
  204. package/kanban/client/dist/vite.svg +1 -0
  205. package/kanban/client/index.html +15 -0
  206. package/kanban/client/package-lock.json +9442 -0
  207. package/kanban/client/package.json +44 -0
  208. package/kanban/client/postcss.config.js +6 -0
  209. package/kanban/client/public/vite.svg +1 -0
  210. package/kanban/client/src/App.jsx +622 -0
  211. package/kanban/client/src/components/ProjectFileEditorPopup.jsx +117 -0
  212. package/kanban/client/src/components/ceremony/AskArchPopup.jsx +416 -0
  213. package/kanban/client/src/components/ceremony/AskModelPopup.jsx +616 -0
  214. package/kanban/client/src/components/ceremony/CeremonyWorkflowModal.jsx +946 -0
  215. package/kanban/client/src/components/ceremony/EpicStorySelectionModal.jsx +254 -0
  216. package/kanban/client/src/components/ceremony/SponsorCallModal.jsx +619 -0
  217. package/kanban/client/src/components/ceremony/SprintPlanningModal.jsx +704 -0
  218. package/kanban/client/src/components/ceremony/steps/ArchitectureStep.jsx +150 -0
  219. package/kanban/client/src/components/ceremony/steps/CompleteStep.jsx +154 -0
  220. package/kanban/client/src/components/ceremony/steps/DatabaseStep.jsx +202 -0
  221. package/kanban/client/src/components/ceremony/steps/DeploymentStep.jsx +123 -0
  222. package/kanban/client/src/components/ceremony/steps/MissionStep.jsx +106 -0
  223. package/kanban/client/src/components/ceremony/steps/ReviewAnswersStep.jsx +125 -0
  224. package/kanban/client/src/components/ceremony/steps/RunningStep.jsx +228 -0
  225. package/kanban/client/src/components/kanban/CardDetailModal.jsx +559 -0
  226. package/kanban/client/src/components/kanban/EpicSection.jsx +146 -0
  227. package/kanban/client/src/components/kanban/FilterToolbar.jsx +222 -0
  228. package/kanban/client/src/components/kanban/GroupingSelector.jsx +57 -0
  229. package/kanban/client/src/components/kanban/KanbanBoard.jsx +211 -0
  230. package/kanban/client/src/components/kanban/KanbanCard.jsx +138 -0
  231. package/kanban/client/src/components/kanban/KanbanColumn.jsx +90 -0
  232. package/kanban/client/src/components/kanban/RefineWorkItemPopup.jsx +789 -0
  233. package/kanban/client/src/components/layout/LoadingScreen.jsx +82 -0
  234. package/kanban/client/src/components/process/ProcessMonitorBar.jsx +80 -0
  235. package/kanban/client/src/components/settings/AgentEditorPopup.jsx +171 -0
  236. package/kanban/client/src/components/settings/AgentsTab.jsx +353 -0
  237. package/kanban/client/src/components/settings/ApiKeysTab.jsx +113 -0
  238. package/kanban/client/src/components/settings/CeremonyModelsTab.jsx +98 -0
  239. package/kanban/client/src/components/settings/CostThresholdsTab.jsx +94 -0
  240. package/kanban/client/src/components/settings/ModelPricingTab.jsx +204 -0
  241. package/kanban/client/src/components/settings/ServersTab.jsx +121 -0
  242. package/kanban/client/src/components/settings/SettingsModal.jsx +84 -0
  243. package/kanban/client/src/components/stats/CostModal.jsx +353 -0
  244. package/kanban/client/src/components/ui/badge.jsx +27 -0
  245. package/kanban/client/src/components/ui/dialog.jsx +121 -0
  246. package/kanban/client/src/components/ui/tabs.jsx +85 -0
  247. package/kanban/client/src/hooks/__tests__/useGrouping.test.js +232 -0
  248. package/kanban/client/src/hooks/useGrouping.js +118 -0
  249. package/kanban/client/src/hooks/useWebSocket.js +120 -0
  250. package/kanban/client/src/lib/__tests__/api.test.js +196 -0
  251. package/kanban/client/src/lib/__tests__/status-grouping.test.js +94 -0
  252. package/kanban/client/src/lib/api.js +401 -0
  253. package/kanban/client/src/lib/status-grouping.js +144 -0
  254. package/kanban/client/src/lib/utils.js +11 -0
  255. package/kanban/client/src/main.jsx +10 -0
  256. package/kanban/client/src/store/__tests__/kanbanStore.test.js +164 -0
  257. package/kanban/client/src/store/ceremonyStore.js +172 -0
  258. package/kanban/client/src/store/filterStore.js +201 -0
  259. package/kanban/client/src/store/kanbanStore.js +115 -0
  260. package/kanban/client/src/store/processStore.js +65 -0
  261. package/kanban/client/src/store/sprintPlanningStore.js +33 -0
  262. package/kanban/client/src/styles/globals.css +59 -0
  263. package/kanban/client/tailwind.config.js +77 -0
  264. package/kanban/client/vite.config.js +28 -0
  265. package/kanban/client/vitest.config.js +28 -0
  266. package/kanban/dev-start.sh +47 -0
  267. package/kanban/package.json +12 -0
  268. package/kanban/server/index.js +516 -0
  269. package/kanban/server/routes/ceremony.js +305 -0
  270. package/kanban/server/routes/costs.js +157 -0
  271. package/kanban/server/routes/processes.js +50 -0
  272. package/kanban/server/routes/settings.js +303 -0
  273. package/kanban/server/routes/websocket.js +276 -0
  274. package/kanban/server/routes/work-items.js +347 -0
  275. package/kanban/server/services/CeremonyService.js +1190 -0
  276. package/kanban/server/services/FileSystemScanner.js +95 -0
  277. package/kanban/server/services/FileWatcher.js +144 -0
  278. package/kanban/server/services/HierarchyBuilder.js +196 -0
  279. package/kanban/server/services/ProcessRegistry.js +122 -0
  280. package/kanban/server/services/WorkItemReader.js +123 -0
  281. package/kanban/server/services/WorkItemRefineService.js +510 -0
  282. package/kanban/server/start.js +49 -0
  283. package/kanban/server/utils/kanban-logger.js +132 -0
  284. package/kanban/server/utils/markdown.js +91 -0
  285. package/kanban/server/utils/status-grouping.js +107 -0
  286. package/kanban/server/workers/sponsor-call-worker.js +84 -0
  287. package/kanban/server/workers/sprint-planning-worker.js +130 -0
  288. package/package.json +18 -5
  289. package/cli/agents/documentation.md +0 -302
@@ -1,4 +1,4 @@
1
- import { execSync } from 'child_process';
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('Failed to read current version:', error.message);
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
- try {
81
+ return new Promise((resolve) => {
82
82
  updateLogger.debug('Checking npm registry for latest version');
83
- const result = execSync(`npm view ${this.packageName} version`, {
84
- encoding: 'utf8',
85
- timeout: 10000,
86
- stdio: ['pipe', 'pipe', 'ignore'] // Suppress stderr
87
- });
88
- const version = result.trim();
89
- updateLogger.debug(`Latest version on npm: ${version}`);
90
- return version;
91
- } catch (error) {
92
- updateLogger.error('Failed to check npm registry', error);
93
- // Silently fail - will retry on next check
94
- return null;
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)
@@ -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
- `⬇️ Downloading update v${state.latestVersion}...`
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
- `✅ Update v${state.downloadedVersion} ready!`
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
- 'Update failed'
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
- `📦 Update available: v${state.latestVersion}`
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 };