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,340 @@
1
+ /**
2
+ * GenerationState - Story 4.4
3
+ * Generates agent profile in BMAD/Copilot format
4
+ *
5
+ * Format:
6
+ * - YAML frontmatter (name, description)
7
+ * - XML structure (<agent>, <persona>, <menu>, <capabilities>)
8
+ * - Compliant with .github/copilot/agents/ standard
9
+ */
10
+
11
+ const Logger = require('../observability/logger');
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+
15
+ class GenerationState {
16
+ constructor(sessionState) {
17
+ if (!sessionState) {
18
+ throw new Error('SessionState is required');
19
+ }
20
+
21
+ this.sessionState = sessionState;
22
+ this.logger = new Logger();
23
+ this.profileGenerated = false;
24
+ this.generatedProfile = null;
25
+ }
26
+
27
+ /**
28
+ * AC2: Generate agent profile from analysis results
29
+ * Creates: name, description, persona, menu, capabilities
30
+ *
31
+ * @returns {Promise<string>} Agent profile content
32
+ */
33
+ async generateProfile() {
34
+ this.logger.info('Starting agent profile generation');
35
+
36
+ // AC6: Try to retrieve analysis results, fallback to user responses
37
+ let requirements;
38
+
39
+ if (this.sessionState.analysisResults && this.sessionState.analysisResults.requirements) {
40
+ requirements = this.sessionState.analysisResults.requirements;
41
+ } else {
42
+ // Fallback: Generate minimal requirements from user responses
43
+ this.logger.warn('No analysis results, generating from user responses');
44
+ requirements = this._extractRequirementsFromResponses();
45
+ }
46
+
47
+ // AC2: Extract components from requirements
48
+ const agentName = this._deriveAgentName(requirements.purpose || 'custom-agent');
49
+ const description = this._deriveDescription(requirements.purpose || 'Custom agent');
50
+ const persona = this._generatePersona(requirements);
51
+ const menu = this._generateMenu(requirements.capabilities || []);
52
+ const capabilities = this._generateCapabilities(requirements);
53
+
54
+ // AC1 & AC3: Build profile with YAML frontmatter + XML
55
+ this.generatedProfile = this._buildProfile({
56
+ name: agentName,
57
+ description,
58
+ persona,
59
+ menu,
60
+ capabilities,
61
+ requirements
62
+ });
63
+
64
+ // AC6: Store in SessionState
65
+ this.sessionState.agentProfileDraft = {
66
+ content: this.generatedProfile,
67
+ name: agentName,
68
+ timestamp: Date.now()
69
+ };
70
+
71
+ this.profileGenerated = true;
72
+
73
+ this.logger.info('Agent profile generated', {
74
+ name: agentName,
75
+ length: this.generatedProfile.length
76
+ });
77
+
78
+ return this.generatedProfile;
79
+ }
80
+
81
+ /**
82
+ * AC4: Validate profile format and compliance
83
+ * Checks: YAML frontmatter, XML well-formed, required fields, no emojis in code
84
+ *
85
+ * @param {string} profile - Profile content to validate
86
+ * @returns {boolean} True if valid
87
+ */
88
+ validateProfile(profile) {
89
+ if (!profile || typeof profile !== 'string') {
90
+ this.logger.warn('Validation failed: invalid profile');
91
+ return false;
92
+ }
93
+
94
+ try {
95
+ // AC4: Validate YAML frontmatter
96
+ const frontmatterMatch = profile.match(/^---([\s\S]*?)---/);
97
+ if (!frontmatterMatch) {
98
+ this.logger.warn('Validation failed: missing YAML frontmatter');
99
+ return false;
100
+ }
101
+
102
+ const frontmatter = frontmatterMatch[1];
103
+
104
+ // Check required YAML fields
105
+ if (!frontmatter.includes('name:')) {
106
+ this.logger.warn('Validation failed: missing name in frontmatter');
107
+ return false;
108
+ }
109
+ if (!frontmatter.includes('description:')) {
110
+ this.logger.warn('Validation failed: missing description in frontmatter');
111
+ return false;
112
+ }
113
+
114
+ // AC4: Validate XML block
115
+ const xmlMatch = profile.match(/```xml\s*([\s\S]*?)\s*```/);
116
+ if (!xmlMatch) {
117
+ this.logger.warn('Validation failed: missing XML block');
118
+ return false;
119
+ }
120
+
121
+ const xml = xmlMatch[1];
122
+
123
+ // AC4: Check XML well-formedness (basic)
124
+ if (!xml.includes('<agent') || !xml.includes('</agent>')) {
125
+ this.logger.warn('Validation failed: malformed XML');
126
+ return false;
127
+ }
128
+
129
+ // AC4: Validate no emojis in XML code sections
130
+ const emojiRegex = /[\u{1F300}-\u{1F9FF}]/u;
131
+ if (emojiRegex.test(xml)) {
132
+ this.logger.warn('Validation failed: emojis found in XML');
133
+ return false;
134
+ }
135
+
136
+ this.logger.info('Profile validation passed');
137
+ return true;
138
+
139
+ } catch (error) {
140
+ this.logger.error('Validation error', { error: error.message });
141
+ return false;
142
+ }
143
+ }
144
+
145
+ /**
146
+ * AC5: Save profile to disk
147
+ * @param {string} filePath - Path to save profile
148
+ */
149
+ saveProfile(filePath) {
150
+ if (!this.generatedProfile) {
151
+ throw new Error('No profile to save - generate profile first');
152
+ }
153
+
154
+ try {
155
+ // Ensure directory exists
156
+ const dir = path.dirname(filePath);
157
+ if (!fs.existsSync(dir)) {
158
+ fs.mkdirSync(dir, { recursive: true });
159
+ }
160
+
161
+ // Write profile
162
+ fs.writeFileSync(filePath, this.generatedProfile, 'utf-8');
163
+
164
+ this.logger.info('Profile saved', {
165
+ path: filePath,
166
+ size: this.generatedProfile.length
167
+ });
168
+
169
+ } catch (error) {
170
+ this.logger.error('Save failed', { error: error.message, path: filePath });
171
+ throw error;
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Get default save path
177
+ * @returns {string} Default path
178
+ */
179
+ getDefaultSavePath() {
180
+ const name = this.sessionState.agentProfileDraft?.name || 'agent';
181
+ return `.github/copilot/agents/${name}.md`;
182
+ }
183
+
184
+ /**
185
+ * Extract requirements from user responses (fallback)
186
+ * @private
187
+ */
188
+ _extractRequirementsFromResponses() {
189
+ const responseData = this.sessionState.userResponses || [];
190
+
191
+ // Extract response text from response objects
192
+ const responses = responseData.map(r => typeof r === 'string' ? r : (r.response || ''));
193
+
194
+ return {
195
+ purpose: responses[0] || 'Custom agent',
196
+ domain: responses[3] || 'General',
197
+ capabilities: responses[7] ? responses[7].split(',').map(c => c.trim()) : ['General capability'],
198
+ knowledgeAreas: responses[2] ? responses[2].split(',').map(k => k.trim()) : ['General'],
199
+ users: responses[4] ? responses[4].split(',').map(u => u.trim()) : ['Users'],
200
+ constraints: responses.length > 10 ? [responses[10]] : []
201
+ };
202
+ }
203
+
204
+ /**
205
+ * Derive agent name from purpose
206
+ * @private
207
+ */
208
+ _deriveAgentName(purpose) {
209
+ // Extract key words, sanitize, hyphenate
210
+ const name = purpose
211
+ .toLowerCase()
212
+ .replace(/[^a-z0-9\s-]/g, '')
213
+ .trim()
214
+ .split(/\s+/)
215
+ .slice(0, 3) // Max 3 words
216
+ .join('-');
217
+
218
+ return name || 'custom-agent';
219
+ }
220
+
221
+ /**
222
+ * Derive description from purpose
223
+ * @private
224
+ */
225
+ _deriveDescription(purpose) {
226
+ // Use first sentence or truncate
227
+ const firstSentence = purpose.split(/[.!?]/)[0].trim();
228
+ return firstSentence.length > 100
229
+ ? firstSentence.substring(0, 97) + '...'
230
+ : firstSentence;
231
+ }
232
+
233
+ /**
234
+ * Generate persona from requirements
235
+ * @private
236
+ */
237
+ _generatePersona(requirements) {
238
+ const { purpose, capabilities, knowledgeAreas } = requirements;
239
+
240
+ return `<persona>
241
+ <role>Specialized Agent</role>
242
+ <identity>${purpose}</identity>
243
+ <expertise>Expert in ${knowledgeAreas.slice(0, 3).join(', ')}</expertise>
244
+ <communication_style>Professional, clear, and focused on delivering results</communication_style>
245
+ <capabilities>
246
+ ${capabilities.slice(0, 5).map(cap => ` <capability>${this._escapeXml(cap)}</capability>`).join('\n')}
247
+ </capabilities>
248
+ </persona>`;
249
+ }
250
+
251
+ /**
252
+ * Generate menu from capabilities
253
+ * @private
254
+ */
255
+ _generateMenu(capabilities) {
256
+ const menuItems = capabilities.slice(0, 5).map((cap, index) => {
257
+ const cmdKey = `C${index + 1}`;
258
+ return ` <item cmd="${cmdKey}">[${cmdKey}] ${this._escapeXml(cap)}</item>`;
259
+ });
260
+
261
+ return `<menu>
262
+ ${menuItems.join('\n')}
263
+ <item cmd="MH">[MH] Menu Help</item>
264
+ </menu>`;
265
+ }
266
+
267
+ /**
268
+ * Generate capabilities section
269
+ * @private
270
+ */
271
+ _generateCapabilities(requirements) {
272
+ const { capabilities, knowledgeAreas, constraints } = requirements;
273
+
274
+ return `<capabilities>
275
+ <primary>
276
+ ${capabilities.map(cap => ` <capability>${this._escapeXml(cap)}</capability>`).join('\n')}
277
+ </primary>
278
+ <knowledge>
279
+ ${knowledgeAreas.map(area => ` <domain>${this._escapeXml(area)}</domain>`).join('\n')}
280
+ </knowledge>
281
+ <constraints>
282
+ ${(constraints || []).map(c => ` <constraint>${this._escapeXml(c)}</constraint>`).join('\n')}
283
+ </constraints>
284
+ </capabilities>`;
285
+ }
286
+
287
+ /**
288
+ * Build complete profile
289
+ * @private
290
+ */
291
+ _buildProfile({ name, description, persona, menu, capabilities, requirements }) {
292
+ const agentId = `${name}.agent.yaml`;
293
+
294
+ return `---
295
+ name: "${name}"
296
+ description: "${description}"
297
+ ---
298
+
299
+ \`\`\`xml
300
+ <agent id="${agentId}" name="${name}" title="${description}">
301
+ <activation>
302
+ <step n="1">Load agent context and requirements</step>
303
+ <step n="2">Initialize with user's project context</step>
304
+ <step n="3">Display greeting and menu</step>
305
+ <step n="4">Await user input</step>
306
+ </activation>
307
+
308
+ ${persona}
309
+
310
+ ${menu}
311
+
312
+ ${capabilities}
313
+
314
+ <guidelines>
315
+ <guideline>Follow user requirements precisely</guideline>
316
+ <guideline>Maintain professional communication</guideline>
317
+ <guideline>Provide actionable, clear responses</guideline>
318
+ <guideline>Leverage knowledge domains effectively</guideline>
319
+ </guidelines>
320
+ </agent>
321
+ \`\`\`
322
+ `;
323
+ }
324
+
325
+ /**
326
+ * Escape XML special characters
327
+ * @private
328
+ */
329
+ _escapeXml(str) {
330
+ if (!str) return '';
331
+ return String(str)
332
+ .replace(/&/g, '&amp;')
333
+ .replace(/</g, '&lt;')
334
+ .replace(/>/g, '&gt;')
335
+ .replace(/"/g, '&quot;')
336
+ .replace(/'/g, '&apos;');
337
+ }
338
+ }
339
+
340
+ module.exports = GenerationState;
@@ -0,0 +1,271 @@
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
+ // AC4: Track responses per phase
37
+ this.phaseResponses = {
38
+ CONTEXT: [],
39
+ BUSINESS: [],
40
+ AGENT_NEEDS: [],
41
+ VALIDATION: []
42
+ };
43
+
44
+ // AC1: Question banks for each phase (min 3 per phase)
45
+ this.questionBanks = {
46
+ CONTEXT: [
47
+ 'What is the main purpose or domain of your project?',
48
+ 'Who are the primary users or stakeholders?',
49
+ 'What is the current workflow or process this agent will support?',
50
+ 'What technologies or platforms are you using?',
51
+ 'What is the scale or scope of your project?'
52
+ ],
53
+ BUSINESS: [
54
+ 'What specific problem or challenge does this agent need to solve?',
55
+ 'What are the key goals or objectives for this agent?',
56
+ 'How will you measure the success of this agent?',
57
+ 'What are the most time-consuming or error-prone tasks currently?',
58
+ 'What business value or ROI do you expect?'
59
+ ],
60
+ AGENT_NEEDS: [
61
+ 'What specific capabilities should this agent have?',
62
+ 'What knowledge or expertise should the agent possess?',
63
+ 'How should the agent interact with users (tone, style, format)?',
64
+ 'What are the critical constraints or limitations for the agent?',
65
+ 'What level of autonomy should the agent have in decision-making?'
66
+ ],
67
+ VALIDATION: [
68
+ 'Let me confirm: The agent will help with [SUMMARY]. Is this correct?',
69
+ 'Are there any critical requirements or edge cases we haven\'t covered?',
70
+ 'What would make this agent a complete failure in your eyes?',
71
+ 'Is there anything else important I should know about this agent?'
72
+ ]
73
+ };
74
+
75
+ this.phaseOrder = ['CONTEXT', 'BUSINESS', 'AGENT_NEEDS', 'VALIDATION'];
76
+ this.minResponsesPerPhase = 3;
77
+ }
78
+
79
+ /**
80
+ * AC2: Ask next question based on phase and responses
81
+ * @returns {Object|null} Question object or null if complete
82
+ */
83
+ askNextQuestion() {
84
+ // Check if waiting for response to last question
85
+ if (this.awaitingResponse && this.lastAskedQuestion) {
86
+ this.logger.warn('Awaiting response to previous question');
87
+ return this.lastAskedQuestion;
88
+ }
89
+
90
+ // Check if interview complete
91
+ if (this.isInterviewComplete()) {
92
+ this.logger.info('Interview complete - no more questions');
93
+ return null;
94
+ }
95
+
96
+ // Check if current phase needs transition
97
+ if (this.isPhaseComplete(this.currentPhase)) {
98
+ this._transitionToNextPhase();
99
+ }
100
+
101
+ // Get question from current phase
102
+ const phaseQuestions = this.questionBanks[this.currentPhase];
103
+ const phaseResponseCount = this.phaseResponses[this.currentPhase].length;
104
+
105
+ // Use response count as index (allows revisiting questions)
106
+ const questionIndex = Math.min(phaseResponseCount, phaseQuestions.length - 1);
107
+ const questionText = phaseQuestions[questionIndex];
108
+
109
+ // Build question object
110
+ const question = {
111
+ questionId: crypto.randomUUID(),
112
+ text: questionText,
113
+ phase: this.currentPhase,
114
+ questionNumber: this.currentQuestionIndex + 1,
115
+ totalInPhase: phaseQuestions.length,
116
+ phaseProgress: `${phaseResponseCount + 1}/${this.minResponsesPerPhase} (min)`
117
+ };
118
+
119
+ // Store in SessionState
120
+ this.sessionState.addQuestion(questionText);
121
+
122
+ // Mark as awaiting response
123
+ this.awaitingResponse = true;
124
+ this.lastAskedQuestion = question;
125
+
126
+ this.logger.info('Question asked', {
127
+ phase: this.currentPhase,
128
+ questionNumber: question.questionNumber,
129
+ phaseProgress: question.phaseProgress
130
+ });
131
+
132
+ return question;
133
+ }
134
+
135
+ /**
136
+ * AC3: Process user response and store
137
+ * @param {string} response - User's response
138
+ */
139
+ processResponse(response) {
140
+ if (!this.awaitingResponse) {
141
+ this.logger.warn('No question pending - ignoring response');
142
+ return false;
143
+ }
144
+
145
+ // Store response in current phase
146
+ const responseData = {
147
+ questionId: this.lastAskedQuestion.questionId,
148
+ response: response || '',
149
+ timestamp: Date.now(),
150
+ phase: this.currentPhase
151
+ };
152
+
153
+ this.phaseResponses[this.currentPhase].push(responseData);
154
+
155
+ // Store in SessionState
156
+ this.sessionState.addResponse(this.lastAskedQuestion.questionId, response);
157
+
158
+ // Update state
159
+ this.currentQuestionIndex++;
160
+ this.awaitingResponse = false;
161
+ this.lastAskedQuestion = null;
162
+
163
+ this.logger.info('Response processed', {
164
+ phase: this.currentPhase,
165
+ responseCount: this.phaseResponses[this.currentPhase].length,
166
+ phaseComplete: this.isPhaseComplete(this.currentPhase)
167
+ });
168
+
169
+ // Transition to next phase if current phase is complete
170
+ if (this.isPhaseComplete(this.currentPhase)) {
171
+ const currentPhaseIndex = this.phaseOrder.indexOf(this.currentPhase);
172
+ if (currentPhaseIndex < this.phaseOrder.length - 1) {
173
+ this._transitionToNextPhase();
174
+ }
175
+ }
176
+
177
+ return this.isInterviewComplete();
178
+ }
179
+
180
+ /**
181
+ * AC4: Check if phase has minimum responses
182
+ * @param {string} phase - Phase name
183
+ * @returns {boolean} True if phase complete
184
+ */
185
+ isPhaseComplete(phase) {
186
+ return this.phaseResponses[phase].length >= this.minResponsesPerPhase;
187
+ }
188
+
189
+ /**
190
+ * AC5: Check if all phases complete and can transition to ANALYSIS
191
+ * @returns {boolean} True if interview complete
192
+ */
193
+ canTransitionToAnalysis() {
194
+ return this.phaseOrder.every(phase => this.isPhaseComplete(phase));
195
+ }
196
+
197
+ /**
198
+ * Check if interview is complete
199
+ * @returns {boolean} True if all phases done
200
+ */
201
+ isInterviewComplete() {
202
+ return this.canTransitionToAnalysis();
203
+ }
204
+
205
+ /**
206
+ * Get all collected responses
207
+ * @returns {Object} All phase responses
208
+ */
209
+ getAllResponses() {
210
+ return { ...this.phaseResponses };
211
+ }
212
+
213
+ /**
214
+ * Get responses for specific phase
215
+ * @param {string} phase - Phase name
216
+ * @returns {Array} Phase responses
217
+ */
218
+ getPhaseResponses(phase) {
219
+ return [...this.phaseResponses[phase]];
220
+ }
221
+
222
+ /**
223
+ * Get interview progress summary
224
+ * @returns {Object} Progress data
225
+ */
226
+ getProgress() {
227
+ const phaseProgress = {};
228
+
229
+ this.phaseOrder.forEach(phase => {
230
+ const count = this.phaseResponses[phase].length;
231
+ phaseProgress[phase] = {
232
+ responses: count,
233
+ complete: this.isPhaseComplete(phase),
234
+ progress: `${count}/${this.minResponsesPerPhase}`
235
+ };
236
+ });
237
+
238
+ return {
239
+ currentPhase: this.currentPhase,
240
+ totalQuestions: this.currentQuestionIndex,
241
+ phases: phaseProgress,
242
+ canTransitionToAnalysis: this.canTransitionToAnalysis()
243
+ };
244
+ }
245
+
246
+ /**
247
+ * Transition to next phase
248
+ * @private
249
+ */
250
+ _transitionToNextPhase() {
251
+ const currentIndex = this.phaseOrder.indexOf(this.currentPhase);
252
+
253
+ if (currentIndex === -1 || currentIndex >= this.phaseOrder.length - 1) {
254
+ // Already at last phase or invalid
255
+ return;
256
+ }
257
+
258
+ const nextPhase = this.phaseOrder[currentIndex + 1];
259
+ const previousPhase = this.currentPhase;
260
+
261
+ this.currentPhase = nextPhase;
262
+
263
+ this.logger.info('Interview phase transition', {
264
+ from: previousPhase,
265
+ to: nextPhase,
266
+ previousPhaseResponses: this.phaseResponses[previousPhase].length
267
+ });
268
+ }
269
+ }
270
+
271
+ module.exports = InterviewState;