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
|
||||
* Removes files and directories starting with 'bmad-' or named 'bmad' from commands dir
|
||||
* @param {string} projectDir - Project directory
|
||||
*/
|
||||
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)) {
|
||||
await fs.remove(bmadCommandsDir);
|
||||
console.log(chalk.dim(` Removed old BMAD commands from ${this.name}`));
|
||||
if (removedCount > 0) {
|
||||
console.log(chalk.dim(` Removed ${removedCount} old BMAD items from ${this.name}`));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -113,28 +114,15 @@ class ClaudeCodeSetup extends BaseIdeSetup {
|
|||
// Create .claude/commands directory structure
|
||||
const claudeDir = path.join(projectDir, this.configDir);
|
||||
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
|
||||
// This creates small launcher files that reference the actual agents in _bmad/
|
||||
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
|
||||
const modules = new Set();
|
||||
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);
|
||||
// Write agent launcher files with flattened naming directly to commands dir
|
||||
const agentCount = await this.writeFlattenedArtifacts(agentArtifacts, commandsDir);
|
||||
|
||||
// Process Claude Code specific injections for installed modules
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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)
|
||||
const workflowGen = new WorkflowCommandGenerator(this.bmadFolderName);
|
||||
const { artifacts: workflowArtifacts } = await workflowGen.collectWorkflowArtifacts(bmadDir);
|
||||
|
||||
// Write only workflow-command artifacts, skip workflow-launcher READMEs
|
||||
let workflowCommandCount = 0;
|
||||
for (const artifact of workflowArtifacts) {
|
||||
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
|
||||
}
|
||||
// Filter to workflow-command type and write with flattened naming
|
||||
const workflowCommands = workflowArtifacts.filter((a) => a.type === 'workflow-command');
|
||||
const workflowCommandCount = await this.writeFlattenedArtifacts(workflowCommands, commandsDir);
|
||||
|
||||
// Generate task and tool commands from manifests (if they exist)
|
||||
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 {
|
||||
success: true,
|
||||
|
|
@ -471,13 +447,13 @@ class ClaudeCodeSetup extends BaseIdeSetup {
|
|||
* @returns {Object|null} Info about created command
|
||||
*/
|
||||
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)))) {
|
||||
return null; // IDE not configured for this project
|
||||
}
|
||||
|
||||
await this.ensureDir(customAgentsDir);
|
||||
await this.ensureDir(commandsDir);
|
||||
|
||||
const launcherContent = `---
|
||||
name: '${agentName}'
|
||||
|
|
@ -496,12 +472,13 @@ You must fully embody this agent's persona and follow all activation instruction
|
|||
</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);
|
||||
|
||||
return {
|
||||
path: launcherPath,
|
||||
command: `/bmad:custom:agents:${agentName}`,
|
||||
command: `/bmad-custom-agents-${agentName}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,13 +33,13 @@ class ClineSetup extends BaseIdeSetup {
|
|||
await this.ensureDir(workflowsDir);
|
||||
|
||||
// Clear old BMAD files
|
||||
await this.clearOldBmadFiles(workflowsDir);
|
||||
await this.clearBmadPrefixedFiles(workflowsDir);
|
||||
|
||||
// Collect all artifacts
|
||||
const { artifacts, counts } = await this.collectClineArtifacts(projectDir, bmadDir, options);
|
||||
|
||||
// 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.dim(` - ${counts.agents} agents installed`));
|
||||
|
|
@ -143,54 +143,7 @@ class ClineSetup extends 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Uses inherited flattenFilename(), writeFlattenedArtifacts(), and clearBmadPrefixedFiles() from BaseIdeSetup
|
||||
|
||||
/**
|
||||
* Read and process file with project-specific paths
|
||||
|
|
@ -205,8 +158,10 @@ class ClineSetup extends BaseIdeSetup {
|
|||
*/
|
||||
async cleanup(projectDir) {
|
||||
const workflowsDir = path.join(projectDir, this.configDir, this.workflowsDir);
|
||||
await this.clearOldBmadFiles(workflowsDir);
|
||||
console.log(chalk.dim(`Removed ${this.name} BMAD configuration`));
|
||||
const removedCount = await this.clearBmadPrefixedFiles(workflowsDir);
|
||||
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',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility: Ensure directory exists
|
||||
*/
|
||||
async ensureDir(dirPath) {
|
||||
await fs.ensureDir(dirPath);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { ClineSetup };
|
||||
|
|
|
|||
Loading…
Reference in New Issue