fix(cli): implement --force flag to protect existing skill files (#324)

The --force option was accepted by Commander but ignored: writeFile()
always overwrote existing files silently. Users expecting protection
against accidental overwrites had no way to preserve existing configs.

- generatePlatformFiles: check file existence before writing; skip with
  a clear message when file exists and force=false
- generateAllPlatformFiles / templateInstall: propagate force parameter
- initCommand: pass options.force through to templateInstall

Behavior change:
  uipro init --ai claude         → skips if SKILL.md already exists
  uipro init --ai claude --force → overwrites regardless

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Alfredo Augusto Petri 2026-06-21 05:46:42 -03:00 committed by GitHub
parent 71d02eccde
commit c522197c72
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 16 additions and 7 deletions

View File

@ -101,17 +101,18 @@ async function templateInstall(
targetDir: string,
aiType: AIType,
spinner: ReturnType<typeof ora>,
isGlobal = false
isGlobal = false,
force = false
): Promise<string[]> {
spinner.text = isGlobal
? 'Generating skill files globally...'
: 'Generating skill files from templates...';
if (aiType === 'all') {
return generateAllPlatformFiles(targetDir, isGlobal);
return generateAllPlatformFiles(targetDir, isGlobal, force);
}
return generatePlatformFiles(targetDir, aiType, isGlobal);
return generatePlatformFiles(targetDir, aiType, isGlobal, force);
}
export async function initCommand(options: InitOptions): Promise<void> {
@ -178,7 +179,7 @@ export async function initCommand(options: InitOptions): Promise<void> {
}
} else {
// Use new template-based generation (default)
copiedFolders = await templateInstall(cwd, aiType, spinner, isGlobal);
copiedFolders = await templateInstall(cwd, aiType, spinner, isGlobal, options.force);
installMethod = 'template';
}

View File

@ -187,7 +187,8 @@ async function copyDataAndScripts(targetSkillDir: string): Promise<void> {
export async function generatePlatformFiles(
targetDir: string,
aiType: string,
isGlobal = false
isGlobal = false,
force = false
): Promise<string[]> {
const config = await loadPlatformConfig(aiType);
const createdFolders: string[] = [];
@ -208,6 +209,13 @@ export async function generatePlatformFiles(
// Render and write skill file (pass isGlobal to adjust paths)
const skillContent = await renderSkillFile(config, isGlobal);
const skillFilePath = join(skillDir, config.folderStructure.filename);
const fileAlreadyExists = await exists(skillFilePath);
if (fileAlreadyExists && !force) {
console.log(` Skipped (already exists): ${skillFilePath} — use --force to overwrite`);
return [];
}
await writeFile(skillFilePath, skillContent, 'utf-8');
createdFolders.push(config.folderStructure.root);
@ -220,12 +228,12 @@ export async function generatePlatformFiles(
/**
* Generate files for all AI types
*/
export async function generateAllPlatformFiles(targetDir: string, isGlobal = false): Promise<string[]> {
export async function generateAllPlatformFiles(targetDir: string, isGlobal = false, force = false): Promise<string[]> {
const allFolders = new Set<string>();
for (const aiType of Object.keys(AI_TO_PLATFORM)) {
try {
const folders = await generatePlatformFiles(targetDir, aiType, isGlobal);
const folders = await generatePlatformFiles(targetDir, aiType, isGlobal, force);
folders.forEach(f => allFolders.add(f));
} catch {
// Skip if generation fails for a platform