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 +110 -1
- package/README.md +11 -8
- package/bin/create-byan-agent.js +119 -26
- package/lib/utils/file-utils.js +25 -12
- package/lib/yanstaller/installer.js +11 -10
- package/lib/yanstaller/interviewer.js +48 -20
- package/lib/yanstaller/recommender.js +10 -5
- package/lib/yanstaller/validator.js +48 -24
- package/lib/yanstaller/wizard.js +23 -5
- package/package.json +5 -2
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
|
-
[](https://www.npmjs.com/package/create-byan-agent)
|
|
850
850
|
[](LICENSE)
|
|
851
851
|
[](https://nodejs.org)
|
|
852
852
|
[](#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
|
-
#
|
|
1024
|
-
create-byan-agent --
|
|
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
|
package/bin/create-byan-agent.js
CHANGED
|
@@ -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.
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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);
|
package/lib/utils/file-utils.js
CHANGED
|
@@ -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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
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
|
|
188
|
-
backend: [...baseAgents
|
|
189
|
-
fullstack: [...baseAgents
|
|
190
|
-
library: [...baseAgents
|
|
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.
|
|
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
|
|
501
|
-
const
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
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 {
|
package/lib/yanstaller/wizard.js
CHANGED
|
@@ -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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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.
|
|
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",
|