chore: add code formatting config and pre-commit hooks (#450)
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { program } = require('commander');
|
||||
const path = require('path');
|
||||
const fs = require('fs').promises;
|
||||
const path = require('node:path');
|
||||
const fs = require('node:fs').promises;
|
||||
const yaml = require('js-yaml');
|
||||
const chalk = require('chalk').default || require('chalk');
|
||||
const inquirer = require('inquirer').default || require('inquirer');
|
||||
const semver = require('semver');
|
||||
const https = require('https');
|
||||
const https = require('node:https');
|
||||
|
||||
// Handle both execution contexts (from root via npx or from installer directory)
|
||||
let version;
|
||||
@@ -18,18 +18,20 @@ try {
|
||||
version = require('../package.json').version;
|
||||
packageName = require('../package.json').name;
|
||||
installer = require('../lib/installer');
|
||||
} catch (e) {
|
||||
} catch (error) {
|
||||
// Fall back to root context (when run via npx from GitHub)
|
||||
console.log(`Installer context not found (${e.message}), trying root context...`);
|
||||
console.log(`Installer context not found (${error.message}), trying root context...`);
|
||||
try {
|
||||
version = require('../../../package.json').version;
|
||||
installer = require('../../../tools/installer/lib/installer');
|
||||
} catch (e2) {
|
||||
console.error('Error: Could not load required modules. Please ensure you are running from the correct directory.');
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'Error: Could not load required modules. Please ensure you are running from the correct directory.',
|
||||
);
|
||||
console.error('Debug info:', {
|
||||
__dirname,
|
||||
cwd: process.cwd(),
|
||||
error: e2.message
|
||||
error: error.message,
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -45,8 +47,14 @@ program
|
||||
.option('-f, --full', 'Install complete BMad Method')
|
||||
.option('-x, --expansion-only', 'Install only expansion packs (no bmad-core)')
|
||||
.option('-d, --directory <path>', 'Installation directory')
|
||||
.option('-i, --ide <ide...>', 'Configure for specific IDE(s) - can specify multiple (cursor, claude-code, windsurf, trae, roo, kilo, cline, gemini, qwen-code, github-copilot, crush, other)')
|
||||
.option('-e, --expansion-packs <packs...>', 'Install specific expansion packs (can specify multiple)')
|
||||
.option(
|
||||
'-i, --ide <ide...>',
|
||||
'Configure for specific IDE(s) - can specify multiple (cursor, claude-code, windsurf, trae, roo, kilo, cline, gemini, qwen-code, github-copilot, other)',
|
||||
)
|
||||
.option(
|
||||
'-e, --expansion-packs <packs...>',
|
||||
'Install specific expansion packs (can specify multiple)',
|
||||
)
|
||||
.action(async (options) => {
|
||||
try {
|
||||
if (!options.full && !options.expansionOnly) {
|
||||
@@ -64,8 +72,8 @@ program
|
||||
const config = {
|
||||
installType,
|
||||
directory: options.directory || '.',
|
||||
ides: (options.ide || []).filter(ide => ide !== 'other'),
|
||||
expansionPacks: options.expansionPacks || []
|
||||
ides: (options.ide || []).filter((ide) => ide !== 'other'),
|
||||
expansionPacks: options.expansionPacks || [],
|
||||
};
|
||||
await installer.install(config);
|
||||
process.exit(0);
|
||||
@@ -96,28 +104,30 @@ program
|
||||
.description('Check for BMad Update')
|
||||
.action(async () => {
|
||||
console.log('Checking for updates...');
|
||||
|
||||
|
||||
// Make HTTP request to npm registry for latest version info
|
||||
const req = https.get(`https://registry.npmjs.org/${packageName}/latest`, res => {
|
||||
const req = https.get(`https://registry.npmjs.org/${packageName}/latest`, (res) => {
|
||||
// Check for HTTP errors (non-200 status codes)
|
||||
if (res.statusCode !== 200) {
|
||||
console.error(chalk.red(`Update check failed: Received status code ${res.statusCode}`));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Accumulate response data chunks
|
||||
let data = '';
|
||||
res.on('data', chunk => data += chunk);
|
||||
|
||||
res.on('data', (chunk) => (data += chunk));
|
||||
|
||||
// Process complete response
|
||||
res.on('end', () => {
|
||||
try {
|
||||
// Parse npm registry response and extract version
|
||||
const latest = JSON.parse(data).version;
|
||||
|
||||
|
||||
// Compare versions using semver
|
||||
if (semver.gt(latest, version)) {
|
||||
console.log(chalk.bold.blue(`⚠️ ${packageName} update available: ${version} → ${latest}`));
|
||||
console.log(
|
||||
chalk.bold.blue(`⚠️ ${packageName} update available: ${version} → ${latest}`),
|
||||
);
|
||||
console.log(chalk.bold.blue('\nInstall latest by running:'));
|
||||
console.log(chalk.bold.magenta(` npm install ${packageName}@latest`));
|
||||
console.log(chalk.dim(' or'));
|
||||
@@ -131,14 +141,14 @@ program
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Handle network/connection errors
|
||||
req.on('error', error => {
|
||||
req.on('error', (error) => {
|
||||
console.error(chalk.red('Update check failed:'), error.message);
|
||||
});
|
||||
|
||||
|
||||
// Set 30 second timeout to prevent hanging
|
||||
req.setTimeout(30000, () => {
|
||||
req.setTimeout(30_000, () => {
|
||||
req.destroy();
|
||||
console.error(chalk.red('Update check timed out'));
|
||||
});
|
||||
@@ -183,16 +193,17 @@ program
|
||||
});
|
||||
|
||||
async function promptInstallation() {
|
||||
|
||||
// Display ASCII logo
|
||||
console.log(chalk.bold.cyan(`
|
||||
██████╗ ███╗ ███╗ █████╗ ██████╗ ███╗ ███╗███████╗████████╗██╗ ██╗ ██████╗ ██████╗
|
||||
console.log(
|
||||
chalk.bold.cyan(`
|
||||
██████╗ ███╗ ███╗ █████╗ ██████╗ ███╗ ███╗███████╗████████╗██╗ ██╗ ██████╗ ██████╗
|
||||
██╔══██╗████╗ ████║██╔══██╗██╔══██╗ ████╗ ████║██╔════╝╚══██╔══╝██║ ██║██╔═══██╗██╔══██╗
|
||||
██████╔╝██╔████╔██║███████║██║ ██║█████╗██╔████╔██║█████╗ ██║ ███████║██║ ██║██║ ██║
|
||||
██╔══██╗██║╚██╔╝██║██╔══██║██║ ██║╚════╝██║╚██╔╝██║██╔══╝ ██║ ██╔══██║██║ ██║██║ ██║
|
||||
██████╔╝██║ ╚═╝ ██║██║ ██║██████╔╝ ██║ ╚═╝ ██║███████╗ ██║ ██║ ██║╚██████╔╝██████╔╝
|
||||
╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝
|
||||
`));
|
||||
╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝
|
||||
`),
|
||||
);
|
||||
|
||||
console.log(chalk.bold.magenta('🚀 Universal AI Agent Framework for Any Domain'));
|
||||
console.log(chalk.bold.blue(`✨ Installer v${version}\n`));
|
||||
@@ -210,8 +221,8 @@ async function promptInstallation() {
|
||||
return 'Please enter a valid project path';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]);
|
||||
answers.directory = directory;
|
||||
|
||||
@@ -238,9 +249,10 @@ async function promptInstallation() {
|
||||
if (state.type === 'v4_existing') {
|
||||
const currentVersion = state.manifest?.version || 'unknown';
|
||||
const newVersion = version; // Always use package.json version
|
||||
const versionInfo = currentVersion === newVersion
|
||||
? `(v${currentVersion} - reinstall)`
|
||||
: `(v${currentVersion} → v${newVersion})`;
|
||||
const versionInfo =
|
||||
currentVersion === newVersion
|
||||
? `(v${currentVersion} - reinstall)`
|
||||
: `(v${currentVersion} → v${newVersion})`;
|
||||
bmadOptionText = `Update ${coreShortTitle} ${versionInfo} .bmad-core`;
|
||||
} else {
|
||||
bmadOptionText = `${coreShortTitle} (v${version}) .bmad-core`;
|
||||
@@ -249,7 +261,7 @@ async function promptInstallation() {
|
||||
choices.push({
|
||||
name: bmadOptionText,
|
||||
value: 'bmad-core',
|
||||
checked: true
|
||||
checked: true,
|
||||
});
|
||||
|
||||
// Add expansion pack options
|
||||
@@ -260,9 +272,10 @@ async function promptInstallation() {
|
||||
if (existing) {
|
||||
const currentVersion = existing.manifest?.version || 'unknown';
|
||||
const newVersion = pack.version;
|
||||
const versionInfo = currentVersion === newVersion
|
||||
? `(v${currentVersion} - reinstall)`
|
||||
: `(v${currentVersion} → v${newVersion})`;
|
||||
const versionInfo =
|
||||
currentVersion === newVersion
|
||||
? `(v${currentVersion} - reinstall)`
|
||||
: `(v${currentVersion} → v${newVersion})`;
|
||||
packOptionText = `Update ${pack.shortTitle} ${versionInfo} .${pack.id}`;
|
||||
} else {
|
||||
packOptionText = `${pack.shortTitle} (v${pack.version}) .${pack.id}`;
|
||||
@@ -271,7 +284,7 @@ async function promptInstallation() {
|
||||
choices.push({
|
||||
name: packOptionText,
|
||||
value: pack.id,
|
||||
checked: false
|
||||
checked: false,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -287,13 +300,13 @@ async function promptInstallation() {
|
||||
return 'Please select at least one item to install';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
// Process selections
|
||||
answers.installType = selectedItems.includes('bmad-core') ? 'full' : 'expansion-only';
|
||||
answers.expansionPacks = selectedItems.filter(item => item !== 'bmad-core');
|
||||
answers.expansionPacks = selectedItems.filter((item) => item !== 'bmad-core');
|
||||
|
||||
// Ask sharding questions if installing BMad core
|
||||
if (selectedItems.includes('bmad-core')) {
|
||||
@@ -306,8 +319,8 @@ async function promptInstallation() {
|
||||
type: 'confirm',
|
||||
name: 'prdSharded',
|
||||
message: 'Will the PRD (Product Requirements Document) be sharded into multiple files?',
|
||||
default: true
|
||||
}
|
||||
default: true,
|
||||
},
|
||||
]);
|
||||
answers.prdSharded = prdSharded;
|
||||
|
||||
@@ -317,18 +330,30 @@ async function promptInstallation() {
|
||||
type: 'confirm',
|
||||
name: 'architectureSharded',
|
||||
message: 'Will the architecture documentation be sharded into multiple files?',
|
||||
default: true
|
||||
}
|
||||
default: true,
|
||||
},
|
||||
]);
|
||||
answers.architectureSharded = architectureSharded;
|
||||
|
||||
// Show warning if architecture sharding is disabled
|
||||
if (!architectureSharded) {
|
||||
console.log(chalk.yellow.bold('\n⚠️ IMPORTANT: Architecture Sharding Disabled'));
|
||||
console.log(chalk.yellow('With architecture sharding disabled, you should still create the files listed'));
|
||||
console.log(chalk.yellow('in devLoadAlwaysFiles (like coding-standards.md, tech-stack.md, source-tree.md)'));
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'With architecture sharding disabled, you should still create the files listed',
|
||||
),
|
||||
);
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'in devLoadAlwaysFiles (like coding-standards.md, tech-stack.md, source-tree.md)',
|
||||
),
|
||||
);
|
||||
console.log(chalk.yellow('as these are used by the dev agent at runtime.'));
|
||||
console.log(chalk.yellow('\nAlternatively, you can remove these files from the devLoadAlwaysFiles list'));
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'\nAlternatively, you can remove these files from the devLoadAlwaysFiles list',
|
||||
),
|
||||
);
|
||||
console.log(chalk.yellow('in your core-config.yaml after installation.'));
|
||||
|
||||
const { acknowledge } = await inquirer.prompt([
|
||||
@@ -336,8 +361,8 @@ async function promptInstallation() {
|
||||
type: 'confirm',
|
||||
name: 'acknowledge',
|
||||
message: 'Do you acknowledge this requirement and want to proceed?',
|
||||
default: false
|
||||
}
|
||||
default: false,
|
||||
},
|
||||
]);
|
||||
|
||||
if (!acknowledge) {
|
||||
@@ -353,7 +378,11 @@ async function promptInstallation() {
|
||||
|
||||
while (!ideSelectionComplete) {
|
||||
console.log(chalk.cyan('\n🛠️ IDE Configuration'));
|
||||
console.log(chalk.bold.yellow.bgRed(' ⚠️ IMPORTANT: This is a MULTISELECT! Use SPACEBAR to toggle each IDE! '));
|
||||
console.log(
|
||||
chalk.bold.yellow.bgRed(
|
||||
' ⚠️ IMPORTANT: This is a MULTISELECT! Use SPACEBAR to toggle each IDE! ',
|
||||
),
|
||||
);
|
||||
console.log(chalk.bold.magenta('🔸 Use arrow keys to navigate'));
|
||||
console.log(chalk.bold.magenta('🔸 Use SPACEBAR to select/deselect IDEs'));
|
||||
console.log(chalk.bold.magenta('🔸 Press ENTER when finished selecting\n'));
|
||||
@@ -362,7 +391,8 @@ async function promptInstallation() {
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'ides',
|
||||
message: 'Which IDE(s) do you want to configure? (Select with SPACEBAR, confirm with ENTER):',
|
||||
message:
|
||||
'Which IDE(s) do you want to configure? (Select with SPACEBAR, confirm with ENTER):',
|
||||
choices: [
|
||||
{ name: 'Cursor', value: 'cursor' },
|
||||
{ name: 'Claude Code', value: 'claude-code' },
|
||||
@@ -374,9 +404,9 @@ async function promptInstallation() {
|
||||
{ name: 'Gemini CLI', value: 'gemini' },
|
||||
{ name: 'Qwen Code', value: 'qwen-code' },
|
||||
{ name: 'Crush', value: 'crush' },
|
||||
{ name: 'Github Copilot', value: 'github-copilot' }
|
||||
]
|
||||
}
|
||||
{ name: 'Github Copilot', value: 'github-copilot' },
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
ides = ideResponse.ides;
|
||||
@@ -387,13 +417,19 @@ async function promptInstallation() {
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirmNoIde',
|
||||
message: chalk.red('⚠️ You have NOT selected any IDEs. This means NO IDE integration will be set up. Is this correct?'),
|
||||
default: false
|
||||
}
|
||||
message: chalk.red(
|
||||
'⚠️ You have NOT selected any IDEs. This means NO IDE integration will be set up. Is this correct?',
|
||||
),
|
||||
default: false,
|
||||
},
|
||||
]);
|
||||
|
||||
if (!confirmNoIde) {
|
||||
console.log(chalk.bold.red('\n🔄 Returning to IDE selection. Remember to use SPACEBAR to select IDEs!\n'));
|
||||
console.log(
|
||||
chalk.bold.red(
|
||||
'\n🔄 Returning to IDE selection. Remember to use SPACEBAR to select IDEs!\n',
|
||||
),
|
||||
);
|
||||
continue; // Go back to IDE selection only
|
||||
}
|
||||
}
|
||||
@@ -407,7 +443,9 @@ async function promptInstallation() {
|
||||
// Configure GitHub Copilot immediately if selected
|
||||
if (ides.includes('github-copilot')) {
|
||||
console.log(chalk.cyan('\n🔧 GitHub Copilot Configuration'));
|
||||
console.log(chalk.dim('BMad works best with specific VS Code settings for optimal agent experience.\n'));
|
||||
console.log(
|
||||
chalk.dim('BMad works best with specific VS Code settings for optimal agent experience.\n'),
|
||||
);
|
||||
|
||||
const { configChoice } = await inquirer.prompt([
|
||||
{
|
||||
@@ -417,19 +455,19 @@ async function promptInstallation() {
|
||||
choices: [
|
||||
{
|
||||
name: 'Use recommended defaults (fastest setup)',
|
||||
value: 'defaults'
|
||||
value: 'defaults',
|
||||
},
|
||||
{
|
||||
name: 'Configure each setting manually (customize to your preferences)',
|
||||
value: 'manual'
|
||||
value: 'manual',
|
||||
},
|
||||
{
|
||||
name: 'Skip settings configuration (I\'ll configure manually later)',
|
||||
value: 'skip'
|
||||
}
|
||||
name: "Skip settings configuration (I'll configure manually later)",
|
||||
value: 'skip',
|
||||
},
|
||||
],
|
||||
default: 'defaults'
|
||||
}
|
||||
default: 'defaults',
|
||||
},
|
||||
]);
|
||||
|
||||
answers.githubCopilotConfig = { configChoice };
|
||||
@@ -440,14 +478,17 @@ async function promptInstallation() {
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'includeWebBundles',
|
||||
message: 'Would you like to include pre-built web bundles? (standalone files for ChatGPT, Claude, Gemini)',
|
||||
default: false
|
||||
}
|
||||
message:
|
||||
'Would you like to include pre-built web bundles? (standalone files for ChatGPT, Claude, Gemini)',
|
||||
default: false,
|
||||
},
|
||||
]);
|
||||
|
||||
if (includeWebBundles) {
|
||||
console.log(chalk.cyan('\n📦 Web bundles are standalone files perfect for web AI platforms.'));
|
||||
console.log(chalk.dim(' You can choose different teams/agents than your IDE installation.\n'));
|
||||
console.log(
|
||||
chalk.dim(' You can choose different teams/agents than your IDE installation.\n'),
|
||||
);
|
||||
|
||||
const { webBundleType } = await inquirer.prompt([
|
||||
{
|
||||
@@ -457,22 +498,22 @@ async function promptInstallation() {
|
||||
choices: [
|
||||
{
|
||||
name: 'All available bundles (agents, teams, expansion packs)',
|
||||
value: 'all'
|
||||
value: 'all',
|
||||
},
|
||||
{
|
||||
name: 'Specific teams only',
|
||||
value: 'teams'
|
||||
value: 'teams',
|
||||
},
|
||||
{
|
||||
name: 'Individual agents only',
|
||||
value: 'agents'
|
||||
value: 'agents',
|
||||
},
|
||||
{
|
||||
name: 'Custom selection',
|
||||
value: 'custom'
|
||||
}
|
||||
]
|
||||
}
|
||||
value: 'custom',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
answers.webBundleType = webBundleType;
|
||||
@@ -485,18 +526,18 @@ async function promptInstallation() {
|
||||
type: 'checkbox',
|
||||
name: 'selectedTeams',
|
||||
message: 'Select team bundles to include:',
|
||||
choices: teams.map(t => ({
|
||||
choices: teams.map((t) => ({
|
||||
name: `${t.icon || '📋'} ${t.name}: ${t.description}`,
|
||||
value: t.id,
|
||||
checked: webBundleType === 'teams' // Check all if teams-only mode
|
||||
checked: webBundleType === 'teams', // Check all if teams-only mode
|
||||
})),
|
||||
validate: (answer) => {
|
||||
if (answer.length < 1) {
|
||||
if (answer.length === 0) {
|
||||
return 'You must select at least one team.';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]);
|
||||
answers.selectedWebBundleTeams = selectedTeams;
|
||||
}
|
||||
@@ -508,8 +549,8 @@ async function promptInstallation() {
|
||||
type: 'confirm',
|
||||
name: 'includeIndividualAgents',
|
||||
message: 'Also include individual agent bundles?',
|
||||
default: true
|
||||
}
|
||||
default: true,
|
||||
},
|
||||
]);
|
||||
answers.includeIndividualAgents = includeIndividualAgents;
|
||||
}
|
||||
@@ -525,8 +566,8 @@ async function promptInstallation() {
|
||||
return 'Please enter a valid directory path';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]);
|
||||
answers.webBundlesDirectory = webBundlesDirectory;
|
||||
}
|
||||
@@ -539,6 +580,6 @@ async function promptInstallation() {
|
||||
program.parse(process.argv);
|
||||
|
||||
// Show help if no command provided
|
||||
if (!process.argv.slice(2).length) {
|
||||
if (process.argv.slice(2).length === 0) {
|
||||
program.outputHelp();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user