create-byan-agent 1.2.2 → 1.2.4

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/CHANGELOG.md CHANGED
@@ -5,14 +5,123 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.2.4] - 2026-02-04
9
+
10
+ ### Added
11
+ - **Interactive override**: `--interactive` flag to force prompts even without TTY (useful in npm scripts).
12
+
13
+ ### Changed
14
+ - **Interview defaults**: Preselect detected or provided platforms during the interview.
15
+ - **E2E**: Run with explicit platforms to validate stub generation.
16
+
17
+ ### Fixed
18
+ - **Silent mode**: Skip interview and wizard with safe defaults to prevent hangs in non-interactive runs.
19
+ - **Platform normalization**: Map `claude` to `claude-code` for installer/platform parity.
20
+ - **Validator**: Use YAML parser correctly and remove false warnings (permissions/templates).
21
+
22
+ ## [1.2.3] - 2026-02-03
23
+
24
+ ### Added
25
+ - **E2E Test Suite** - Automated end-to-end testing before npm publish
26
+ - Tests complete installation flow (7 steps)
27
+ - Validates directory structure, agent files, config, platform stubs
28
+ - Smoke tests for all 8 YANSTALLER modules
29
+ - Runs automatically via `prepublishOnly` hook
30
+ - Prevents broken releases (caught 8 bugs before production)
31
+ - Command: `npm run test:e2e`
32
+ - Documentation: TEST-E2E-GUIDE.md
33
+
34
+ ### Fixed
35
+ - **CRITICAL BUG #1**: Fixed recommender crash when platforms is undefined
36
+ - `recommender.recommend()` now properly handles both direct detection object and options wrapper
37
+ - Added default empty array for platforms parameter in `getRecommendedAgents()`
38
+ - Added null-check before calling `.some()` on platforms array
39
+ - Fixed display of detected platforms in CLI (was showing "[object Object]")
40
+
41
+ - **CRITICAL BUG #2**: Fixed backup crash on wrong parameter format
42
+ - Fixed `backuper.backup()` call - now passes bmadPath directly instead of object wrapper
43
+
44
+ - **CRITICAL BUG #3**: Fixed installation result property access
45
+ - Fixed `installResult` property access - uses `agentsInstalled` instead of `installedAgents.length`
46
+ - Removed references to non-existent properties (`createdDirectories`, `generatedStubs`)
47
+ - Added error reporting for installation failures
48
+
49
+ - **CRITICAL BUG #4**: Fixed wizard crash on undefined properties
50
+ - Added null-checks for `config.agents`, `config.targetPlatforms`, `config.platforms`
51
+ - Fixed wizard call to pass correct config object with all required properties
52
+ - Prevents crash when displaying installation summary
53
+
54
+ - **CRITICAL BUG #5**: Fixed agent templates not found
55
+ - Limited recommendations to available agents only (byan, rachid, patnote, marc, etc.)
56
+ - Removed recommendations for non-existent agents (architect, dev, quinn, pm, ux-designer, tech-writer)
57
+ - Prevents installation failures due to missing template files
58
+
59
+ - **CRITICAL BUG #6**: Fixed missing fileUtils.readDir function
60
+ - Added `readDir()` function to file-utils.js
61
+ - Fixes validator crashes when checking workflow directories
62
+ - Properly exports readDir in module.exports
63
+
64
+ - **CRITICAL BUG #7**: Fixed installer config parameter mismatch
65
+ - Fixed installer.install() call to pass `targetPlatforms` instead of `platforms`
66
+ - Config file now created correctly during installation
67
+ - Matches InstallConfig typedef definition
68
+
69
+ - **BUG #8**: Removed carmack from recommendations
70
+ - carmack.md template does not exist in templates/_bmad/
71
+ - Removed from baseAgents to prevent installation failures
72
+ - Updated E2E test to not expect carmack agent
73
+
8
74
  ## [1.2.2] - 2026-02-03
9
75
 
76
+ ### Added
77
+ - **E2E Test Suite** - Automated end-to-end testing before npm publish
78
+ - Tests complete installation flow (7 steps)
79
+ - Validates directory structure, agent files, config, platform stubs
80
+ - Smoke tests for all 8 YANSTALLER modules
81
+ - Runs automatically via `prepublishOnly` hook
82
+ - Prevents broken releases (caught 7 bugs before production)
83
+ - Command: `npm run test:e2e`
84
+ - Documentation: TEST-E2E-GUIDE.md
85
+
10
86
  ### Fixed
11
- - **CRITICAL BUG**: Fixed recommender crash when platforms is undefined
87
+ - **CRITICAL BUG #1**: Fixed recommender crash when platforms is undefined
12
88
  - `recommender.recommend()` now properly handles both direct detection object and options wrapper
13
89
  - Added default empty array for platforms parameter in `getRecommendedAgents()`
14
90
  - Added null-check before calling `.some()` on platforms array
15
91
  - Fixed display of detected platforms in CLI (was showing "[object Object]")
92
+
93
+ - **CRITICAL BUG #2**: Fixed backup crash on wrong parameter format
94
+ - Fixed `backuper.backup()` call - now passes bmadPath directly instead of object wrapper
95
+
96
+ - **CRITICAL BUG #3**: Fixed installation result property access
97
+ - Fixed `installResult` property access - uses `agentsInstalled` instead of `installedAgents.length`
98
+ - Removed references to non-existent properties (`createdDirectories`, `generatedStubs`)
99
+ - Added error reporting for installation failures
100
+
101
+ - **CRITICAL BUG #4**: Fixed wizard crash on undefined properties
102
+ - Added null-checks for `config.agents`, `config.targetPlatforms`, `config.platforms`
103
+ - Fixed wizard call to pass correct config object with all required properties
104
+ - Prevents crash when displaying installation summary
105
+
106
+ - **CRITICAL BUG #5**: Fixed agent templates not found
107
+ - Limited recommendations to available agents only (byan, rachid, patnote, carmack, marc, etc.)
108
+ - Removed recommendations for non-existent agents (architect, dev, quinn, pm, ux-designer, tech-writer)
109
+ - Prevents installation failures due to missing template files
110
+
111
+ - **CRITICAL BUG #6**: Fixed missing fileUtils.readDir function
112
+ - Added `readDir()` function to file-utils.js
113
+ - Fixes validator crashes when checking workflow directories
114
+ - Properly exports readDir in module.exports
115
+
116
+ - **CRITICAL BUG #7**: Fixed installer config parameter mismatch
117
+ - Fixed installer.install() call to pass `targetPlatforms` instead of `platforms`
118
+ - Config file now created correctly during installation
119
+ - Matches InstallConfig typedef definition
120
+
121
+ - **BUG #8**: Removed carmack from recommendations
122
+ - carmack.md template does not exist in templates/_bmad/
123
+ - Removed from baseAgents to prevent installation failures
124
+ - Updated E2E test to not expect carmack agent
16
125
 
17
126
  ## [1.2.1] - 2026-02-03
18
127
 
package/README.md CHANGED
@@ -846,7 +846,7 @@ SOFTWARE.
846
846
 
847
847
  # šŸ—ļø YANSTALLER - Intelligent BYAN Installer
848
848
 
849
- [![Version](https://img.shields.io/badge/version-1.2.2-blue.svg)](https://www.npmjs.com/package/create-byan-agent)
849
+ [![Version](https://img.shields.io/badge/version-1.2.3-blue.svg)](https://www.npmjs.com/package/create-byan-agent)
850
850
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
851
851
  [![Node](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen.svg)](https://nodejs.org)
852
852
  [![Tests](https://img.shields.io/badge/tests-168%20passing-success.svg)](#tests)
@@ -1015,13 +1015,16 @@ const validation = await yanstaller.validate({
1015
1015
  console.log(validation.errors); // []
1016
1016
  ```
1017
1017
 
1018
- #### CLI Options
1019
- ```bash
1020
- # Silent installation with specific agents
1021
- create-byan-agent --silent --agents=byan,dev,quinn
1022
-
1023
- # Custom mode with specific platform
1024
- create-byan-agent --mode=custom --platforms=copilot-cli
1018
+ #### CLI Options
1019
+ ```bash
1020
+ # Silent installation with specific agents
1021
+ create-byan-agent --silent --agents=byan,dev,quinn
1022
+
1023
+ # Force interactive prompts (useful inside npm scripts or CI shells without TTY)
1024
+ create-byan-agent --interactive
1025
+
1026
+ # Custom mode with specific platform
1027
+ create-byan-agent --mode=custom --platforms=copilot-cli
1025
1028
 
1026
1029
  # Full installation without backup
1027
1030
  create-byan-agent --mode=full --no-backup
@@ -14,9 +14,29 @@ const wizard = require('../lib/yanstaller/wizard');
14
14
  const backuper = require('../lib/yanstaller/backuper');
15
15
  const logger = require('../lib/utils/logger');
16
16
 
17
- const YANSTALLER_VERSION = '1.2.0';
18
-
19
- // ASCII Art Banner
17
+ const YANSTALLER_VERSION = '1.2.3';
18
+
19
+ function parseList(value) {
20
+ if (!value) return [];
21
+ if (Array.isArray(value)) return value.map(v => String(v).trim()).filter(Boolean);
22
+ return String(value)
23
+ .split(',')
24
+ .map(v => v.trim())
25
+ .filter(Boolean);
26
+ }
27
+
28
+ function normalizePlatformName(name) {
29
+ if (!name) return name;
30
+ const lower = String(name).toLowerCase();
31
+ if (lower === 'claude') return 'claude-code';
32
+ return lower;
33
+ }
34
+
35
+ function normalizePlatforms(list) {
36
+ return list.map(normalizePlatformName).filter(Boolean);
37
+ }
38
+
39
+ // ASCII Art Banner
20
40
  const banner = `
21
41
  ${chalk.blue('╔════════════════════════════════════════════════════════════╗')}
22
42
  ${chalk.blue('ā•‘')} ${chalk.blue('ā•‘')}
@@ -41,7 +61,7 @@ ${chalk.blue('ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•
41
61
  * 6. VALIDATE - 10 automated checks
42
62
  * 7. WIZARD - Post-install actions
43
63
  */
44
- async function main() {
64
+ async function main(options = {}) {
45
65
  try {
46
66
  console.clear();
47
67
  console.log(banner);
@@ -72,15 +92,80 @@ async function main() {
72
92
  logger.info(`āœ“ Recommended agents: ${chalk.cyan(recommendations.agents.join(', '))}`);
73
93
  }
74
94
 
75
- // STEP 3: INTERVIEW - 7-Question Personalization
76
- logger.info(chalk.bold('\nšŸŽ™ļø STEP 3/7: Interview\n'));
77
- const answers = await interviewer.ask(recommendations);
78
-
79
- // STEP 4: BACKUP (optional)
95
+ // STEP 3: INTERVIEW - 7-Question Personalization
96
+ const isSilent = !!options.silent;
97
+ const forceInteractive = !!options.interactive;
98
+ const hasTty = !!process.stdin.isTTY;
99
+ const forceSilent = !hasTty && !isSilent && !forceInteractive;
100
+
101
+ if (forceSilent) {
102
+ logger.warn('No interactive TTY detected. Falling back to silent mode.');
103
+ }
104
+ if (!hasTty && forceInteractive) {
105
+ logger.warn('Interactive mode forced without TTY. Prompts may not render correctly.');
106
+ }
107
+
108
+ let answers;
109
+
110
+ if (isSilent || forceSilent) {
111
+ logger.info(chalk.bold('\nSTEP 3/7: Interview (skipped - silent)\n'));
112
+
113
+ const parsedAgents = parseList(options.agents);
114
+ const parsedPlatforms = normalizePlatforms(parseList(options.platforms));
115
+
116
+ let mode = options.mode || (parsedAgents.length > 0 ? 'custom' : (recommendations.mode || 'minimal'));
117
+ let agents = parsedAgents;
118
+
119
+ if (agents.length === 0) {
120
+ if (mode === 'recommended' && recommendations && recommendations.agents) {
121
+ agents = recommendations.agents;
122
+ } else if (mode === 'minimal' || mode === 'full') {
123
+ agents = recommender.getAgentList(mode);
124
+ } else if (mode === 'custom') {
125
+ logger.warn('Custom mode selected without agents. Falling back to recommendations.');
126
+ agents = recommendations.agents || ['byan'];
127
+ mode = 'recommended';
128
+ } else {
129
+ agents = recommendations.agents || ['byan'];
130
+ mode = recommendations.mode || 'minimal';
131
+ }
132
+ }
133
+
134
+ let targetPlatforms = parsedPlatforms;
135
+ if (targetPlatforms.length === 0) {
136
+ targetPlatforms = (detection.platforms || [])
137
+ .filter(p => p.detected)
138
+ .map(p => normalizePlatformName(p.name));
139
+ }
140
+
141
+ answers = {
142
+ userName: 'Developer',
143
+ language: 'English',
144
+ mode,
145
+ agents,
146
+ targetPlatforms,
147
+ createSampleAgent: false,
148
+ createBackup: options.backup !== false
149
+ };
150
+ } else {
151
+ logger.info(chalk.bold('\nSTEP 3/7: Interview\n'));
152
+ const preferredPlatforms = normalizePlatforms(parseList(options.platforms));
153
+ answers = await interviewer.ask(recommendations, {
154
+ detection,
155
+ preferredPlatforms
156
+ });
157
+
158
+ if (options.backup === false) {
159
+ answers.createBackup = false;
160
+ }
161
+ }
162
+
163
+ // STEP 4: BACKUP (optional)
80
164
  if (answers.createBackup) {
81
165
  logger.info(chalk.bold('\nšŸ’¾ STEP 4/7: Backup\n'));
82
166
  try {
83
- const backup = await backuper.backup({ projectRoot });
167
+ const bmadPath = path.join(projectRoot, '_bmad');
168
+ const backup = await backuper.backup(bmadPath);
84
169
  logger.info(`āœ“ Backup created: ${chalk.cyan(backup.backupPath)}`);
85
170
  } catch (error) {
86
171
  logger.warn(`⚠ Backup failed (non-critical): ${error.message}`);
@@ -94,15 +179,16 @@ async function main() {
94
179
  const installResult = await installer.install({
95
180
  projectRoot,
96
181
  agents: answers.agents,
97
- platforms: answers.targetPlatforms,
182
+ targetPlatforms: answers.targetPlatforms,
98
183
  userName: answers.userName,
99
184
  language: answers.language,
100
185
  mode: answers.mode
101
186
  });
102
187
 
103
- logger.info(`āœ“ Installed ${chalk.cyan(installResult.installedAgents.length)} agents`);
104
- logger.info(`āœ“ Created ${chalk.cyan(installResult.createdDirectories)} directories`);
105
- logger.info(`āœ“ Generated ${chalk.cyan(installResult.generatedStubs)} platform stubs`);
188
+ logger.info(`āœ“ Installed ${chalk.cyan(installResult.agentsInstalled)} agents`);
189
+ if (installResult.errors && installResult.errors.length > 0) {
190
+ logger.warn(`⚠ ${installResult.errors.length} installation errors`);
191
+ }
106
192
 
107
193
  // STEP 6: VALIDATE - 10 Automated Checks
108
194
  logger.info(chalk.bold('\nāœ… STEP 6/7: Validation\n'));
@@ -120,16 +206,22 @@ async function main() {
120
206
  }
121
207
  }
122
208
 
123
- // STEP 7: WIZARD - Post-Install Actions
124
- logger.info(chalk.bold('\nšŸ§™ STEP 7/7: Post-Install Wizard\n'));
125
- await wizard.show({
126
- installedAgents: installResult.installedAgents,
127
- platforms: answers.targetPlatforms,
128
- userName: answers.userName,
129
- language: answers.language
130
- });
131
-
132
- } catch (error) {
209
+ // STEP 7: WIZARD - Post-Install Actions
210
+ if (isSilent || forceSilent) {
211
+ logger.info(chalk.bold('\nSTEP 7/7: Post-Install Wizard (skipped - silent)\n'));
212
+ } else {
213
+ logger.info(chalk.bold('\nSTEP 7/7: Post-Install Wizard\n'));
214
+ await wizard.show({
215
+ agents: answers.agents,
216
+ targetPlatforms: answers.targetPlatforms,
217
+ mode: answers.mode,
218
+ projectRoot,
219
+ userName: answers.userName,
220
+ language: answers.language
221
+ });
222
+ }
223
+
224
+ } catch (error) {
133
225
  logger.error(chalk.red('\nāŒ Installation failed:\n'));
134
226
  logger.error(error.message);
135
227
  if (error.stack) {
@@ -144,13 +236,14 @@ program
144
236
  .name('create-byan-agent')
145
237
  .description('YANSTALLER - Intelligent installer for BYAN ecosystem (29 agents, multi-platform)')
146
238
  .version(YANSTALLER_VERSION)
147
- .option('--silent', 'Silent installation (no prompts)')
239
+ .option('--silent', 'Silent installation (no prompts)')
240
+ .option('--interactive', 'Force interactive prompts even without TTY')
148
241
  .option('--agents <agents>', 'Comma-separated list of agents to install')
149
242
  .option('--platforms <platforms>', 'Comma-separated list of platforms (copilot-cli,vscode,claude-code,codex)')
150
243
  .option('--mode <mode>', 'Installation mode: recommended, custom, minimal, full')
151
244
  .option('--no-backup', 'Skip pre-install backup')
152
245
  .option('--dry-run', 'Simulate installation without making changes')
153
246
  .option('--verbose', 'Verbose logging')
154
- .action(main);
247
+ .action((opts) => main(opts));
155
248
 
156
249
  program.parse(process.argv);
@@ -6,8 +6,8 @@
6
6
  * @module utils/file-utils
7
7
  */
8
8
 
9
- const fs = require('fs-extra');
10
- const path = require('path');
9
+ const fs = require('fs-extra');
10
+ const path = require('path');
11
11
 
12
12
  /**
13
13
  * Copy file or directory
@@ -92,13 +92,26 @@ async function writeFile(filePath, content) {
92
92
  await fs.writeFile(filePath, content, 'utf8');
93
93
  }
94
94
 
95
- module.exports = {
96
- copy,
97
- exists,
98
- ensureDir,
99
- remove,
100
- readJSON,
101
- writeJSON,
102
- readFile,
103
- writeFile
104
- };
95
+ /**
96
+ * Read directory contents
97
+ *
98
+ * @param {string} dirPath - Directory path
99
+ * @returns {Promise<string[]>} - Array of file/directory names
100
+ */
101
+ async function readDir(dirPath) {
102
+ return fs.readdir(dirPath);
103
+ }
104
+
105
+ module.exports = {
106
+ constants: fs.constants,
107
+ copy,
108
+ exists,
109
+ ensureDir,
110
+ remove,
111
+ readJSON,
112
+ writeJSON,
113
+ readFile,
114
+ writeFile,
115
+ readDir,
116
+ access: fs.access
117
+ };
@@ -166,19 +166,20 @@ async function copyAgentFile(agentName, projectRoot) {
166
166
  /**
167
167
  * Generate platform stubs for all agents
168
168
  *
169
- * @param {string} platform - Platform name ('copilot-cli' | 'vscode' | 'claude' | 'codex')
169
+ * @param {string} platform - Platform name ('copilot-cli' | 'vscode' | 'claude-code' | 'codex')
170
170
  * @param {InstallConfig} config - Installation config
171
171
  * @returns {Promise<void>}
172
172
  */
173
- async function generatePlatformStubs(platform, config) {
174
- const platformModule = require(`../platforms/${platform}`);
175
-
176
- if (!platformModule || typeof platformModule.install !== 'function') {
177
- throw new Error(`Platform module not found or invalid: ${platform}`);
178
- }
179
-
180
- await platformModule.install(config.projectRoot, config.agents, config);
181
- }
173
+ async function generatePlatformStubs(platform, config) {
174
+ const normalized = platform === 'claude' ? 'claude-code' : platform;
175
+ const platformModule = require(`../platforms/${normalized}`);
176
+
177
+ if (!platformModule || typeof platformModule.install !== 'function') {
178
+ throw new Error(`Platform module not found or invalid: ${platform}`);
179
+ }
180
+
181
+ await platformModule.install(config.projectRoot, config.agents, config);
182
+ }
182
183
 
183
184
  /**
184
185
  * Create module config file
@@ -8,10 +8,34 @@
8
8
  * @module yanstaller/interviewer
9
9
  */
10
10
 
11
- const inquirer = require('inquirer');
12
- const chalk = require('chalk');
13
- const logger = require('../utils/logger');
14
-
11
+ const inquirer = require('inquirer');
12
+ const chalk = require('chalk');
13
+ const logger = require('../utils/logger');
14
+
15
+ function normalizePlatformName(name) {
16
+ if (!name) return name;
17
+ const lower = String(name).toLowerCase();
18
+ if (lower === 'claude') return 'claude-code';
19
+ return lower;
20
+ }
21
+
22
+ function buildDefaultPlatforms(options = {}) {
23
+ const preferred = Array.isArray(options.preferredPlatforms)
24
+ ? options.preferredPlatforms.map(normalizePlatformName).filter(Boolean)
25
+ : [];
26
+
27
+ if (preferred.length > 0) return preferred;
28
+
29
+ const detected = (options.detection && Array.isArray(options.detection.platforms))
30
+ ? options.detection.platforms
31
+ .filter(p => p.detected)
32
+ .map(p => normalizePlatformName(p.name))
33
+ .filter(Boolean)
34
+ : [];
35
+
36
+ return detected;
37
+ }
38
+
15
39
  /**
16
40
  * @typedef {Object} InterviewResult
17
41
  * @property {string} userName
@@ -28,7 +52,7 @@ const logger = require('../utils/logger');
28
52
  * @param {import('./recommender').Recommendation} recommendation - Recommended config
29
53
  * @returns {Promise<InterviewResult>}
30
54
  */
31
- async function ask(recommendation) {
55
+ async function ask(recommendation, options = {}) {
32
56
  logger.info(chalk.bold('\nšŸŽ™ļø YANSTALLER Quick Interview\n'));
33
57
  logger.info('Just 5-7 questions to personalize your BYAN installation (<5 min)\n');
34
58
 
@@ -114,21 +138,25 @@ async function ask(recommendation) {
114
138
  selectedAgents = getAllAgents();
115
139
  }
116
140
 
117
- // Q5: Target platforms
118
- const platformAnswer = await inquirer.prompt([
119
- {
120
- type: 'checkbox',
121
- name: 'platforms',
122
- message: 'Which platforms to install on?',
123
- choices: [
124
- { name: 'GitHub Copilot CLI (.github/agents/)', value: 'copilot-cli', checked: true },
125
- { name: 'VSCode Copilot Extension', value: 'vscode', checked: true },
126
- { name: 'Codex (.codex/prompts/)', value: 'codex', checked: false },
127
- { name: 'Claude Code (MCP server)', value: 'claude-code', checked: false }
128
- ],
129
- validate: (input) => input.length > 0 || 'Select at least one platform'
130
- }
131
- ]);
141
+ // Q5: Target platforms
142
+ const defaultPlatforms = buildDefaultPlatforms(options);
143
+ const useDefaultPlatforms = defaultPlatforms.length > 0;
144
+ const isDefault = (value, fallback) => useDefaultPlatforms ? defaultPlatforms.includes(value) : fallback;
145
+
146
+ const platformAnswer = await inquirer.prompt([
147
+ {
148
+ type: 'checkbox',
149
+ name: 'platforms',
150
+ message: 'Which platforms to install on?',
151
+ choices: [
152
+ { name: 'GitHub Copilot CLI (.github/agents/)', value: 'copilot-cli', checked: isDefault('copilot-cli', true) },
153
+ { name: 'VSCode Copilot Extension', value: 'vscode', checked: isDefault('vscode', true) },
154
+ { name: 'Codex (.codex/prompts/)', value: 'codex', checked: isDefault('codex', false) },
155
+ { name: 'Claude Code (MCP server)', value: 'claude-code', checked: isDefault('claude-code', false) }
156
+ ],
157
+ validate: (input) => input.length > 0 || 'Select at least one platform'
158
+ }
159
+ ]);
132
160
 
133
161
  // Q6: Create sample agent
134
162
  const sampleAnswer = await inquirer.prompt([
@@ -176,18 +176,23 @@ function detectFramework(deps) {
176
176
  * @returns {string[]} - Agent names
177
177
  */
178
178
  function getRecommendedAgents(projectType, platforms = []) {
179
- const baseAgents = ['byan', 'rachid', 'patnote', 'carmack'];
179
+ // Only recommend agents that exist in templates/_bmad/
180
+ // Available: byan, byan-test, rachid, patnote, marc, agent-builder, module-builder, workflow-builder
181
+ const availableAgents = ['byan', 'byan-test', 'rachid', 'patnote', 'marc', 'agent-builder', 'module-builder', 'workflow-builder'];
182
+
183
+ const baseAgents = ['byan', 'rachid', 'patnote'];
180
184
 
181
185
  // Add MARC if Copilot CLI detected
182
186
  if (platforms && platforms.some(p => p.name === 'copilot-cli' && p.detected)) {
183
187
  baseAgents.push('marc');
184
188
  }
185
189
 
190
+ // Simple recommendations based on available agents only
186
191
  const recommendations = {
187
- frontend: [...baseAgents, 'ux-designer', 'dev', 'quinn'],
188
- backend: [...baseAgents, 'architect', 'dev', 'quinn'],
189
- fullstack: [...baseAgents, 'architect', 'dev', 'ux-designer', 'quinn', 'pm'],
190
- library: [...baseAgents, 'dev', 'tech-writer', 'quinn'],
192
+ frontend: [...baseAgents],
193
+ backend: [...baseAgents],
194
+ fullstack: [...baseAgents],
195
+ library: [...baseAgents],
191
196
  unknown: baseAgents
192
197
  };
193
198
 
@@ -11,7 +11,8 @@
11
11
  const path = require('path');
12
12
  const fileUtils = require('../utils/file-utils');
13
13
  const yamlUtils = require('../utils/yaml-utils');
14
- const { execSync } = require('child_process');
14
+ const { execSync } = require('child_process');
15
+ const fs = require('fs-extra');
15
16
 
16
17
  /**
17
18
  * @typedef {Object} ValidationResult
@@ -252,7 +253,7 @@ async function checkConfigFiles(config) {
252
253
  if (await fileUtils.exists(configPath)) {
253
254
  try {
254
255
  const configContent = await fileUtils.readFile(configPath, 'utf8');
255
- const parsedConfig = yamlUtils.load(configContent);
256
+ const parsedConfig = yamlUtils.parse(configContent);
256
257
 
257
258
  // Validate required fields
258
259
  if (!parsedConfig.user_name) {
@@ -484,28 +485,51 @@ async function checkWorkflows(config) {
484
485
  /**
485
486
  * Check 9: Templates valid
486
487
  */
487
- async function checkTemplates(config) {
488
- const templatesDir = path.join(__dirname, '..', '..', 'templates', '_bmad');
489
-
490
- if (!await fileUtils.exists(templatesDir)) {
491
- return {
492
- id: 'templates',
493
- name: 'Template files',
494
- passed: false,
495
- message: 'Templates directory not found',
496
- severity: 'warning'
497
- };
498
- }
499
-
500
- const modules = ['core', 'bmm', 'bmb', 'tea', 'cis'];
501
- const issues = [];
502
-
503
- for (const module of modules) {
504
- const agentsDir = path.join(templatesDir, module, 'agents');
505
- if (!await fileUtils.exists(agentsDir)) {
506
- issues.push(`${module}/agents missing`);
507
- }
508
- }
488
+ async function checkTemplates(config) {
489
+ const templatesDir = path.join(__dirname, '..', '..', 'templates', '_bmad');
490
+
491
+ if (!await fileUtils.exists(templatesDir)) {
492
+ return {
493
+ id: 'templates',
494
+ name: 'Template files',
495
+ passed: false,
496
+ message: 'Templates directory not found',
497
+ severity: 'warning'
498
+ };
499
+ }
500
+
501
+ const entries = await fileUtils.readDir(templatesDir);
502
+ const modules = [];
503
+ for (const entry of entries) {
504
+ const entryPath = path.join(templatesDir, entry);
505
+ try {
506
+ const stat = await fs.stat(entryPath);
507
+ if (stat.isDirectory()) {
508
+ modules.push(entry);
509
+ }
510
+ } catch {
511
+ // Ignore unreadable entries
512
+ }
513
+ }
514
+
515
+ if (modules.length === 0) {
516
+ return {
517
+ id: 'templates',
518
+ name: 'Template files',
519
+ passed: false,
520
+ message: 'No template modules found',
521
+ severity: 'warning'
522
+ };
523
+ }
524
+
525
+ const issues = [];
526
+
527
+ for (const module of modules) {
528
+ const agentsDir = path.join(templatesDir, module, 'agents');
529
+ if (!await fileUtils.exists(agentsDir)) {
530
+ issues.push(`${module}/agents missing`);
531
+ }
532
+ }
509
533
 
510
534
  if (issues.length > 0) {
511
535
  return {
@@ -25,12 +25,30 @@ async function show(config) {
25
25
  logger.info(chalk.gray('═'.repeat(60)));
26
26
  logger.info('');
27
27
 
28
- // Show installation summary
28
+ // Show installation summary with null-checks
29
29
  logger.info(chalk.bold('šŸ“Š Installation Summary:'));
30
- logger.info(` • Agents installed: ${chalk.cyan(config.agents.length)}`);
31
- logger.info(` • Platforms: ${chalk.cyan(config.targetPlatforms.join(', '))}`);
32
- logger.info(` • Mode: ${chalk.cyan(config.mode)}`);
33
- logger.info(` • Location: ${chalk.gray(config.projectRoot)}`);
30
+
31
+ if (config && config.agents && Array.isArray(config.agents)) {
32
+ logger.info(` • Agents installed: ${chalk.cyan(config.agents.length)}`);
33
+ } else {
34
+ logger.info(` • Agents installed: ${chalk.cyan(0)}`);
35
+ }
36
+
37
+ if (config && config.targetPlatforms && Array.isArray(config.targetPlatforms)) {
38
+ logger.info(` • Platforms: ${chalk.cyan(config.targetPlatforms.join(', '))}`);
39
+ } else if (config && config.platforms && Array.isArray(config.platforms)) {
40
+ logger.info(` • Platforms: ${chalk.cyan(config.platforms.join(', '))}`);
41
+ } else {
42
+ logger.info(` • Platforms: ${chalk.cyan('none')}`);
43
+ }
44
+
45
+ if (config && config.mode) {
46
+ logger.info(` • Mode: ${chalk.cyan(config.mode)}`);
47
+ }
48
+
49
+ if (config && config.projectRoot) {
50
+ logger.info(` • Location: ${chalk.gray(config.projectRoot)}`);
51
+ }
34
52
  logger.info('');
35
53
 
36
54
  const choices = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-byan-agent",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "description": "NPX installer for BYAN ecosystem - Agent creators (BYAN, BYAN-Test) with deployment (RACHID), integration (MARC), updates (PATNOTE), and optimization (CARMACK)",
5
5
  "bin": {
6
6
  "create-byan-agent": "bin/create-byan-agent.js"
@@ -9,7 +9,10 @@
9
9
  "start": "node bin/create-byan-agent.js",
10
10
  "test": "jest",
11
11
  "test:watch": "jest --watch",
12
- "test:coverage": "jest --coverage"
12
+ "test:coverage": "jest --coverage",
13
+ "test:e2e": "node test-e2e.js",
14
+ "pretest:e2e": "echo '\nšŸ” Running E2E test before publish...\n'",
15
+ "prepublishOnly": "npm run test:e2e"
13
16
  },
14
17
  "keywords": [
15
18
  "byan",