create-byan-agent 2.2.1 ā 2.2.2
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/install/lib/platforms/claude-code.js +115 -5
- package/install/lib/yanstaller/agent-launcher.js +348 -0
- package/install/lib/yanstaller/index.js +56 -10
- package/install/lib/yanstaller/platform-selector.js +277 -0
- package/install/package.json +2 -2
- package/install/templates/.github/agents/bmad-agent-claude.md +48 -0
- package/install/templates/.github/agents/bmad-agent-codex.md +48 -0
- package/install/templates/_byan/bmb/agents/claude.md +502 -0
- package/install/templates/_byan/bmb/agents/codex.md +407 -0
- package/package.json +2 -2
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Claude Code Platform Support
|
|
3
3
|
*
|
|
4
4
|
* Detects and installs MCP server config for Claude Code.
|
|
5
|
+
* Uses agent Claude for native integration via MCP protocol.
|
|
5
6
|
*
|
|
6
7
|
* @module platforms/claude-code
|
|
7
8
|
*/
|
|
@@ -9,8 +10,10 @@
|
|
|
9
10
|
const path = require('path');
|
|
10
11
|
const os = require('os');
|
|
11
12
|
const fileUtils = require('../utils/file-utils');
|
|
13
|
+
const logger = require('../utils/logger');
|
|
12
14
|
|
|
13
15
|
const PLATFORM_NAME = 'Claude Code';
|
|
16
|
+
const MCP_SERVER_FILENAME = 'byan-mcp-server.js';
|
|
14
17
|
|
|
15
18
|
/**
|
|
16
19
|
* Get config path for current platform
|
|
@@ -48,24 +51,131 @@ async function detect() {
|
|
|
48
51
|
/**
|
|
49
52
|
* Install MCP server config for Claude Code
|
|
50
53
|
*
|
|
54
|
+
* Delegates to agent Claude for native integration.
|
|
55
|
+
* Falls back to basic JSON update if agent unavailable.
|
|
56
|
+
*
|
|
51
57
|
* @param {string} projectRoot - Project root directory
|
|
52
58
|
* @param {string[]} agents - Agent names to install
|
|
53
59
|
* @param {Object} config - Installation config
|
|
54
|
-
* @
|
|
60
|
+
* @param {Object} [options] - Installation options
|
|
61
|
+
* @param {string} [options.specialist] - Specialist agent to use (e.g., 'claude')
|
|
62
|
+
* @param {boolean} [options.useAgent] - Use specialist agent if available (default: true)
|
|
63
|
+
* @returns {Promise<{success: boolean, installed: number, method: string}>}
|
|
55
64
|
*/
|
|
56
|
-
async function install(projectRoot, agents, config) {
|
|
65
|
+
async function install(projectRoot, agents, config, options = {}) {
|
|
57
66
|
const configPath = getConfigPath();
|
|
58
67
|
|
|
59
68
|
if (!configPath) {
|
|
60
69
|
throw new Error(`Unsupported platform: ${os.platform()}`);
|
|
61
70
|
}
|
|
62
71
|
|
|
63
|
-
|
|
64
|
-
|
|
72
|
+
const useAgent = options.useAgent !== false && options.specialist === 'claude';
|
|
73
|
+
|
|
74
|
+
if (useAgent) {
|
|
75
|
+
logger.info('Using agent Claude for native MCP integration...');
|
|
76
|
+
return await installViaCopilotAgent(projectRoot, agents, config);
|
|
77
|
+
} else {
|
|
78
|
+
logger.info('Using direct MCP configuration...');
|
|
79
|
+
return await installDirectMCP(projectRoot, agents, config);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Install via agent Claude (native integration)
|
|
85
|
+
*
|
|
86
|
+
* @param {string} projectRoot
|
|
87
|
+
* @param {string[]} agents
|
|
88
|
+
* @param {Object} config
|
|
89
|
+
* @returns {Promise<{success: boolean, installed: number, method: string}>}
|
|
90
|
+
*/
|
|
91
|
+
async function installViaCopilotAgent(projectRoot, agents, config) {
|
|
92
|
+
const agentLauncher = require('../yanstaller/agent-launcher');
|
|
93
|
+
|
|
94
|
+
// Check if native launch is available
|
|
95
|
+
if (agentLauncher.supportsNativeLaunch('claude')) {
|
|
96
|
+
logger.info('\nš Launching agent Claude for MCP integration...');
|
|
97
|
+
|
|
98
|
+
// Launch agent Claude with create-mcp-server action
|
|
99
|
+
const result = await agentLauncher.launch({
|
|
100
|
+
agent: 'claude',
|
|
101
|
+
platform: 'claude',
|
|
102
|
+
prompt: 'create-mcp-server'
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
if (result.success) {
|
|
106
|
+
return {
|
|
107
|
+
success: true,
|
|
108
|
+
installed: agents.length,
|
|
109
|
+
method: 'agent-claude-native'
|
|
110
|
+
};
|
|
111
|
+
} else {
|
|
112
|
+
logger.warn(`Native launch failed: ${result.error}`);
|
|
113
|
+
logger.info('Falling back to manual instructions...');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Fallback: Manual instructions
|
|
118
|
+
logger.info('\nš To complete Claude Code integration:');
|
|
119
|
+
logger.info(' 1. Run: @bmad-agent-claude');
|
|
120
|
+
logger.info(' 2. Select option 1: Create MCP server for BYAN agents');
|
|
121
|
+
logger.info(' 3. Follow the guided setup\n');
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
success: true,
|
|
125
|
+
installed: agents.length,
|
|
126
|
+
method: 'agent-claude-guided'
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Install via direct MCP config update
|
|
132
|
+
*
|
|
133
|
+
* @param {string} projectRoot
|
|
134
|
+
* @param {string[]} agents
|
|
135
|
+
* @param {Object} config
|
|
136
|
+
* @returns {Promise<{success: boolean, installed: number, method: string}>}
|
|
137
|
+
*/
|
|
138
|
+
async function installDirectMCP(projectRoot, agents, config) {
|
|
139
|
+
const configPath = getConfigPath();
|
|
140
|
+
const mcpServerPath = path.join(projectRoot, MCP_SERVER_FILENAME);
|
|
141
|
+
|
|
142
|
+
// Check if MCP server exists
|
|
143
|
+
if (!await fileUtils.exists(mcpServerPath)) {
|
|
144
|
+
logger.warn(`MCP server not found at: ${mcpServerPath}`);
|
|
145
|
+
logger.warn('Run @bmad-agent-claude to generate MCP server first.');
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
success: false,
|
|
149
|
+
installed: 0,
|
|
150
|
+
method: 'direct-mcp-failed'
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Backup existing config
|
|
155
|
+
const backupPath = `${configPath}.backup`;
|
|
156
|
+
await fileUtils.copy(configPath, backupPath);
|
|
157
|
+
logger.info(`Backed up config to: ${backupPath}`);
|
|
158
|
+
|
|
159
|
+
// Read and update config
|
|
160
|
+
const existingConfig = await fileUtils.readJson(configPath);
|
|
161
|
+
existingConfig.mcpServers = existingConfig.mcpServers || {};
|
|
162
|
+
|
|
163
|
+
existingConfig.mcpServers.byan = {
|
|
164
|
+
command: 'node',
|
|
165
|
+
args: [mcpServerPath],
|
|
166
|
+
env: {
|
|
167
|
+
PROJECT_ROOT: projectRoot
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// Write updated config
|
|
172
|
+
await fileUtils.writeJson(configPath, existingConfig, { spaces: 2 });
|
|
173
|
+
logger.info(`Updated MCP config: ${configPath}`);
|
|
65
174
|
|
|
66
175
|
return {
|
|
67
176
|
success: true,
|
|
68
|
-
installed: agents.length
|
|
177
|
+
installed: agents.length,
|
|
178
|
+
method: 'direct-mcp'
|
|
69
179
|
};
|
|
70
180
|
}
|
|
71
181
|
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AGENT LAUNCHER Module
|
|
3
|
+
*
|
|
4
|
+
* Launches specialist agents using native platform commands.
|
|
5
|
+
* Each platform has its own invocation syntax.
|
|
6
|
+
*
|
|
7
|
+
* @module yanstaller/agent-launcher
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { execSync, spawn } = require('child_process');
|
|
11
|
+
const logger = require('../utils/logger');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {Object} LaunchOptions
|
|
15
|
+
* @property {string} agent - Agent name (e.g., 'claude', 'marc')
|
|
16
|
+
* @property {string} platform - Platform ID (e.g., 'copilot-cli', 'claude')
|
|
17
|
+
* @property {string} [prompt] - Initial prompt/action
|
|
18
|
+
* @property {string} [model] - Model to use
|
|
19
|
+
* @property {Object} [config] - Additional config
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @typedef {Object} LaunchResult
|
|
24
|
+
* @property {boolean} success
|
|
25
|
+
* @property {string} method - How agent was launched
|
|
26
|
+
* @property {string} [output] - Command output
|
|
27
|
+
* @property {string} [error] - Error message if failed
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Platform-specific launch configurations
|
|
32
|
+
*/
|
|
33
|
+
const LAUNCH_CONFIGS = {
|
|
34
|
+
'copilot-cli': {
|
|
35
|
+
command: 'gh',
|
|
36
|
+
args: (agent, options) => {
|
|
37
|
+
const args = ['copilot'];
|
|
38
|
+
|
|
39
|
+
// Use @agent syntax if available
|
|
40
|
+
if (agent) {
|
|
41
|
+
args.push(`@bmad-agent-${agent}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (options.prompt) {
|
|
45
|
+
args.push(options.prompt);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return args;
|
|
49
|
+
},
|
|
50
|
+
checkAvailable: () => {
|
|
51
|
+
try {
|
|
52
|
+
execSync('which gh', { stdio: 'ignore' });
|
|
53
|
+
return true;
|
|
54
|
+
} catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
'claude': {
|
|
61
|
+
command: 'claude',
|
|
62
|
+
args: (agent, options = {}) => {
|
|
63
|
+
const args = [];
|
|
64
|
+
|
|
65
|
+
// Agent specification
|
|
66
|
+
if (agent) {
|
|
67
|
+
args.push('--agent', agent);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Model selection
|
|
71
|
+
if (options.model) {
|
|
72
|
+
args.push('--model', options.model);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// System prompt for context (before positional prompt)
|
|
76
|
+
if (options.systemPrompt) {
|
|
77
|
+
args.push('--system-prompt', options.systemPrompt);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// MCP config if needed
|
|
81
|
+
if (options.mcpConfig) {
|
|
82
|
+
args.push('--mcp-config', options.mcpConfig);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Prompt as POSITIONAL argument (not --prompt)
|
|
86
|
+
// Must come AFTER all flags
|
|
87
|
+
if (options.prompt) {
|
|
88
|
+
args.push(options.prompt);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return args;
|
|
92
|
+
},
|
|
93
|
+
checkAvailable: () => {
|
|
94
|
+
try {
|
|
95
|
+
execSync('which claude', { stdio: 'ignore' });
|
|
96
|
+
return true;
|
|
97
|
+
} catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
'codex': {
|
|
104
|
+
command: 'codex',
|
|
105
|
+
args: (agent, options = {}) => {
|
|
106
|
+
const args = [];
|
|
107
|
+
|
|
108
|
+
// Codex uses "skills" not "agents"
|
|
109
|
+
// Format: codex skill <skill-name> [prompt]
|
|
110
|
+
|
|
111
|
+
if (agent) {
|
|
112
|
+
args.push('skill', `bmad-${agent}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Prompt as positional argument
|
|
116
|
+
if (options.prompt) {
|
|
117
|
+
args.push(options.prompt);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Model selection (if Codex supports it)
|
|
121
|
+
if (options.model) {
|
|
122
|
+
args.push('--model', options.model);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return args;
|
|
126
|
+
},
|
|
127
|
+
checkAvailable: () => {
|
|
128
|
+
try {
|
|
129
|
+
execSync('which codex', { stdio: 'ignore' });
|
|
130
|
+
return true;
|
|
131
|
+
} catch {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Launch agent using native platform command
|
|
140
|
+
*
|
|
141
|
+
* @param {LaunchOptions} options - Launch options
|
|
142
|
+
* @returns {Promise<LaunchResult>}
|
|
143
|
+
*/
|
|
144
|
+
async function launch(options) {
|
|
145
|
+
const { agent, platform, prompt, model, config } = options;
|
|
146
|
+
|
|
147
|
+
const platformConfig = LAUNCH_CONFIGS[platform];
|
|
148
|
+
|
|
149
|
+
if (!platformConfig) {
|
|
150
|
+
return {
|
|
151
|
+
success: false,
|
|
152
|
+
method: 'unsupported',
|
|
153
|
+
error: `Platform ${platform} not supported for native launch`
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Check if platform command is available
|
|
158
|
+
if (!platformConfig.checkAvailable()) {
|
|
159
|
+
return {
|
|
160
|
+
success: false,
|
|
161
|
+
method: 'command-not-found',
|
|
162
|
+
error: `Command '${platformConfig.command}' not found in PATH`
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Build command arguments
|
|
167
|
+
const args = platformConfig.args(agent, {
|
|
168
|
+
prompt,
|
|
169
|
+
model,
|
|
170
|
+
systemPrompt: config?.systemPrompt,
|
|
171
|
+
mcpConfig: config?.mcpConfig
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const fullCommand = `${platformConfig.command} ${args.join(' ')}`;
|
|
175
|
+
|
|
176
|
+
logger.info(`Launching agent via: ${fullCommand}`);
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
// Launch in interactive mode (inherit stdio)
|
|
180
|
+
const result = spawn(platformConfig.command, args, {
|
|
181
|
+
stdio: 'inherit',
|
|
182
|
+
shell: true
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
return new Promise((resolve) => {
|
|
186
|
+
result.on('close', (code) => {
|
|
187
|
+
if (code === 0) {
|
|
188
|
+
resolve({
|
|
189
|
+
success: true,
|
|
190
|
+
method: 'native-interactive',
|
|
191
|
+
output: `Agent launched successfully via ${platform}`
|
|
192
|
+
});
|
|
193
|
+
} else {
|
|
194
|
+
resolve({
|
|
195
|
+
success: false,
|
|
196
|
+
method: 'native-interactive',
|
|
197
|
+
error: `Agent exited with code ${code}`
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
result.on('error', (error) => {
|
|
203
|
+
resolve({
|
|
204
|
+
success: false,
|
|
205
|
+
method: 'native-interactive',
|
|
206
|
+
error: error.message
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
} catch (error) {
|
|
211
|
+
return {
|
|
212
|
+
success: false,
|
|
213
|
+
method: 'native-interactive',
|
|
214
|
+
error: error.message
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Launch agent with specific prompt (non-interactive)
|
|
221
|
+
*
|
|
222
|
+
* Useful for automation or scripted workflows.
|
|
223
|
+
*
|
|
224
|
+
* @param {LaunchOptions} options - Launch options
|
|
225
|
+
* @returns {Promise<LaunchResult>}
|
|
226
|
+
*/
|
|
227
|
+
async function launchWithPrompt(options) {
|
|
228
|
+
const { agent, platform, prompt, model, config } = options;
|
|
229
|
+
|
|
230
|
+
if (!prompt) {
|
|
231
|
+
throw new Error('Prompt required for non-interactive launch');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const platformConfig = LAUNCH_CONFIGS[platform];
|
|
235
|
+
|
|
236
|
+
if (!platformConfig) {
|
|
237
|
+
return {
|
|
238
|
+
success: false,
|
|
239
|
+
method: 'unsupported',
|
|
240
|
+
error: `Platform ${platform} not supported`
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (!platformConfig.checkAvailable()) {
|
|
245
|
+
return {
|
|
246
|
+
success: false,
|
|
247
|
+
method: 'command-not-found',
|
|
248
|
+
error: `Command '${platformConfig.command}' not found`
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const args = platformConfig.args(agent, {
|
|
253
|
+
prompt,
|
|
254
|
+
model,
|
|
255
|
+
systemPrompt: config?.systemPrompt
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// Add --print flag for Claude to get output
|
|
259
|
+
if (platform === 'claude') {
|
|
260
|
+
args.unshift('--print');
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const fullCommand = `${platformConfig.command} ${args.join(' ')}`;
|
|
264
|
+
|
|
265
|
+
logger.info(`Executing: ${fullCommand}`);
|
|
266
|
+
|
|
267
|
+
try {
|
|
268
|
+
const output = execSync(fullCommand, {
|
|
269
|
+
encoding: 'utf8',
|
|
270
|
+
maxBuffer: 10 * 1024 * 1024 // 10MB
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
return {
|
|
274
|
+
success: true,
|
|
275
|
+
method: 'native-print',
|
|
276
|
+
output: output.trim()
|
|
277
|
+
};
|
|
278
|
+
} catch (error) {
|
|
279
|
+
return {
|
|
280
|
+
success: false,
|
|
281
|
+
method: 'native-print',
|
|
282
|
+
error: error.message
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Generate launch instructions for manual invocation
|
|
289
|
+
*
|
|
290
|
+
* Used as fallback when native launch isn't possible.
|
|
291
|
+
*
|
|
292
|
+
* @param {LaunchOptions} options - Launch options
|
|
293
|
+
* @returns {string} - Human-readable instructions
|
|
294
|
+
*/
|
|
295
|
+
function getLaunchInstructions(options) {
|
|
296
|
+
const { agent, platform, prompt, model } = options;
|
|
297
|
+
|
|
298
|
+
const platformConfig = LAUNCH_CONFIGS[platform];
|
|
299
|
+
|
|
300
|
+
if (!platformConfig) {
|
|
301
|
+
return `Platform ${platform} not yet supported for automated launch.\nPlease activate the agent manually.`;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const args = platformConfig.args(agent, { prompt, model });
|
|
305
|
+
const command = `${platformConfig.command} ${args.join(' ')}`;
|
|
306
|
+
|
|
307
|
+
return `
|
|
308
|
+
To activate the agent, run:
|
|
309
|
+
|
|
310
|
+
${command}
|
|
311
|
+
|
|
312
|
+
Or in interactive mode:
|
|
313
|
+
${platformConfig.command}
|
|
314
|
+
Then: @bmad-agent-${agent}
|
|
315
|
+
`;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Check if platform supports native agent launch
|
|
320
|
+
*
|
|
321
|
+
* @param {string} platform - Platform ID
|
|
322
|
+
* @returns {boolean}
|
|
323
|
+
*/
|
|
324
|
+
function supportsNativeLaunch(platform) {
|
|
325
|
+
const config = LAUNCH_CONFIGS[platform];
|
|
326
|
+
if (!config) return false;
|
|
327
|
+
return config.checkAvailable();
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Get available platforms for native launch
|
|
332
|
+
*
|
|
333
|
+
* @returns {string[]} - List of platform IDs
|
|
334
|
+
*/
|
|
335
|
+
function getAvailablePlatforms() {
|
|
336
|
+
return Object.keys(LAUNCH_CONFIGS).filter(platform => {
|
|
337
|
+
const config = LAUNCH_CONFIGS[platform];
|
|
338
|
+
return config.checkAvailable();
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
module.exports = {
|
|
343
|
+
launch,
|
|
344
|
+
launchWithPrompt,
|
|
345
|
+
getLaunchInstructions,
|
|
346
|
+
supportsNativeLaunch,
|
|
347
|
+
getAvailablePlatforms
|
|
348
|
+
};
|
|
@@ -14,12 +14,14 @@ const troubleshooter = require('./troubleshooter');
|
|
|
14
14
|
const interviewer = require('./interviewer');
|
|
15
15
|
const backuper = require('./backuper');
|
|
16
16
|
const wizard = require('./wizard');
|
|
17
|
+
const platformSelector = require('./platform-selector');
|
|
17
18
|
const logger = require('../utils/logger');
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* @typedef {Object} YanInstallerOptions
|
|
21
22
|
* @property {boolean} [yes] - Skip confirmations (--yes flag)
|
|
22
23
|
* @property {string} [mode] - Installation mode: 'full' | 'minimal' | 'custom'
|
|
24
|
+
* @property {string[]} [platforms] - Target platforms (override detection)
|
|
23
25
|
* @property {boolean} [verbose] - Verbose output
|
|
24
26
|
* @property {boolean} [quiet] - Minimal output
|
|
25
27
|
*/
|
|
@@ -34,16 +36,57 @@ async function install(options = {}) {
|
|
|
34
36
|
let backupPath = null;
|
|
35
37
|
|
|
36
38
|
try {
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
//
|
|
42
|
-
|
|
39
|
+
// Phase 1: Detect environment
|
|
40
|
+
logger.info('š Detecting environment...');
|
|
41
|
+
const detection = await detector.detect();
|
|
42
|
+
|
|
43
|
+
// Phase 2: Validate Node version (FAIL FAST)
|
|
44
|
+
if (!detector.isNodeVersionValid(detection.nodeVersion, '18.0.0')) {
|
|
45
|
+
throw new Error(`Node.js >= 18.0.0 required. Found: ${detection.nodeVersion}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Phase 3: Platform Selection
|
|
49
|
+
let platformSelection;
|
|
50
|
+
if (options.platforms) {
|
|
51
|
+
// CLI override
|
|
52
|
+
platformSelection = {
|
|
53
|
+
platforms: options.platforms,
|
|
54
|
+
mode: 'manual'
|
|
55
|
+
};
|
|
56
|
+
} else if (options.yes) {
|
|
57
|
+
// Auto mode
|
|
58
|
+
platformSelection = {
|
|
59
|
+
platforms: detection.platforms.filter(p => p.detected).map(p => p.name),
|
|
60
|
+
mode: 'auto'
|
|
61
|
+
};
|
|
62
|
+
} else {
|
|
63
|
+
// Interactive selection
|
|
64
|
+
platformSelection = await platformSelector.select(detection);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
logger.info(`\nā Selected ${platformSelection.platforms.length} platform(s)`);
|
|
68
|
+
logger.info(` Mode: ${platformSelection.mode}`);
|
|
69
|
+
if (platformSelection.specialist) {
|
|
70
|
+
logger.info(` Specialist: @bmad-agent-${platformSelection.specialist}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Phase 4: Recommend configuration
|
|
74
|
+
// TODO: Implement
|
|
75
|
+
|
|
76
|
+
// Phase 5: Run interview (unless --yes)
|
|
77
|
+
// TODO: Implement
|
|
78
|
+
|
|
79
|
+
// Phase 6: Backup existing installation
|
|
43
80
|
// backupPath = await backuper.backup('_bmad');
|
|
44
|
-
|
|
45
|
-
// 7
|
|
46
|
-
//
|
|
81
|
+
|
|
82
|
+
// Phase 7: Install agents
|
|
83
|
+
// TODO: Implement
|
|
84
|
+
|
|
85
|
+
// Phase 8: Validate installation
|
|
86
|
+
// TODO: Implement
|
|
87
|
+
|
|
88
|
+
// Phase 9: Show post-install wizard
|
|
89
|
+
// TODO: Implement
|
|
47
90
|
} catch (error) {
|
|
48
91
|
// ROLLBACK STRATEGY: Leave partial state + clear message
|
|
49
92
|
// Rationale (Mantra #37 Ockham's Razor):
|
|
@@ -89,5 +132,8 @@ async function update(version) {
|
|
|
89
132
|
module.exports = {
|
|
90
133
|
install,
|
|
91
134
|
uninstall,
|
|
92
|
-
update
|
|
135
|
+
update,
|
|
136
|
+
// Expose for testing
|
|
137
|
+
detector,
|
|
138
|
+
platformSelector
|
|
93
139
|
};
|