Compare commits
3 Commits
25f62c74a1
...
536b845a09
| Author | SHA1 | Date |
|---|---|---|
|
|
536b845a09 | |
|
|
db0c06f9dc | |
|
|
48b3bb1709 |
|
|
@ -36,6 +36,25 @@ class ConfigDrivenIdeSetup extends BaseIdeSetup {
|
|||
async setup(projectDir, bmadDir, options = {}) {
|
||||
if (!options.silent) await prompts.log.info(`Setting up ${this.name}...`);
|
||||
|
||||
// Check for BMAD files in ancestor directories that would cause duplicates
|
||||
if (this.installerConfig?.ancestor_conflict_check) {
|
||||
const conflict = await this.findAncestorConflict(projectDir);
|
||||
if (conflict) {
|
||||
await prompts.log.error(
|
||||
`Found existing BMAD commands in ancestor directory: ${conflict}\n` +
|
||||
` ${this.name} inherits commands from parent directories, so this would cause duplicates.\n` +
|
||||
` Please remove the BMAD files from that directory first:\n` +
|
||||
` rm "${conflict}/"bmad*`,
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
reason: 'ancestor-conflict',
|
||||
error: `Ancestor conflict: ${conflict}`,
|
||||
conflictDir: conflict,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up any old BMAD installation first
|
||||
await this.cleanup(projectDir, options);
|
||||
|
||||
|
|
@ -531,6 +550,40 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
|
|||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Check ancestor directories for existing BMAD files in the same target_dir.
|
||||
* IDEs like Claude Code inherit commands from parent directories, so an existing
|
||||
* installation in an ancestor would cause duplicate commands.
|
||||
* @param {string} projectDir - Project directory being installed to
|
||||
* @returns {Promise<string|null>} Path to conflicting directory, or null if clean
|
||||
*/
|
||||
async findAncestorConflict(projectDir) {
|
||||
const targetDir = this.installerConfig?.target_dir;
|
||||
if (!targetDir) return null;
|
||||
|
||||
const resolvedProject = path.resolve(projectDir);
|
||||
let current = path.dirname(resolvedProject);
|
||||
const root = path.parse(current).root;
|
||||
|
||||
while (current !== root && current.length > root.length) {
|
||||
const candidatePath = path.join(current, targetDir);
|
||||
try {
|
||||
if (await fs.pathExists(candidatePath)) {
|
||||
const entries = await fs.readdir(candidatePath);
|
||||
const hasBmad = entries.some((e) => typeof e === 'string' && e.startsWith('bmad'));
|
||||
if (hasBmad) {
|
||||
return candidatePath;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Can't read directory — skip
|
||||
}
|
||||
current = path.dirname(current);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively remove empty directories walking up from dir toward projectDir
|
||||
* Stops at projectDir boundary — never removes projectDir itself
|
||||
|
|
|
|||
|
|
@ -206,7 +206,9 @@ class IdeManager {
|
|||
if (handlerResult.tools > 0) parts.push(`${handlerResult.tools} tools`);
|
||||
detail = parts.join(', ');
|
||||
}
|
||||
return { success: true, ide: ideName, detail, handlerResult };
|
||||
// Propagate handler's success status (default true for backward compat)
|
||||
const success = handlerResult?.success !== false;
|
||||
return { success, ide: ideName, detail, error: handlerResult?.error, handlerResult };
|
||||
} catch (error) {
|
||||
await prompts.log.error(`Failed to setup ${ideName}: ${error.message}`);
|
||||
return { success: false, ide: ideName, error: error.message };
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ platforms:
|
|||
installer:
|
||||
target_dir: .claude/commands
|
||||
template_type: default
|
||||
ancestor_conflict_check: true
|
||||
|
||||
cline:
|
||||
name: "Cline"
|
||||
|
|
|
|||
Loading…
Reference in New Issue