(
skillsDir: string,
clientName: string,
skillType: SkillType,
opts: { force: boolean; removeConflict: boolean },
)
| 126 | } |
| 127 | |
| 128 | async function installSkill( |
| 129 | skillsDir: string, |
| 130 | clientName: string, |
| 131 | skillType: SkillType, |
| 132 | opts: { force: boolean; removeConflict: boolean }, |
| 133 | ): Promise<InstallResult> { |
| 134 | const targetDir = path.join(skillsDir, skillDirName(skillType)); |
| 135 | const altDir = path.join(skillsDir, altSkillDirName(skillType)); |
| 136 | const targetFile = path.join(targetDir, 'SKILL.md'); |
| 137 | const content = readSkillContent(skillType); |
| 138 | |
| 139 | if (fs.existsSync(altDir)) { |
| 140 | if (opts.removeConflict) { |
| 141 | fs.rmSync(altDir, { recursive: true, force: true }); |
| 142 | } else { |
| 143 | const altType = skillType === 'mcp' ? 'cli' : 'mcp'; |
| 144 | if (!isInteractiveTTY()) { |
| 145 | throw new Error( |
| 146 | `Installing ${skillDisplayName(skillType)} but conflicting ${altType} skill found in ${skillsDir}. ` + |
| 147 | `Use --remove-conflict to auto-remove it, or uninstall the ${altType} skill first.`, |
| 148 | ); |
| 149 | } |
| 150 | |
| 151 | const confirmed = await promptConfirm( |
| 152 | `Installing ${skillDisplayName(skillType)} but a conflicting ${altType} skill exists in ${skillsDir}. Remove it?`, |
| 153 | ); |
| 154 | if (!confirmed) { |
| 155 | throw new Error('Installation cancelled due to conflicting skill.'); |
| 156 | } |
| 157 | fs.rmSync(altDir, { recursive: true, force: true }); |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | if (fs.existsSync(targetFile) && !opts.force) { |
| 162 | if (!isInteractiveTTY()) { |
| 163 | throw new Error(`Skill already installed at ${targetFile}. Use --force to overwrite.`); |
| 164 | } |
| 165 | |
| 166 | const confirmed = await promptConfirm(`Skill already installed at ${targetFile}. Overwrite?`); |
| 167 | if (!confirmed) { |
| 168 | throw new Error('Installation cancelled.'); |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | fs.mkdirSync(targetDir, { recursive: true }); |
| 173 | fs.writeFileSync(targetFile, content, 'utf8'); |
| 174 | |
| 175 | return { client: clientName, location: targetFile }; |
| 176 | } |
| 177 | |
| 178 | function uninstallSkill( |
| 179 | skillsDir: string, |
no test coverage detected