BMAD-METHOD/workspace-utils/init.js

292 lines
8.1 KiB
JavaScript

#!/usr/bin/env node
/**
* BMAD Workspace Initialization Utility
* Cross-IDE workspace initialization with session management
*/
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
/**
* Detect IDE environment from various sources
*/
function detectIDE() {
// Check environment variables
if (process.env.CURSOR_SOCKET) return 'cursor';
if (process.env.WINDSURF_SESSION) return 'windsurf';
if (process.env.TRAE_MODE) return 'trae';
if (process.env.ROO_CODE) return 'roo';
if (process.env.CLINE_ACTIVE) return 'cline';
if (process.env.GEMINI_AI_STUDIO) return 'gemini';
if (process.env.GITHUB_COPILOT) return 'github-copilot';
if (process.env.VSCODE_PID) return 'vscode';
if (process.env.IDE_TYPE) return process.env.IDE_TYPE;
// Check for IDE-specific files or patterns
if (fs.existsSync('.cursor')) return 'cursor';
if (fs.existsSync('.windsurf')) return 'windsurf';
if (fs.existsSync('.vscode')) return 'vscode';
return 'unknown';
}
/**
* Create workspace directory structure
*/
function createWorkspaceStructure(workspacePath) {
const directories = [
'sessions',
'context',
'handoffs',
'decisions',
'progress',
'quality',
'archive',
'hooks',
'templates',
'logs'
];
directories.forEach(dir => {
const dirPath = path.join(workspacePath, dir);
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
});
}
/**
* Initialize workspace configuration
*/
function initWorkspaceConfig(workspacePath) {
const configPath = path.join(workspacePath, 'workspace-config.json');
if (!fs.existsSync(configPath)) {
const config = {
version: '1.0.0',
created: new Date().toISOString(),
lastUpdated: new Date().toISOString(),
features: {
crossIDESupport: true,
sessionManagement: true,
contextPersistence: true,
agentHandoffs: true,
qualityTracking: true
},
settings: {
maxSessions: 10,
sessionTimeout: 3600000, // 1 hour in milliseconds
autoCleanup: true,
logLevel: 'info'
}
};
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
return config;
}
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
}
/**
* Create session with IDE-specific metadata
*/
function createSession(workspacePath, ide) {
const sessionId = crypto.randomBytes(8).toString('hex');
const timestamp = new Date().toISOString();
const sessionData = {
id: sessionId,
ide: ide,
created: timestamp,
lastHeartbeat: timestamp,
pid: process.pid,
user: process.env.USER || process.env.USERNAME || 'unknown',
cwd: process.cwd(),
nodeVersion: process.version,
platform: process.platform,
arch: process.arch,
metadata: {
ideSpecific: getIDESpecificMetadata(ide),
features: ['context-sharing', 'agent-handoffs', 'quality-tracking']
}
};
const sessionFile = path.join(workspacePath, 'sessions', `${sessionId}.json`);
fs.writeFileSync(sessionFile, JSON.stringify(sessionData, null, 2));
return sessionData;
}
/**
* Get IDE-specific metadata
*/
function getIDESpecificMetadata(ide) {
const metadata = {
supportsTerminalCommands: true,
hasIntegratedGit: false,
supportsPanels: false,
hasExtensionSystem: false
};
switch (ide) {
case 'cursor':
metadata.hasIntegratedGit = true;
metadata.supportsPanels = true;
metadata.hasExtensionSystem = true;
metadata.features = ['custom-rules', 'ai-assistance', 'git-integration'];
break;
case 'windsurf':
metadata.hasIntegratedGit = true;
metadata.supportsPanels = true;
metadata.features = ['ai-agent', 'terminal-integration'];
break;
case 'vscode':
metadata.hasIntegratedGit = true;
metadata.supportsPanels = true;
metadata.hasExtensionSystem = true;
metadata.features = ['extensions', 'integrated-terminal', 'git-integration'];
break;
case 'github-copilot':
metadata.hasIntegratedGit = true;
metadata.hasExtensionSystem = true;
metadata.features = ['ai-assistance', 'code-completion'];
break;
default:
metadata.features = ['basic-terminal'];
}
return metadata;
}
/**
* Create IDE-specific setup hints
*/
function createIDESetupHints(workspacePath, ide) {
const hintsPath = path.join(workspacePath, 'templates', `${ide}-setup.md`);
let setupContent = `# ${ide.toUpperCase()} Workspace Setup\n\n`;
switch (ide) {
case 'cursor':
setupContent += `## Cursor Integration
- Add workspace commands to your terminal
- Use \`npm run workspace-status\` to check collaboration status
- Workspace context is automatically shared between sessions
- Custom rules in .cursor/rules/ will respect workspace state
## Commands
\`\`\`bash
npm run workspace-init # Initialize session
npm run workspace-status # Check status
npm run workspace-cleanup # Maintenance
\`\`\`
`;
break;
case 'windsurf':
setupContent += `## Windsurf Integration
- Workspace utilities available through terminal
- Context sharing works with Windsurf AI agent
- Session state persists across Windsurf restarts
## Commands
\`\`\`bash
npm run workspace-init # Start workspace session
npm run workspace-handoff # Prepare agent handoff
npm run workspace-sync # Sync with latest context
\`\`\`
`;
break;
default:
setupContent += `## ${ide.toUpperCase()} Integration
- Use terminal commands for workspace management
- Full workspace functionality available
- Context persists across IDE sessions
## Available Commands
\`\`\`bash
npm run workspace-init # Initialize workspace session
npm run workspace-status # Show workspace status
npm run workspace-cleanup # Clean and optimize workspace
npm run workspace-handoff # Manage agent handoffs
npm run workspace-sync # Synchronize context
npm run workspace-health # Check workspace health
\`\`\`
`;
}
if (!fs.existsSync(hintsPath)) {
fs.writeFileSync(hintsPath, setupContent);
}
}
/**
* Main initialization function
*/
async function initWorkspace() {
try {
const workspacePath = path.join(process.cwd(), '.workspace');
// Create workspace directory structure
if (!fs.existsSync(workspacePath)) {
fs.mkdirSync(workspacePath, { recursive: true });
}
createWorkspaceStructure(workspacePath);
// Initialize configuration
const config = initWorkspaceConfig(workspacePath);
// Detect IDE and create session
const ide = detectIDE();
const session = createSession(workspacePath, ide);
// Create IDE-specific setup hints
createIDESetupHints(workspacePath, ide);
// Log initialization
const logEntry = {
timestamp: new Date().toISOString(),
action: 'workspace-init',
sessionId: session.id,
ide: ide,
user: session.user
};
const logPath = path.join(workspacePath, 'logs', 'workspace.log');
fs.appendFileSync(logPath, JSON.stringify(logEntry) + '\n');
// Success output
console.log('✅ BMAD Workspace initialized successfully');
console.log('=====================================');
console.log(`📁 Workspace: ${workspacePath}`);
console.log(`📍 Session ID: ${session.id}`);
console.log(`💻 IDE: ${ide}`);
console.log(`👤 User: ${session.user}`);
console.log(`🕐 Created: ${new Date(session.created).toLocaleString()}`);
if (ide !== 'unknown') {
console.log(`\n📖 Setup guide: .workspace/templates/${ide}-setup.md`);
}
console.log('\n🚀 Ready for collaborative development!');
console.log(' • Run `npm run workspace-status` to check status');
console.log(' • Run `npm run workspace-health` for health check');
return session.id;
} catch (error) {
console.error('❌ Failed to initialize workspace:', error.message);
console.error(' Make sure you have proper file permissions');
console.error(' Try running from project root directory');
process.exit(1);
}
}
// Command line execution
if (require.main === module) {
initWorkspace();
}
module.exports = { initWorkspace, detectIDE };