create-byan-agent 2.0.1 → 2.1.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.
Files changed (240) hide show
  1. package/API-BYAN-V2.md +741 -0
  2. package/BMAD-QUICK-REFERENCE.md +370 -0
  3. package/CHANGELOG-v2.1.0.md +371 -0
  4. package/LICENSE +1 -1
  5. package/MIGRATION-v2.0-to-v2.1.md +430 -0
  6. package/README-BYAN-V2.md +446 -0
  7. package/README.md +264 -201
  8. package/install/.eslintrc.js +20 -0
  9. package/install/.prettierrc +7 -0
  10. package/install/BUGFIX-CHALK.md +173 -0
  11. package/install/BUGFIX-DOCUMENTATION-INDEX.md +299 -0
  12. package/install/BUGFIX-PATH-RESOLUTION.md +293 -0
  13. package/install/BUGFIX-QUICKSTART.md +184 -0
  14. package/install/BUGFIX-SUMMARY.txt +91 -0
  15. package/install/BUGFIX-VISUAL-SUMMARY.md +253 -0
  16. package/install/DEPLOYMENT-GUIDE-V2.md +431 -0
  17. package/install/DOCS-INDEX.md +261 -0
  18. package/install/GUIDE-INSTALLATION-BYAN-SIMPLE.md +1083 -0
  19. package/install/INSTALLER-V2-CHANGES.md +472 -0
  20. package/install/LICENSE +21 -0
  21. package/install/PUBLICATION-CHECKLIST.md +265 -0
  22. package/install/PUBLISH-GUIDE.md +190 -0
  23. package/install/QUICKSTART.md +311 -0
  24. package/install/README-NPM-PUBLISH.md +298 -0
  25. package/install/README-NPM-SHORT.md +298 -0
  26. package/install/README-NPM.md +433 -0
  27. package/install/README-RACHID.md +302 -0
  28. package/install/README-V2-INDEX.md +306 -0
  29. package/install/README.md +298 -0
  30. package/install/RESUME-EXECUTIF-YAN.md +408 -0
  31. package/install/UPDATE-SUMMARY.md +205 -0
  32. package/install/__tests__/integration/detection-flow.test.js +154 -0
  33. package/install/__tests__/platforms/claude-code.test.js +175 -0
  34. package/install/__tests__/platforms/codex.test.js +80 -0
  35. package/install/__tests__/platforms/copilot-cli.test.js +118 -0
  36. package/install/__tests__/platforms/vscode.test.js +67 -0
  37. package/install/__tests__/utils/file-utils.test.js +87 -0
  38. package/install/__tests__/utils/git-detector.test.js +80 -0
  39. package/install/__tests__/utils/logger.test.js +83 -0
  40. package/install/__tests__/utils/node-detector.test.js +71 -0
  41. package/install/__tests__/utils/os-detector.test.js +63 -0
  42. package/install/__tests__/utils/yaml-utils.test.js +85 -0
  43. package/install/__tests__/yanstaller/detector.test.js +210 -0
  44. package/install/coverage/clover.xml +219 -0
  45. package/install/coverage/coverage-final.json +13 -0
  46. package/install/coverage/lcov-report/base.css +224 -0
  47. package/install/coverage/lcov-report/block-navigation.js +87 -0
  48. package/install/coverage/lcov-report/favicon.png +0 -0
  49. package/install/coverage/lcov-report/index.html +146 -0
  50. package/install/coverage/lcov-report/lib/errors.js.html +268 -0
  51. package/install/coverage/lcov-report/lib/exit-codes.js.html +247 -0
  52. package/install/coverage/lcov-report/lib/index.html +131 -0
  53. package/install/coverage/lcov-report/lib/platforms/claude-code.js.html +343 -0
  54. package/install/coverage/lcov-report/lib/platforms/codex.js.html +361 -0
  55. package/install/coverage/lcov-report/lib/platforms/copilot-cli.js.html +454 -0
  56. package/install/coverage/lcov-report/lib/platforms/index.html +176 -0
  57. package/install/coverage/lcov-report/lib/platforms/index.js.html +127 -0
  58. package/install/coverage/lcov-report/lib/platforms/vscode.js.html +238 -0
  59. package/install/coverage/lcov-report/lib/utils/config-loader.js.html +322 -0
  60. package/install/coverage/lcov-report/lib/utils/file-utils.js.html +397 -0
  61. package/install/coverage/lcov-report/lib/utils/git-detector.js.html +190 -0
  62. package/install/coverage/lcov-report/lib/utils/index.html +206 -0
  63. package/install/coverage/lcov-report/lib/utils/logger.js.html +277 -0
  64. package/install/coverage/lcov-report/lib/utils/node-detector.js.html +259 -0
  65. package/install/coverage/lcov-report/lib/utils/os-detector.js.html +307 -0
  66. package/install/coverage/lcov-report/lib/utils/yaml-utils.js.html +346 -0
  67. package/install/coverage/lcov-report/lib/yanstaller/backuper.js.html +409 -0
  68. package/install/coverage/lcov-report/lib/yanstaller/detector.js.html +508 -0
  69. package/install/coverage/lcov-report/lib/yanstaller/index.html +236 -0
  70. package/install/coverage/lcov-report/lib/yanstaller/index.js.html +364 -0
  71. package/install/coverage/lcov-report/lib/yanstaller/installer.js.html +505 -0
  72. package/install/coverage/lcov-report/lib/yanstaller/interviewer.js.html +349 -0
  73. package/install/coverage/lcov-report/lib/yanstaller/recommender.js.html +379 -0
  74. package/install/coverage/lcov-report/lib/yanstaller/troubleshooter.js.html +352 -0
  75. package/install/coverage/lcov-report/lib/yanstaller/validator.js.html +679 -0
  76. package/install/coverage/lcov-report/lib/yanstaller/wizard.js.html +412 -0
  77. package/install/coverage/lcov-report/platforms/claude-code.js.html +343 -0
  78. package/install/coverage/lcov-report/platforms/codex.js.html +361 -0
  79. package/install/coverage/lcov-report/platforms/copilot-cli.js.html +454 -0
  80. package/install/coverage/lcov-report/platforms/index.html +176 -0
  81. package/install/coverage/lcov-report/platforms/index.js.html +127 -0
  82. package/install/coverage/lcov-report/platforms/vscode.js.html +238 -0
  83. package/install/coverage/lcov-report/prettify.css +1 -0
  84. package/install/coverage/lcov-report/prettify.js +2 -0
  85. package/install/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  86. package/install/coverage/lcov-report/sorter.js +210 -0
  87. package/install/coverage/lcov-report/utils/file-utils.js.html +397 -0
  88. package/install/coverage/lcov-report/utils/git-detector.js.html +190 -0
  89. package/install/coverage/lcov-report/utils/index.html +191 -0
  90. package/install/coverage/lcov-report/utils/logger.js.html +277 -0
  91. package/install/coverage/lcov-report/utils/node-detector.js.html +259 -0
  92. package/install/coverage/lcov-report/utils/os-detector.js.html +307 -0
  93. package/install/coverage/lcov-report/utils/yaml-utils.js.html +346 -0
  94. package/install/coverage/lcov-report/yanstaller/detector.js.html +508 -0
  95. package/install/coverage/lcov-report/yanstaller/index.html +116 -0
  96. package/install/coverage/lcov.info +414 -0
  97. package/install/install.sh +239 -0
  98. package/install/jest.config.js +33 -0
  99. package/install/lib/errors.js +61 -0
  100. package/install/lib/exit-codes.js +54 -0
  101. package/install/lib/platforms/claude-code.js +86 -0
  102. package/install/lib/platforms/codex.js +92 -0
  103. package/install/lib/platforms/copilot-cli.js +123 -0
  104. package/install/lib/platforms/index.js +14 -0
  105. package/install/lib/platforms/vscode.js +51 -0
  106. package/install/lib/utils/config-loader.js +79 -0
  107. package/install/lib/utils/file-utils.js +104 -0
  108. package/install/lib/utils/git-detector.js +35 -0
  109. package/install/lib/utils/logger.js +64 -0
  110. package/install/lib/utils/node-detector.js +58 -0
  111. package/install/lib/utils/os-detector.js +74 -0
  112. package/install/lib/utils/yaml-utils.js +87 -0
  113. package/install/lib/yanstaller/backuper.js +108 -0
  114. package/install/lib/yanstaller/detector.js +141 -0
  115. package/install/lib/yanstaller/index.js +93 -0
  116. package/install/lib/yanstaller/installer.js +140 -0
  117. package/install/lib/yanstaller/interviewer.js +88 -0
  118. package/install/lib/yanstaller/recommender.js +98 -0
  119. package/install/lib/yanstaller/troubleshooter.js +89 -0
  120. package/install/lib/yanstaller/validator.js +198 -0
  121. package/install/lib/yanstaller/wizard.js +109 -0
  122. package/install/package-npm.json +55 -0
  123. package/install/package.json +63 -0
  124. package/install/src/byan-v2/context/copilot-context.js +79 -0
  125. package/install/src/byan-v2/context/session-state.js +98 -0
  126. package/install/src/byan-v2/dispatcher/complexity-scorer.js +232 -0
  127. package/install/src/byan-v2/dispatcher/local-executor.js +221 -0
  128. package/install/src/byan-v2/dispatcher/task-router.js +122 -0
  129. package/install/src/byan-v2/dispatcher/task-tool-interface-mock.js +134 -0
  130. package/install/src/byan-v2/dispatcher/task-tool-interface.js +123 -0
  131. package/install/src/byan-v2/generation/agent-profile-validator.js +113 -0
  132. package/install/src/byan-v2/generation/profile-template.js +113 -0
  133. package/install/src/byan-v2/generation/templates/default-agent.md +49 -0
  134. package/install/src/byan-v2/generation/templates/test-template.md +1 -0
  135. package/install/src/byan-v2/index.js +199 -0
  136. package/install/src/byan-v2/observability/error-tracker.js +105 -0
  137. package/install/src/byan-v2/observability/logger.js +154 -0
  138. package/install/src/byan-v2/observability/metrics-collector.js +194 -0
  139. package/install/src/byan-v2/orchestrator/analysis-state.js +268 -0
  140. package/install/src/byan-v2/orchestrator/generation-state.js +340 -0
  141. package/install/src/byan-v2/orchestrator/interview-state.js +271 -0
  142. package/install/src/byan-v2/orchestrator/state-machine.js +204 -0
  143. package/install/src/core/cache/cache.js +126 -0
  144. package/install/src/core/context/context.js +86 -0
  145. package/install/src/core/dispatcher/dispatcher.js +135 -0
  146. package/install/src/core/worker-pool/worker-pool.js +194 -0
  147. package/install/src/core/workflow/workflow-executor.js +220 -0
  148. package/install/src/index.js +139 -0
  149. package/install/src/observability/dashboard/dashboard.js +191 -0
  150. package/install/src/observability/logger/structured-logger.js +254 -0
  151. package/install/src/observability/metrics/metrics-collector.js +325 -0
  152. package/install/switch-to-v2.sh +126 -0
  153. package/install/test-chalk-fix.sh +210 -0
  154. package/install/test-installer-v2.sh +204 -0
  155. package/install/test-path-resolution.sh +200 -0
  156. package/package.json +53 -33
  157. package/src/byan-v2/context/copilot-context.js +79 -0
  158. package/src/byan-v2/context/session-state.js +98 -0
  159. package/src/byan-v2/data/mantras.json +852 -0
  160. package/src/byan-v2/dispatcher/complexity-scorer.js +232 -0
  161. package/src/byan-v2/dispatcher/five-whys-analyzer.js +310 -0
  162. package/src/byan-v2/dispatcher/local-executor.js +221 -0
  163. package/src/byan-v2/dispatcher/task-router.js +122 -0
  164. package/src/byan-v2/dispatcher/task-tool-interface-mock.js +134 -0
  165. package/src/byan-v2/dispatcher/task-tool-interface.js +123 -0
  166. package/src/byan-v2/generation/agent-profile-validator.js +113 -0
  167. package/src/byan-v2/generation/mantra-validator.js +416 -0
  168. package/src/byan-v2/generation/profile-template.js +113 -0
  169. package/src/byan-v2/generation/templates/default-agent.md +49 -0
  170. package/src/byan-v2/generation/templates/test-template.md +1 -0
  171. package/src/byan-v2/index.js +652 -0
  172. package/src/byan-v2/integration/voice-integration.js +295 -0
  173. package/src/byan-v2/observability/error-tracker.js +105 -0
  174. package/src/byan-v2/observability/logger.js +154 -0
  175. package/src/byan-v2/observability/metrics-collector.js +194 -0
  176. package/src/byan-v2/orchestrator/active-listener.js +541 -0
  177. package/src/byan-v2/orchestrator/analysis-state.js +268 -0
  178. package/src/byan-v2/orchestrator/generation-state.js +340 -0
  179. package/src/byan-v2/orchestrator/glossary-builder.js +431 -0
  180. package/src/byan-v2/orchestrator/interview-state.js +353 -0
  181. package/src/byan-v2/orchestrator/state-machine.js +253 -0
  182. package/src/core/cache/cache.js +126 -0
  183. package/src/core/context/context.js +86 -0
  184. package/src/core/dispatcher/dispatcher.js +135 -0
  185. package/src/core/worker-pool/worker-pool.js +194 -0
  186. package/src/core/workflow/workflow-executor.js +220 -0
  187. package/src/index.js +139 -0
  188. package/src/observability/dashboard/dashboard.js +191 -0
  189. package/src/observability/logger/structured-logger.js +254 -0
  190. package/src/observability/metrics/metrics-collector.js +325 -0
  191. package/templates/.github/agents/bmad-agent-test-dynamic.md +0 -21
  192. package/templates/.github/agents/franck.md +0 -379
  193. /package/{CHANGELOG.md → install/CHANGELOG.md} +0 -0
  194. /package/{bin → install/bin}/create-byan-agent-backup.js +0 -0
  195. /package/{bin → install/bin}/create-byan-agent-fixed.js +0 -0
  196. /package/{bin → install/bin}/create-byan-agent-v2.js +0 -0
  197. /package/{bin → install/bin}/create-byan-agent.js +0 -0
  198. /package/{templates → install/templates}/.github/agents/bmad-agent-bmad-master.md +0 -0
  199. /package/{templates → install/templates}/.github/agents/bmad-agent-bmb-agent-builder.md +0 -0
  200. /package/{templates → install/templates}/.github/agents/bmad-agent-bmb-module-builder.md +0 -0
  201. /package/{templates → install/templates}/.github/agents/bmad-agent-bmb-workflow-builder.md +0 -0
  202. /package/{templates → install/templates}/.github/agents/bmad-agent-bmm-analyst.md +0 -0
  203. /package/{templates → install/templates}/.github/agents/bmad-agent-bmm-architect.md +0 -0
  204. /package/{templates → install/templates}/.github/agents/bmad-agent-bmm-dev.md +0 -0
  205. /package/{templates → install/templates}/.github/agents/bmad-agent-bmm-pm.md +0 -0
  206. /package/{templates → install/templates}/.github/agents/bmad-agent-bmm-quick-flow-solo-dev.md +0 -0
  207. /package/{templates → install/templates}/.github/agents/bmad-agent-bmm-quinn.md +0 -0
  208. /package/{templates → install/templates}/.github/agents/bmad-agent-bmm-sm.md +0 -0
  209. /package/{templates → install/templates}/.github/agents/bmad-agent-bmm-tech-writer.md +0 -0
  210. /package/{templates → install/templates}/.github/agents/bmad-agent-bmm-ux-designer.md +0 -0
  211. /package/{templates → install/templates}/.github/agents/bmad-agent-byan-test.md +0 -0
  212. /package/{templates → install/templates}/.github/agents/bmad-agent-byan.md +0 -0
  213. /package/{templates → install/templates}/.github/agents/bmad-agent-carmack.md +0 -0
  214. /package/{templates → install/templates}/.github/agents/bmad-agent-cis-brainstorming-coach.md +0 -0
  215. /package/{templates → install/templates}/.github/agents/bmad-agent-cis-creative-problem-solver.md +0 -0
  216. /package/{templates → install/templates}/.github/agents/bmad-agent-cis-design-thinking-coach.md +0 -0
  217. /package/{templates → install/templates}/.github/agents/bmad-agent-cis-innovation-strategist.md +0 -0
  218. /package/{templates → install/templates}/.github/agents/bmad-agent-cis-presentation-master.md +0 -0
  219. /package/{templates → install/templates}/.github/agents/bmad-agent-cis-storyteller.md +0 -0
  220. /package/{templates → install/templates}/.github/agents/bmad-agent-marc.md +0 -0
  221. /package/{templates → install/templates}/.github/agents/bmad-agent-patnote.md +0 -0
  222. /package/{templates → install/templates}/.github/agents/bmad-agent-rachid.md +0 -0
  223. /package/{templates → install/templates}/.github/agents/bmad-agent-tea-tea.md +0 -0
  224. /package/{templates → install/templates}/_bmad/bmb/agents/agent-builder.md +0 -0
  225. /package/{templates → install/templates}/_bmad/bmb/agents/byan-test.md +0 -0
  226. /package/{templates → install/templates}/_bmad/bmb/agents/byan.md +0 -0
  227. /package/{templates → install/templates}/_bmad/bmb/agents/marc.md +0 -0
  228. /package/{templates → install/templates}/_bmad/bmb/agents/module-builder.md +0 -0
  229. /package/{templates → install/templates}/_bmad/bmb/agents/patnote.md +0 -0
  230. /package/{templates → install/templates}/_bmad/bmb/agents/rachid.md +0 -0
  231. /package/{templates → install/templates}/_bmad/bmb/agents/workflow-builder.md +0 -0
  232. /package/{templates → install/templates}/_bmad/bmb/workflows/byan/data/mantras.yaml +0 -0
  233. /package/{templates → install/templates}/_bmad/bmb/workflows/byan/data/templates.yaml +0 -0
  234. /package/{templates → install/templates}/_bmad/bmb/workflows/byan/delete-agent-workflow.md +0 -0
  235. /package/{templates → install/templates}/_bmad/bmb/workflows/byan/edit-agent-workflow.md +0 -0
  236. /package/{templates → install/templates}/_bmad/bmb/workflows/byan/interview-workflow.md +0 -0
  237. /package/{templates → install/templates}/_bmad/bmb/workflows/byan/quick-create-workflow.md +0 -0
  238. /package/{templates → install/templates}/_bmad/bmb/workflows/byan/templates/base-agent-template.md +0 -0
  239. /package/{templates → install/templates}/_bmad/bmb/workflows/byan/validate-agent-workflow.md +0 -0
  240. /package/{templates → install/templates}/_bmad/core/agents/carmack.md +0 -0
@@ -0,0 +1,353 @@
1
+ /**
2
+ * InterviewState - Story 4.2
3
+ * Manages structured interview flow through 4 phases
4
+ *
5
+ * Phases:
6
+ * 1. CONTEXT: Project context, domain, users
7
+ * 2. BUSINESS: Goals, problems, success criteria
8
+ * 3. AGENT_NEEDS: Agent capabilities, behavior, constraints
9
+ * 4. VALIDATION: Confirm understanding, clarifications
10
+ *
11
+ * Integrates with SessionState to persist interview data
12
+ */
13
+
14
+ const Logger = require('../observability/logger');
15
+ const crypto = require('crypto');
16
+
17
+ class InterviewState {
18
+ constructor(sessionState) {
19
+ this.sessionState = sessionState;
20
+ this.logger = new Logger();
21
+
22
+ // AC1: Define 4 phases
23
+ this.PHASES = {
24
+ CONTEXT: 'CONTEXT',
25
+ BUSINESS: 'BUSINESS',
26
+ AGENT_NEEDS: 'AGENT_NEEDS',
27
+ VALIDATION: 'VALIDATION'
28
+ };
29
+
30
+ // Current interview state
31
+ this.currentPhase = this.PHASES.CONTEXT;
32
+ this.currentQuestionIndex = 0;
33
+ this.awaitingResponse = false;
34
+ this.lastAskedQuestion = null;
35
+
36
+ // v2.1.0: BMAD integration tracking
37
+ this.activeListeningCounter = 0; // Track responses for active listening
38
+ this.painPointsDetected = false; // Flag if pain points found
39
+ this.glossaryTriggered = false; // Flag if glossary should trigger
40
+ this.reformulatedResponses = []; // Store reformulated responses alongside originals
41
+
42
+ // AC4: Track responses per phase
43
+ this.phaseResponses = {
44
+ CONTEXT: [],
45
+ BUSINESS: [],
46
+ AGENT_NEEDS: [],
47
+ VALIDATION: []
48
+ };
49
+
50
+ // AC1: Question banks for each phase (min 3 per phase)
51
+ this.questionBanks = {
52
+ CONTEXT: [
53
+ 'What is the main purpose or domain of your project?',
54
+ 'Who are the primary users or stakeholders?',
55
+ 'What is the current workflow or process this agent will support?',
56
+ 'What technologies or platforms are you using?',
57
+ 'What is the scale or scope of your project?'
58
+ ],
59
+ BUSINESS: [
60
+ 'What specific problem or challenge does this agent need to solve?',
61
+ 'What are the key goals or objectives for this agent?',
62
+ 'How will you measure the success of this agent?',
63
+ 'What are the most time-consuming or error-prone tasks currently?',
64
+ 'What business value or ROI do you expect?'
65
+ ],
66
+ AGENT_NEEDS: [
67
+ 'What specific capabilities should this agent have?',
68
+ 'What knowledge or expertise should the agent possess?',
69
+ 'How should the agent interact with users (tone, style, format)?',
70
+ 'What are the critical constraints or limitations for the agent?',
71
+ 'What level of autonomy should the agent have in decision-making?'
72
+ ],
73
+ VALIDATION: [
74
+ 'Let me confirm: The agent will help with [SUMMARY]. Is this correct?',
75
+ 'Are there any critical requirements or edge cases we haven\'t covered?',
76
+ 'What would make this agent a complete failure in your eyes?',
77
+ 'Is there anything else important I should know about this agent?'
78
+ ]
79
+ };
80
+
81
+ this.phaseOrder = ['CONTEXT', 'BUSINESS', 'AGENT_NEEDS', 'VALIDATION'];
82
+ this.minResponsesPerPhase = 3;
83
+ }
84
+
85
+ /**
86
+ * AC2: Ask next question based on phase and responses
87
+ * @returns {Object|null} Question object or null if complete
88
+ */
89
+ askNextQuestion() {
90
+ // Check if waiting for response to last question
91
+ if (this.awaitingResponse && this.lastAskedQuestion) {
92
+ this.logger.warn('Awaiting response to previous question');
93
+ return this.lastAskedQuestion;
94
+ }
95
+
96
+ // Check if interview complete
97
+ if (this.isInterviewComplete()) {
98
+ this.logger.info('Interview complete - no more questions');
99
+ return null;
100
+ }
101
+
102
+ // Check if current phase needs transition
103
+ if (this.isPhaseComplete(this.currentPhase)) {
104
+ this._transitionToNextPhase();
105
+ }
106
+
107
+ // Get question from current phase
108
+ const phaseQuestions = this.questionBanks[this.currentPhase];
109
+ const phaseResponseCount = this.phaseResponses[this.currentPhase].length;
110
+
111
+ // Use response count as index (allows revisiting questions)
112
+ const questionIndex = Math.min(phaseResponseCount, phaseQuestions.length - 1);
113
+ const questionText = phaseQuestions[questionIndex];
114
+
115
+ // Build question object
116
+ const question = {
117
+ questionId: crypto.randomUUID(),
118
+ text: questionText,
119
+ phase: this.currentPhase,
120
+ questionNumber: this.currentQuestionIndex + 1,
121
+ totalInPhase: phaseQuestions.length,
122
+ phaseProgress: `${phaseResponseCount + 1}/${this.minResponsesPerPhase} (min)`
123
+ };
124
+
125
+ // Store in SessionState
126
+ this.sessionState.addQuestion(questionText);
127
+
128
+ // Mark as awaiting response
129
+ this.awaitingResponse = true;
130
+ this.lastAskedQuestion = question;
131
+
132
+ this.logger.info('Question asked', {
133
+ phase: this.currentPhase,
134
+ questionNumber: question.questionNumber,
135
+ phaseProgress: question.phaseProgress
136
+ });
137
+
138
+ return question;
139
+ }
140
+
141
+ /**
142
+ * AC3: Process user response and store
143
+ * @param {string} response - User's response
144
+ * @param {Object} metadata - Optional metadata (v2.1.0: reformulated, painPoints)
145
+ */
146
+ processResponse(response, metadata = {}) {
147
+ if (!this.awaitingResponse) {
148
+ this.logger.warn('No question pending - ignoring response');
149
+ return false;
150
+ }
151
+
152
+ // Store response in current phase
153
+ const responseData = {
154
+ questionId: this.lastAskedQuestion.questionId,
155
+ response: response || '',
156
+ timestamp: Date.now(),
157
+ phase: this.currentPhase,
158
+ // v2.1.0: BMAD metadata
159
+ reformulated: metadata.reformulated || null,
160
+ painPointsDetected: metadata.painPointsDetected || false,
161
+ clarityScore: metadata.clarityScore || null
162
+ };
163
+
164
+ this.phaseResponses[this.currentPhase].push(responseData);
165
+
166
+ // v2.1.0: Track reformulated responses
167
+ if (metadata.reformulated) {
168
+ this.reformulatedResponses.push({
169
+ original: response,
170
+ reformulated: metadata.reformulated,
171
+ questionId: this.lastAskedQuestion.questionId,
172
+ phase: this.currentPhase
173
+ });
174
+ }
175
+
176
+ // v2.1.0: Track pain points
177
+ if (metadata.painPointsDetected) {
178
+ this.painPointsDetected = true;
179
+ }
180
+
181
+ // v2.1.0: Increment active listening counter
182
+ this.activeListeningCounter++;
183
+
184
+ // Store in SessionState
185
+ this.sessionState.addResponse(this.lastAskedQuestion.questionId, response);
186
+
187
+ // Update state
188
+ this.currentQuestionIndex++;
189
+ this.awaitingResponse = false;
190
+ this.lastAskedQuestion = null;
191
+
192
+ this.logger.info('Response processed', {
193
+ phase: this.currentPhase,
194
+ responseCount: this.phaseResponses[this.currentPhase].length,
195
+ phaseComplete: this.isPhaseComplete(this.currentPhase),
196
+ activeListeningCounter: this.activeListeningCounter
197
+ });
198
+
199
+ // Transition to next phase if current phase is complete
200
+ if (this.isPhaseComplete(this.currentPhase)) {
201
+ const currentPhaseIndex = this.phaseOrder.indexOf(this.currentPhase);
202
+ if (currentPhaseIndex < this.phaseOrder.length - 1) {
203
+ this._transitionToNextPhase();
204
+ }
205
+ }
206
+
207
+ return this.isInterviewComplete();
208
+ }
209
+
210
+ /**
211
+ * AC4: Check if phase has minimum responses
212
+ * @param {string} phase - Phase name
213
+ * @returns {boolean} True if phase complete
214
+ */
215
+ isPhaseComplete(phase) {
216
+ return this.phaseResponses[phase].length >= this.minResponsesPerPhase;
217
+ }
218
+
219
+ /**
220
+ * AC5: Check if all phases complete and can transition to ANALYSIS
221
+ * @returns {boolean} True if interview complete
222
+ */
223
+ canTransitionToAnalysis() {
224
+ return this.phaseOrder.every(phase => this.isPhaseComplete(phase));
225
+ }
226
+
227
+ /**
228
+ * Check if interview is complete
229
+ * @returns {boolean} True if all phases done
230
+ */
231
+ isInterviewComplete() {
232
+ return this.canTransitionToAnalysis();
233
+ }
234
+
235
+ /**
236
+ * Get all collected responses
237
+ * @returns {Object} All phase responses
238
+ */
239
+ getAllResponses() {
240
+ return { ...this.phaseResponses };
241
+ }
242
+
243
+ /**
244
+ * Get responses for specific phase
245
+ * @param {string} phase - Phase name
246
+ * @returns {Array} Phase responses
247
+ */
248
+ getPhaseResponses(phase) {
249
+ return [...this.phaseResponses[phase]];
250
+ }
251
+
252
+ /**
253
+ * Get interview progress summary
254
+ * @returns {Object} Progress data
255
+ */
256
+ getProgress() {
257
+ const phaseProgress = {};
258
+
259
+ this.phaseOrder.forEach(phase => {
260
+ const count = this.phaseResponses[phase].length;
261
+ phaseProgress[phase] = {
262
+ responses: count,
263
+ complete: this.isPhaseComplete(phase),
264
+ progress: `${count}/${this.minResponsesPerPhase}`
265
+ };
266
+ });
267
+
268
+ return {
269
+ currentPhase: this.currentPhase,
270
+ totalQuestions: this.currentQuestionIndex,
271
+ phases: phaseProgress,
272
+ canTransitionToAnalysis: this.canTransitionToAnalysis()
273
+ };
274
+ }
275
+
276
+ /**
277
+ * Transition to next phase
278
+ * @private
279
+ */
280
+ _transitionToNextPhase() {
281
+ const currentIndex = this.phaseOrder.indexOf(this.currentPhase);
282
+
283
+ if (currentIndex === -1 || currentIndex >= this.phaseOrder.length - 1) {
284
+ // Already at last phase or invalid
285
+ return;
286
+ }
287
+
288
+ const nextPhase = this.phaseOrder[currentIndex + 1];
289
+ const previousPhase = this.currentPhase;
290
+
291
+ this.currentPhase = nextPhase;
292
+
293
+ this.logger.info('Interview phase transition', {
294
+ from: previousPhase,
295
+ to: nextPhase,
296
+ previousPhaseResponses: this.phaseResponses[previousPhase].length
297
+ });
298
+ }
299
+
300
+ // v2.1.0: BMAD integration methods
301
+
302
+ /**
303
+ * Get active listening counter
304
+ * @returns {number} Number of responses processed
305
+ */
306
+ getActiveListeningCounter() {
307
+ return this.activeListeningCounter;
308
+ }
309
+
310
+ /**
311
+ * Check if active listening validation needed
312
+ * @param {number} frequency - Validation frequency (default: 3)
313
+ * @returns {boolean} True if validation needed
314
+ */
315
+ shouldValidateUnderstanding(frequency = 3) {
316
+ return this.activeListeningCounter > 0 &&
317
+ this.activeListeningCounter % frequency === 0;
318
+ }
319
+
320
+ /**
321
+ * Check if pain points were detected
322
+ * @returns {boolean} True if pain points found
323
+ */
324
+ hasPainPointsDetected() {
325
+ return this.painPointsDetected;
326
+ }
327
+
328
+ /**
329
+ * Get all reformulated responses
330
+ * @returns {Array} Reformulated response objects
331
+ */
332
+ getReformulatedResponses() {
333
+ return [...this.reformulatedResponses];
334
+ }
335
+
336
+ /**
337
+ * Set glossary trigger flag
338
+ * @param {boolean} value - Flag value
339
+ */
340
+ setGlossaryTriggered(value) {
341
+ this.glossaryTriggered = value;
342
+ }
343
+
344
+ /**
345
+ * Check if glossary should be triggered
346
+ * @returns {boolean} Glossary trigger flag
347
+ */
348
+ shouldTriggerGlossary() {
349
+ return this.glossaryTriggered;
350
+ }
351
+ }
352
+
353
+ module.exports = InterviewState;
@@ -0,0 +1,253 @@
1
+ /**
2
+ * StateMachine Core - Story 4.1
3
+ * Manages workflow state transitions for BYAN v2 orchestrator
4
+ *
5
+ * States: INTERVIEW → ANALYSIS → GENERATION → COMPLETED (+ ERROR from any state)
6
+ */
7
+
8
+ const Logger = require('../observability/logger');
9
+ const ErrorTracker = require('../observability/error-tracker');
10
+
11
+ class StateMachine {
12
+ constructor(options = {}) {
13
+ this.logger = options.logger || new Logger();
14
+ this.errorTracker = options.errorTracker || new ErrorTracker();
15
+
16
+ // AC1: Define states (v2.1.0: added GLOSSARY, VALIDATION)
17
+ this.STATES = {
18
+ INTERVIEW: 'INTERVIEW',
19
+ GLOSSARY: 'GLOSSARY', // Optional: Business glossary creation (v2.1.0)
20
+ ANALYSIS: 'ANALYSIS',
21
+ GENERATION: 'GENERATION',
22
+ VALIDATION: 'VALIDATION', // Optional: Agent validation (v2.1.0)
23
+ COMPLETED: 'COMPLETED',
24
+ ERROR: 'ERROR'
25
+ };
26
+
27
+ // AC2: Valid state transitions (v2.1.0: added optional states)
28
+ // Backwards compatible: INTERVIEW -> ANALYSIS still valid
29
+ this.validTransitions = {
30
+ INTERVIEW: ['GLOSSARY', 'ANALYSIS', 'ERROR'],
31
+ GLOSSARY: ['ANALYSIS', 'ERROR'],
32
+ ANALYSIS: ['GENERATION', 'ERROR'],
33
+ GENERATION: ['VALIDATION', 'COMPLETED', 'ERROR'],
34
+ VALIDATION: ['COMPLETED', 'ERROR'],
35
+ COMPLETED: [], // Terminal state
36
+ ERROR: [] // Terminal state
37
+ };
38
+
39
+ // State metadata (v2.1.0: enhanced with optional state info)
40
+ this.currentState = this.STATES.INTERVIEW;
41
+ this.stateMetadata = {
42
+ name: this.STATES.INTERVIEW,
43
+ timestamp: Date.now(),
44
+ attempts: 0,
45
+ optional: false, // Track if current state is optional
46
+ skippable: false, // Track if state can be skipped
47
+ description: 'Collecting user requirements through structured interview'
48
+ };
49
+
50
+ // v2.1.0: Optional states configuration
51
+ this.optionalStates = ['GLOSSARY', 'VALIDATION'];
52
+ this.stateDescriptions = {
53
+ INTERVIEW: 'Collecting user requirements through structured interview',
54
+ GLOSSARY: 'Building business glossary for domain clarity (optional)',
55
+ ANALYSIS: 'Analyzing requirements and extracting insights',
56
+ GENERATION: 'Generating agent profile from analysis',
57
+ VALIDATION: 'Validating agent against mantras (optional)',
58
+ COMPLETED: 'Session completed successfully',
59
+ ERROR: 'Session terminated with error'
60
+ };
61
+
62
+ // AC5: Hooks for state enter/exit
63
+ this.enterHooks = {}; // { stateName: [callbacks] }
64
+ this.exitHooks = {}; // { stateName: [callbacks] }
65
+ }
66
+
67
+ /**
68
+ * AC3: Validate and execute state transition
69
+ * @param {string} newState - Target state
70
+ * @returns {Object} { success, previousState, newState, error? }
71
+ */
72
+ transition(newState) {
73
+ const previousState = this.currentState;
74
+
75
+ // Validate newState exists
76
+ if (!newState || !this.STATES[newState]) {
77
+ const error = new Error(`Unknown state: ${newState}`);
78
+ this.errorTracker.trackError({
79
+ error,
80
+ component: 'StateMachine',
81
+ operation: 'transition',
82
+ currentState: this.currentState,
83
+ attemptedState: newState
84
+ });
85
+ return {
86
+ success: false,
87
+ error: error.message
88
+ };
89
+ }
90
+
91
+ // Validate transition is allowed
92
+ if (!this.validTransitions[this.currentState].includes(newState)) {
93
+ const error = new Error(
94
+ `Invalid transition from ${this.currentState} to ${newState}`
95
+ );
96
+ this.errorTracker.trackError({
97
+ error,
98
+ component: 'StateMachine',
99
+ operation: 'transition',
100
+ currentState: this.currentState,
101
+ attemptedState: newState
102
+ });
103
+ return {
104
+ success: false,
105
+ error: error.message
106
+ };
107
+ }
108
+
109
+ // Execute transition with hooks
110
+ try {
111
+ // AC5: Call exit hooks
112
+ this._executeHooks(this.exitHooks[this.currentState], {
113
+ from: this.currentState,
114
+ to: newState
115
+ });
116
+
117
+ // Update state (v2.1.0: enhanced metadata)
118
+ this.currentState = newState;
119
+ this.stateMetadata = {
120
+ name: newState,
121
+ timestamp: Date.now(),
122
+ attempts: 0,
123
+ optional: this.optionalStates.includes(newState),
124
+ skippable: this.optionalStates.includes(newState),
125
+ description: this.stateDescriptions[newState] || ''
126
+ };
127
+
128
+ // AC5: Call enter hooks
129
+ this._executeHooks(this.enterHooks[newState], {
130
+ from: previousState,
131
+ to: newState
132
+ });
133
+
134
+ // Log successful transition
135
+ this.logger.info('State transition', {
136
+ from: previousState,
137
+ to: newState,
138
+ timestamp: this.stateMetadata.timestamp
139
+ });
140
+
141
+ return {
142
+ success: true,
143
+ previousState,
144
+ newState
145
+ };
146
+ } catch (error) {
147
+ // If hooks fail, track but complete transition
148
+ this.errorTracker.trackError({
149
+ error,
150
+ component: 'StateMachine',
151
+ operation: 'transition-hooks',
152
+ state: newState
153
+ });
154
+
155
+ return {
156
+ success: true,
157
+ previousState,
158
+ newState,
159
+ hookError: error.message
160
+ };
161
+ }
162
+ }
163
+
164
+ /**
165
+ * AC4: Get current state with metadata
166
+ * @returns {Object} { name, timestamp, attempts, optional, skippable, description }
167
+ */
168
+ getCurrentState() {
169
+ return { ...this.stateMetadata };
170
+ }
171
+
172
+ /**
173
+ * v2.1.0: Check if current state is optional
174
+ * @returns {boolean} True if state is optional
175
+ */
176
+ isCurrentStateOptional() {
177
+ return this.optionalStates.includes(this.currentState);
178
+ }
179
+
180
+ /**
181
+ * v2.1.0: Check if a specific state is optional
182
+ * @param {string} stateName - State to check
183
+ * @returns {boolean} True if state is optional
184
+ */
185
+ isStateOptional(stateName) {
186
+ return this.optionalStates.includes(stateName);
187
+ }
188
+
189
+ /**
190
+ * v2.1.0: Get state description
191
+ * @param {string} stateName - State name
192
+ * @returns {string} State description
193
+ */
194
+ getStateDescription(stateName) {
195
+ return this.stateDescriptions[stateName] || 'Unknown state';
196
+ }
197
+
198
+ /**
199
+ * Increment retry attempts for current state
200
+ */
201
+ incrementAttempts() {
202
+ this.stateMetadata.attempts++;
203
+ }
204
+
205
+ /**
206
+ * AC5: Register state enter hook
207
+ * @param {string} stateName - State to hook into
208
+ * @param {Function} callback - Hook function
209
+ */
210
+ onStateEnter(stateName, callback) {
211
+ if (!this.enterHooks[stateName]) {
212
+ this.enterHooks[stateName] = [];
213
+ }
214
+ this.enterHooks[stateName].push(callback);
215
+ }
216
+
217
+ /**
218
+ * AC5: Register state exit hook
219
+ * @param {string} stateName - State to hook into
220
+ * @param {Function} callback - Hook function
221
+ */
222
+ onStateExit(stateName, callback) {
223
+ if (!this.exitHooks[stateName]) {
224
+ this.exitHooks[stateName] = [];
225
+ }
226
+ this.exitHooks[stateName].push(callback);
227
+ }
228
+
229
+ /**
230
+ * Execute all hooks for a state
231
+ * @private
232
+ */
233
+ _executeHooks(hooks, context) {
234
+ if (!hooks || hooks.length === 0) return;
235
+
236
+ hooks.forEach(hook => {
237
+ try {
238
+ hook(context);
239
+ } catch (error) {
240
+ // Track hook error but don't block transition
241
+ this.errorTracker.trackError({
242
+ error,
243
+ component: 'StateMachine',
244
+ operation: 'hook-execution',
245
+ context
246
+ });
247
+ throw error; // Re-throw to be caught by transition()
248
+ }
249
+ });
250
+ }
251
+ }
252
+
253
+ module.exports = StateMachine;