From 4b2762e1d8c84ccdcc36005f8ee523b2f2db392e Mon Sep 17 00:00:00 2001 From: Carlos Alcaraz <193642530+calcarazgre646@users.noreply.github.com> Date: Thu, 4 Jun 2026 20:52:46 -0300 Subject: [PATCH] fix(cli): validate init flags before creating the project directory --- packages/cli/src/commands/init.test.ts | 45 ++++++++++++++++++++++++++ packages/cli/src/commands/init.ts | 38 ++++++++++++---------- 2 files changed, 66 insertions(+), 17 deletions(-) diff --git a/packages/cli/src/commands/init.test.ts b/packages/cli/src/commands/init.test.ts index 9e8fda909..6334ea174 100644 --- a/packages/cli/src/commands/init.test.ts +++ b/packages/cli/src/commands/init.test.ts @@ -147,6 +147,51 @@ describe("hyperframes init flag rename", () => { ]); expect(res.status).toBe(1); expect(res.stderr).toContain("Video file not found: missing.mp4"); + expect(existsSync(target)).toBe(false); + } finally { + rmSync(dir, { recursive: true, force: true }); + } + }); + + it("--audio with a missing file fails without creating the project directory", () => { + const dir = mkdtempSync(join(tmpdir(), "hf-init-test-")); + const target = join(dir, "proj"); + try { + const res = runInit([ + target, + "--example", + "blank", + "--non-interactive", + "--skip-skills", + "--audio", + "missing.mp3", + ]); + expect(res.status).toBe(1); + expect(res.stderr).toContain("Audio file not found: missing.mp3"); + expect(existsSync(target)).toBe(false); + } finally { + rmSync(dir, { recursive: true, force: true }); + } + }); + + it("--video and --audio together fail without creating the project directory", () => { + const dir = mkdtempSync(join(tmpdir(), "hf-init-test-")); + const target = join(dir, "proj"); + try { + const res = runInit([ + target, + "--example", + "blank", + "--non-interactive", + "--skip-skills", + "--video", + "clip.mp4", + "--audio", + "track.mp3", + ]); + expect(res.status).toBe(1); + expect(res.stderr).toContain("Cannot use --video and --audio together"); + expect(existsSync(target)).toBe(false); } finally { rmSync(dir, { recursive: true, force: true }); } diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index ef47374dc..437ac0fbc 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -693,24 +693,33 @@ export default defineCommand({ process.exit(1); } + if (videoFlag && audioFlag) { + console.error(c.error("Cannot use --video and --audio together")); + process.exit(1); + } + + // Validate source files before creating destDir so a failed run does + // not leave an empty orphan directory behind. The interactive path + // already validates in this order. + const videoPath = videoFlag ? resolve(videoFlag) : undefined; + if (videoPath && !existsSync(videoPath)) { + console.error(c.error(`Video file not found: ${videoFlag}`)); + process.exit(1); + } + const audioPath = audioFlag ? resolve(audioFlag) : undefined; + if (audioPath && !existsSync(audioPath)) { + console.error(c.error(`Audio file not found: ${audioFlag}`)); + process.exit(1); + } + mkdirSync(destDir, { recursive: true }); let localVideoName: string | undefined; let videoDuration: number | undefined; let sourceFilePath: string | undefined; - if (videoFlag && audioFlag) { - console.error(c.error("Cannot use --video and --audio together")); - process.exit(1); - } - // Handle video - if (videoFlag) { - const videoPath = resolve(videoFlag); - if (!existsSync(videoPath)) { - console.error(c.error(`Video file not found: ${videoFlag}`)); - process.exit(1); - } + if (videoPath) { sourceFilePath = videoPath; const result = await handleVideoFile(videoPath, destDir, false); localVideoName = result.localVideoName; @@ -721,12 +730,7 @@ export default defineCommand({ } // Handle audio - if (audioFlag) { - const audioPath = resolve(audioFlag); - if (!existsSync(audioPath)) { - console.error(c.error(`Audio file not found: ${audioFlag}`)); - process.exit(1); - } + if (audioPath) { sourceFilePath = audioPath; copyFileSync(audioPath, resolve(destDir, basename(audioPath))); console.log(`Audio: ${basename(audioPath)}`);