refactor: convert claude-code and cline to flat structure with shared methods
This commit is contained in:
parent
bdacdad3a8
commit
7a390a3a08
|
|
@ -84,14 +84,15 @@ class ClaudeCodeSetup extends BaseIdeSetup {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleanup old BMAD installation before reinstalling
|
* Cleanup old BMAD installation before reinstalling
|
||||||
|
* Removes files and directories starting with 'bmad-' or named 'bmad' from commands dir
|
||||||
* @param {string} projectDir - Project directory
|
* @param {string} projectDir - Project directory
|
||||||
*/
|
*/
|
||||||
async cleanup(projectDir) {
|
async cleanup(projectDir) {
|
||||||
const bmadCommandsDir = path.join(projectDir, this.configDir, this.commandsDir, 'bmad');
|
const commandsDir = path.join(projectDir, this.configDir, this.commandsDir);
|
||||||
|
const removedCount = await this.clearBmadPrefixedFiles(commandsDir);
|
||||||
|
|
||||||
if (await fs.pathExists(bmadCommandsDir)) {
|
if (removedCount > 0) {
|
||||||
await fs.remove(bmadCommandsDir);
|
console.log(chalk.dim(` Removed ${removedCount} old BMAD items from ${this.name}`));
|
||||||
console.log(chalk.dim(` Removed old BMAD commands from ${this.name}`));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,28 +114,15 @@ class ClaudeCodeSetup extends BaseIdeSetup {
|
||||||
// Create .claude/commands directory structure
|
// Create .claude/commands directory structure
|
||||||
const claudeDir = path.join(projectDir, this.configDir);
|
const claudeDir = path.join(projectDir, this.configDir);
|
||||||
const commandsDir = path.join(claudeDir, this.commandsDir);
|
const commandsDir = path.join(claudeDir, this.commandsDir);
|
||||||
const bmadCommandsDir = path.join(commandsDir, 'bmad');
|
|
||||||
|
|
||||||
await this.ensureDir(bmadCommandsDir);
|
await this.ensureDir(commandsDir);
|
||||||
|
|
||||||
// Generate agent launchers using AgentCommandGenerator
|
// Generate agent launchers using AgentCommandGenerator
|
||||||
// This creates small launcher files that reference the actual agents in _bmad/
|
|
||||||
const agentGen = new AgentCommandGenerator(this.bmadFolderName);
|
const agentGen = new AgentCommandGenerator(this.bmadFolderName);
|
||||||
const { artifacts: agentArtifacts, counts: agentCounts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []);
|
const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []);
|
||||||
|
|
||||||
// Create directories for each module
|
// Write agent launcher files with flattened naming directly to commands dir
|
||||||
const modules = new Set();
|
const agentCount = await this.writeFlattenedArtifacts(agentArtifacts, commandsDir);
|
||||||
for (const artifact of agentArtifacts) {
|
|
||||||
modules.add(artifact.module);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const module of modules) {
|
|
||||||
await this.ensureDir(path.join(bmadCommandsDir, module));
|
|
||||||
await this.ensureDir(path.join(bmadCommandsDir, module, 'agents'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write agent launcher files
|
|
||||||
const agentCount = await agentGen.writeAgentLaunchers(bmadCommandsDir, agentArtifacts);
|
|
||||||
|
|
||||||
// Process Claude Code specific injections for installed modules
|
// Process Claude Code specific injections for installed modules
|
||||||
// Use pre-collected configuration if available, or skip if already configured
|
// Use pre-collected configuration if available, or skip if already configured
|
||||||
|
|
@ -148,25 +136,13 @@ class ClaudeCodeSetup extends BaseIdeSetup {
|
||||||
await this.processModuleInjections(projectDir, bmadDir, options);
|
await this.processModuleInjections(projectDir, bmadDir, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip CLAUDE.md creation - let user manage their own CLAUDE.md file
|
|
||||||
// await this.createClaudeConfig(projectDir, modules);
|
|
||||||
|
|
||||||
// Generate workflow commands from manifest (if it exists)
|
// Generate workflow commands from manifest (if it exists)
|
||||||
const workflowGen = new WorkflowCommandGenerator(this.bmadFolderName);
|
const workflowGen = new WorkflowCommandGenerator(this.bmadFolderName);
|
||||||
const { artifacts: workflowArtifacts } = await workflowGen.collectWorkflowArtifacts(bmadDir);
|
const { artifacts: workflowArtifacts } = await workflowGen.collectWorkflowArtifacts(bmadDir);
|
||||||
|
|
||||||
// Write only workflow-command artifacts, skip workflow-launcher READMEs
|
// Filter to workflow-command type and write with flattened naming
|
||||||
let workflowCommandCount = 0;
|
const workflowCommands = workflowArtifacts.filter((a) => a.type === 'workflow-command');
|
||||||
for (const artifact of workflowArtifacts) {
|
const workflowCommandCount = await this.writeFlattenedArtifacts(workflowCommands, commandsDir);
|
||||||
if (artifact.type === 'workflow-command') {
|
|
||||||
const moduleWorkflowsDir = path.join(bmadCommandsDir, artifact.module, 'workflows');
|
|
||||||
await this.ensureDir(moduleWorkflowsDir);
|
|
||||||
const commandPath = path.join(moduleWorkflowsDir, path.basename(artifact.relativePath));
|
|
||||||
await this.writeFile(commandPath, artifact.content);
|
|
||||||
workflowCommandCount++;
|
|
||||||
}
|
|
||||||
// Skip workflow-launcher READMEs as they would be treated as slash commands
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate task and tool commands from manifests (if they exist)
|
// Generate task and tool commands from manifests (if they exist)
|
||||||
const taskToolGen = new TaskToolCommandGenerator();
|
const taskToolGen = new TaskToolCommandGenerator();
|
||||||
|
|
@ -184,7 +160,7 @@ class ClaudeCodeSetup extends BaseIdeSetup {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
console.log(chalk.dim(` - Commands directory: ${path.relative(projectDir, bmadCommandsDir)}`));
|
console.log(chalk.dim(` - Commands directory: ${path.relative(projectDir, commandsDir)}`));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
|
@ -471,13 +447,13 @@ class ClaudeCodeSetup extends BaseIdeSetup {
|
||||||
* @returns {Object|null} Info about created command
|
* @returns {Object|null} Info about created command
|
||||||
*/
|
*/
|
||||||
async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) {
|
async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) {
|
||||||
const customAgentsDir = path.join(projectDir, this.configDir, this.commandsDir, 'bmad', 'custom', 'agents');
|
const commandsDir = path.join(projectDir, this.configDir, this.commandsDir);
|
||||||
|
|
||||||
if (!(await this.exists(path.join(projectDir, this.configDir)))) {
|
if (!(await this.exists(path.join(projectDir, this.configDir)))) {
|
||||||
return null; // IDE not configured for this project
|
return null; // IDE not configured for this project
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.ensureDir(customAgentsDir);
|
await this.ensureDir(commandsDir);
|
||||||
|
|
||||||
const launcherContent = `---
|
const launcherContent = `---
|
||||||
name: '${agentName}'
|
name: '${agentName}'
|
||||||
|
|
@ -496,12 +472,13 @@ You must fully embody this agent's persona and follow all activation instruction
|
||||||
</agent-activation>
|
</agent-activation>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const launcherPath = path.join(customAgentsDir, `${agentName}.md`);
|
const fileName = `bmad-custom-agents-${agentName}.md`;
|
||||||
|
const launcherPath = path.join(commandsDir, fileName);
|
||||||
await this.writeFile(launcherPath, launcherContent);
|
await this.writeFile(launcherPath, launcherContent);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
path: launcherPath,
|
path: launcherPath,
|
||||||
command: `/bmad:custom:agents:${agentName}`,
|
command: `/bmad-custom-agents-${agentName}`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,13 +33,13 @@ class ClineSetup extends BaseIdeSetup {
|
||||||
await this.ensureDir(workflowsDir);
|
await this.ensureDir(workflowsDir);
|
||||||
|
|
||||||
// Clear old BMAD files
|
// Clear old BMAD files
|
||||||
await this.clearOldBmadFiles(workflowsDir);
|
await this.clearBmadPrefixedFiles(workflowsDir);
|
||||||
|
|
||||||
// Collect all artifacts
|
// Collect all artifacts
|
||||||
const { artifacts, counts } = await this.collectClineArtifacts(projectDir, bmadDir, options);
|
const { artifacts, counts } = await this.collectClineArtifacts(projectDir, bmadDir, options);
|
||||||
|
|
||||||
// Write flattened files
|
// Write flattened files
|
||||||
const written = await this.flattenAndWriteArtifacts(artifacts, workflowsDir);
|
const written = await this.writeFlattenedArtifacts(artifacts, workflowsDir);
|
||||||
|
|
||||||
console.log(chalk.green(`✓ ${this.name} configured:`));
|
console.log(chalk.green(`✓ ${this.name} configured:`));
|
||||||
console.log(chalk.dim(` - ${counts.agents} agents installed`));
|
console.log(chalk.dim(` - ${counts.agents} agents installed`));
|
||||||
|
|
@ -143,54 +143,7 @@ class ClineSetup extends BaseIdeSetup {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Uses inherited flattenFilename(), writeFlattenedArtifacts(), and clearBmadPrefixedFiles() from BaseIdeSetup
|
||||||
* Flatten file path to bmad-module-type-name.md format
|
|
||||||
*/
|
|
||||||
flattenFilename(relativePath) {
|
|
||||||
const sanitized = relativePath.replaceAll(/[\\/]/g, '-');
|
|
||||||
return `bmad-${sanitized}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write all artifacts with flattened names
|
|
||||||
*/
|
|
||||||
async flattenAndWriteArtifacts(artifacts, destDir) {
|
|
||||||
let written = 0;
|
|
||||||
|
|
||||||
for (const artifact of artifacts) {
|
|
||||||
const flattenedName = this.flattenFilename(artifact.relativePath);
|
|
||||||
const targetPath = path.join(destDir, flattenedName);
|
|
||||||
await fs.writeFile(targetPath, artifact.content);
|
|
||||||
written++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return written;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear old BMAD files from the workflows directory
|
|
||||||
*/
|
|
||||||
async clearOldBmadFiles(destDir) {
|
|
||||||
if (!(await fs.pathExists(destDir))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const entries = await fs.readdir(destDir);
|
|
||||||
|
|
||||||
for (const entry of entries) {
|
|
||||||
if (!entry.startsWith('bmad-')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const entryPath = path.join(destDir, entry);
|
|
||||||
const stat = await fs.stat(entryPath);
|
|
||||||
if (stat.isFile()) {
|
|
||||||
await fs.remove(entryPath);
|
|
||||||
} else if (stat.isDirectory()) {
|
|
||||||
await fs.remove(entryPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read and process file with project-specific paths
|
* Read and process file with project-specific paths
|
||||||
|
|
@ -205,8 +158,10 @@ class ClineSetup extends BaseIdeSetup {
|
||||||
*/
|
*/
|
||||||
async cleanup(projectDir) {
|
async cleanup(projectDir) {
|
||||||
const workflowsDir = path.join(projectDir, this.configDir, this.workflowsDir);
|
const workflowsDir = path.join(projectDir, this.configDir, this.workflowsDir);
|
||||||
await this.clearOldBmadFiles(workflowsDir);
|
const removedCount = await this.clearBmadPrefixedFiles(workflowsDir);
|
||||||
console.log(chalk.dim(`Removed ${this.name} BMAD configuration`));
|
if (removedCount > 0) {
|
||||||
|
console.log(chalk.dim(` Removed ${removedCount} old BMAD items from ${this.name}`));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -257,13 +212,6 @@ The agent will follow the persona and instructions from the main agent file.
|
||||||
type: 'custom-agent-launcher',
|
type: 'custom-agent-launcher',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility: Ensure directory exists
|
|
||||||
*/
|
|
||||||
async ensureDir(dirPath) {
|
|
||||||
await fs.ensureDir(dirPath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { ClineSetup };
|
module.exports = { ClineSetup };
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue