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.
- package/API-BYAN-V2.md +741 -0
- package/BMAD-QUICK-REFERENCE.md +370 -0
- package/CHANGELOG-v2.1.0.md +371 -0
- package/LICENSE +1 -1
- package/MIGRATION-v2.0-to-v2.1.md +430 -0
- package/README-BYAN-V2.md +446 -0
- package/README.md +264 -201
- package/install/.eslintrc.js +20 -0
- package/install/.prettierrc +7 -0
- package/install/BUGFIX-CHALK.md +173 -0
- package/install/BUGFIX-DOCUMENTATION-INDEX.md +299 -0
- package/install/BUGFIX-PATH-RESOLUTION.md +293 -0
- package/install/BUGFIX-QUICKSTART.md +184 -0
- package/install/BUGFIX-SUMMARY.txt +91 -0
- package/install/BUGFIX-VISUAL-SUMMARY.md +253 -0
- package/install/DEPLOYMENT-GUIDE-V2.md +431 -0
- package/install/DOCS-INDEX.md +261 -0
- package/install/GUIDE-INSTALLATION-BYAN-SIMPLE.md +1083 -0
- package/install/INSTALLER-V2-CHANGES.md +472 -0
- package/install/LICENSE +21 -0
- package/install/PUBLICATION-CHECKLIST.md +265 -0
- package/install/PUBLISH-GUIDE.md +190 -0
- package/install/QUICKSTART.md +311 -0
- package/install/README-NPM-PUBLISH.md +298 -0
- package/install/README-NPM-SHORT.md +298 -0
- package/install/README-NPM.md +433 -0
- package/install/README-RACHID.md +302 -0
- package/install/README-V2-INDEX.md +306 -0
- package/install/README.md +298 -0
- package/install/RESUME-EXECUTIF-YAN.md +408 -0
- package/install/UPDATE-SUMMARY.md +205 -0
- package/install/__tests__/integration/detection-flow.test.js +154 -0
- package/install/__tests__/platforms/claude-code.test.js +175 -0
- package/install/__tests__/platforms/codex.test.js +80 -0
- package/install/__tests__/platforms/copilot-cli.test.js +118 -0
- package/install/__tests__/platforms/vscode.test.js +67 -0
- package/install/__tests__/utils/file-utils.test.js +87 -0
- package/install/__tests__/utils/git-detector.test.js +80 -0
- package/install/__tests__/utils/logger.test.js +83 -0
- package/install/__tests__/utils/node-detector.test.js +71 -0
- package/install/__tests__/utils/os-detector.test.js +63 -0
- package/install/__tests__/utils/yaml-utils.test.js +85 -0
- package/install/__tests__/yanstaller/detector.test.js +210 -0
- package/install/coverage/clover.xml +219 -0
- package/install/coverage/coverage-final.json +13 -0
- package/install/coverage/lcov-report/base.css +224 -0
- package/install/coverage/lcov-report/block-navigation.js +87 -0
- package/install/coverage/lcov-report/favicon.png +0 -0
- package/install/coverage/lcov-report/index.html +146 -0
- package/install/coverage/lcov-report/lib/errors.js.html +268 -0
- package/install/coverage/lcov-report/lib/exit-codes.js.html +247 -0
- package/install/coverage/lcov-report/lib/index.html +131 -0
- package/install/coverage/lcov-report/lib/platforms/claude-code.js.html +343 -0
- package/install/coverage/lcov-report/lib/platforms/codex.js.html +361 -0
- package/install/coverage/lcov-report/lib/platforms/copilot-cli.js.html +454 -0
- package/install/coverage/lcov-report/lib/platforms/index.html +176 -0
- package/install/coverage/lcov-report/lib/platforms/index.js.html +127 -0
- package/install/coverage/lcov-report/lib/platforms/vscode.js.html +238 -0
- package/install/coverage/lcov-report/lib/utils/config-loader.js.html +322 -0
- package/install/coverage/lcov-report/lib/utils/file-utils.js.html +397 -0
- package/install/coverage/lcov-report/lib/utils/git-detector.js.html +190 -0
- package/install/coverage/lcov-report/lib/utils/index.html +206 -0
- package/install/coverage/lcov-report/lib/utils/logger.js.html +277 -0
- package/install/coverage/lcov-report/lib/utils/node-detector.js.html +259 -0
- package/install/coverage/lcov-report/lib/utils/os-detector.js.html +307 -0
- package/install/coverage/lcov-report/lib/utils/yaml-utils.js.html +346 -0
- package/install/coverage/lcov-report/lib/yanstaller/backuper.js.html +409 -0
- package/install/coverage/lcov-report/lib/yanstaller/detector.js.html +508 -0
- package/install/coverage/lcov-report/lib/yanstaller/index.html +236 -0
- package/install/coverage/lcov-report/lib/yanstaller/index.js.html +364 -0
- package/install/coverage/lcov-report/lib/yanstaller/installer.js.html +505 -0
- package/install/coverage/lcov-report/lib/yanstaller/interviewer.js.html +349 -0
- package/install/coverage/lcov-report/lib/yanstaller/recommender.js.html +379 -0
- package/install/coverage/lcov-report/lib/yanstaller/troubleshooter.js.html +352 -0
- package/install/coverage/lcov-report/lib/yanstaller/validator.js.html +679 -0
- package/install/coverage/lcov-report/lib/yanstaller/wizard.js.html +412 -0
- package/install/coverage/lcov-report/platforms/claude-code.js.html +343 -0
- package/install/coverage/lcov-report/platforms/codex.js.html +361 -0
- package/install/coverage/lcov-report/platforms/copilot-cli.js.html +454 -0
- package/install/coverage/lcov-report/platforms/index.html +176 -0
- package/install/coverage/lcov-report/platforms/index.js.html +127 -0
- package/install/coverage/lcov-report/platforms/vscode.js.html +238 -0
- package/install/coverage/lcov-report/prettify.css +1 -0
- package/install/coverage/lcov-report/prettify.js +2 -0
- package/install/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/install/coverage/lcov-report/sorter.js +210 -0
- package/install/coverage/lcov-report/utils/file-utils.js.html +397 -0
- package/install/coverage/lcov-report/utils/git-detector.js.html +190 -0
- package/install/coverage/lcov-report/utils/index.html +191 -0
- package/install/coverage/lcov-report/utils/logger.js.html +277 -0
- package/install/coverage/lcov-report/utils/node-detector.js.html +259 -0
- package/install/coverage/lcov-report/utils/os-detector.js.html +307 -0
- package/install/coverage/lcov-report/utils/yaml-utils.js.html +346 -0
- package/install/coverage/lcov-report/yanstaller/detector.js.html +508 -0
- package/install/coverage/lcov-report/yanstaller/index.html +116 -0
- package/install/coverage/lcov.info +414 -0
- package/install/install.sh +239 -0
- package/install/jest.config.js +33 -0
- package/install/lib/errors.js +61 -0
- package/install/lib/exit-codes.js +54 -0
- package/install/lib/platforms/claude-code.js +86 -0
- package/install/lib/platforms/codex.js +92 -0
- package/install/lib/platforms/copilot-cli.js +123 -0
- package/install/lib/platforms/index.js +14 -0
- package/install/lib/platforms/vscode.js +51 -0
- package/install/lib/utils/config-loader.js +79 -0
- package/install/lib/utils/file-utils.js +104 -0
- package/install/lib/utils/git-detector.js +35 -0
- package/install/lib/utils/logger.js +64 -0
- package/install/lib/utils/node-detector.js +58 -0
- package/install/lib/utils/os-detector.js +74 -0
- package/install/lib/utils/yaml-utils.js +87 -0
- package/install/lib/yanstaller/backuper.js +108 -0
- package/install/lib/yanstaller/detector.js +141 -0
- package/install/lib/yanstaller/index.js +93 -0
- package/install/lib/yanstaller/installer.js +140 -0
- package/install/lib/yanstaller/interviewer.js +88 -0
- package/install/lib/yanstaller/recommender.js +98 -0
- package/install/lib/yanstaller/troubleshooter.js +89 -0
- package/install/lib/yanstaller/validator.js +198 -0
- package/install/lib/yanstaller/wizard.js +109 -0
- package/install/package-npm.json +55 -0
- package/install/package.json +63 -0
- package/install/src/byan-v2/context/copilot-context.js +79 -0
- package/install/src/byan-v2/context/session-state.js +98 -0
- package/install/src/byan-v2/dispatcher/complexity-scorer.js +232 -0
- package/install/src/byan-v2/dispatcher/local-executor.js +221 -0
- package/install/src/byan-v2/dispatcher/task-router.js +122 -0
- package/install/src/byan-v2/dispatcher/task-tool-interface-mock.js +134 -0
- package/install/src/byan-v2/dispatcher/task-tool-interface.js +123 -0
- package/install/src/byan-v2/generation/agent-profile-validator.js +113 -0
- package/install/src/byan-v2/generation/profile-template.js +113 -0
- package/install/src/byan-v2/generation/templates/default-agent.md +49 -0
- package/install/src/byan-v2/generation/templates/test-template.md +1 -0
- package/install/src/byan-v2/index.js +199 -0
- package/install/src/byan-v2/observability/error-tracker.js +105 -0
- package/install/src/byan-v2/observability/logger.js +154 -0
- package/install/src/byan-v2/observability/metrics-collector.js +194 -0
- package/install/src/byan-v2/orchestrator/analysis-state.js +268 -0
- package/install/src/byan-v2/orchestrator/generation-state.js +340 -0
- package/install/src/byan-v2/orchestrator/interview-state.js +271 -0
- package/install/src/byan-v2/orchestrator/state-machine.js +204 -0
- package/install/src/core/cache/cache.js +126 -0
- package/install/src/core/context/context.js +86 -0
- package/install/src/core/dispatcher/dispatcher.js +135 -0
- package/install/src/core/worker-pool/worker-pool.js +194 -0
- package/install/src/core/workflow/workflow-executor.js +220 -0
- package/install/src/index.js +139 -0
- package/install/src/observability/dashboard/dashboard.js +191 -0
- package/install/src/observability/logger/structured-logger.js +254 -0
- package/install/src/observability/metrics/metrics-collector.js +325 -0
- package/install/switch-to-v2.sh +126 -0
- package/install/test-chalk-fix.sh +210 -0
- package/install/test-installer-v2.sh +204 -0
- package/install/test-path-resolution.sh +200 -0
- package/package.json +53 -33
- package/src/byan-v2/context/copilot-context.js +79 -0
- package/src/byan-v2/context/session-state.js +98 -0
- package/src/byan-v2/data/mantras.json +852 -0
- package/src/byan-v2/dispatcher/complexity-scorer.js +232 -0
- package/src/byan-v2/dispatcher/five-whys-analyzer.js +310 -0
- package/src/byan-v2/dispatcher/local-executor.js +221 -0
- package/src/byan-v2/dispatcher/task-router.js +122 -0
- package/src/byan-v2/dispatcher/task-tool-interface-mock.js +134 -0
- package/src/byan-v2/dispatcher/task-tool-interface.js +123 -0
- package/src/byan-v2/generation/agent-profile-validator.js +113 -0
- package/src/byan-v2/generation/mantra-validator.js +416 -0
- package/src/byan-v2/generation/profile-template.js +113 -0
- package/src/byan-v2/generation/templates/default-agent.md +49 -0
- package/src/byan-v2/generation/templates/test-template.md +1 -0
- package/src/byan-v2/index.js +652 -0
- package/src/byan-v2/integration/voice-integration.js +295 -0
- package/src/byan-v2/observability/error-tracker.js +105 -0
- package/src/byan-v2/observability/logger.js +154 -0
- package/src/byan-v2/observability/metrics-collector.js +194 -0
- package/src/byan-v2/orchestrator/active-listener.js +541 -0
- package/src/byan-v2/orchestrator/analysis-state.js +268 -0
- package/src/byan-v2/orchestrator/generation-state.js +340 -0
- package/src/byan-v2/orchestrator/glossary-builder.js +431 -0
- package/src/byan-v2/orchestrator/interview-state.js +353 -0
- package/src/byan-v2/orchestrator/state-machine.js +253 -0
- package/src/core/cache/cache.js +126 -0
- package/src/core/context/context.js +86 -0
- package/src/core/dispatcher/dispatcher.js +135 -0
- package/src/core/worker-pool/worker-pool.js +194 -0
- package/src/core/workflow/workflow-executor.js +220 -0
- package/src/index.js +139 -0
- package/src/observability/dashboard/dashboard.js +191 -0
- package/src/observability/logger/structured-logger.js +254 -0
- package/src/observability/metrics/metrics-collector.js +325 -0
- package/templates/.github/agents/bmad-agent-test-dynamic.md +0 -21
- package/templates/.github/agents/franck.md +0 -379
- /package/{CHANGELOG.md → install/CHANGELOG.md} +0 -0
- /package/{bin → install/bin}/create-byan-agent-backup.js +0 -0
- /package/{bin → install/bin}/create-byan-agent-fixed.js +0 -0
- /package/{bin → install/bin}/create-byan-agent-v2.js +0 -0
- /package/{bin → install/bin}/create-byan-agent.js +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-bmad-master.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-bmb-agent-builder.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-bmb-module-builder.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-bmb-workflow-builder.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-bmm-analyst.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-bmm-architect.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-bmm-dev.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-bmm-pm.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-bmm-quick-flow-solo-dev.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-bmm-quinn.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-bmm-sm.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-bmm-tech-writer.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-bmm-ux-designer.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-byan-test.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-byan.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-carmack.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-cis-brainstorming-coach.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-cis-creative-problem-solver.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-cis-design-thinking-coach.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-cis-innovation-strategist.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-cis-presentation-master.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-cis-storyteller.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-marc.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-patnote.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-rachid.md +0 -0
- /package/{templates → install/templates}/.github/agents/bmad-agent-tea-tea.md +0 -0
- /package/{templates → install/templates}/_bmad/bmb/agents/agent-builder.md +0 -0
- /package/{templates → install/templates}/_bmad/bmb/agents/byan-test.md +0 -0
- /package/{templates → install/templates}/_bmad/bmb/agents/byan.md +0 -0
- /package/{templates → install/templates}/_bmad/bmb/agents/marc.md +0 -0
- /package/{templates → install/templates}/_bmad/bmb/agents/module-builder.md +0 -0
- /package/{templates → install/templates}/_bmad/bmb/agents/patnote.md +0 -0
- /package/{templates → install/templates}/_bmad/bmb/agents/rachid.md +0 -0
- /package/{templates → install/templates}/_bmad/bmb/agents/workflow-builder.md +0 -0
- /package/{templates → install/templates}/_bmad/bmb/workflows/byan/data/mantras.yaml +0 -0
- /package/{templates → install/templates}/_bmad/bmb/workflows/byan/data/templates.yaml +0 -0
- /package/{templates → install/templates}/_bmad/bmb/workflows/byan/delete-agent-workflow.md +0 -0
- /package/{templates → install/templates}/_bmad/bmb/workflows/byan/edit-agent-workflow.md +0 -0
- /package/{templates → install/templates}/_bmad/bmb/workflows/byan/interview-workflow.md +0 -0
- /package/{templates → install/templates}/_bmad/bmb/workflows/byan/quick-create-workflow.md +0 -0
- /package/{templates → install/templates}/_bmad/bmb/workflows/byan/templates/base-agent-template.md +0 -0
- /package/{templates → install/templates}/_bmad/bmb/workflows/byan/validate-agent-workflow.md +0 -0
- /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;
|