fix(cli): validate init flags before creating the project directory (#1207)

Co-authored-by: Carlos Alcaraz <193642530+calcarazgre646@users.noreply.github.com>
This commit is contained in:
Carlos Alcaraz Gregor
2026-06-16 23:57:05 -07:00
committed by GitHub
co-authored by Carlos Alcaraz
parent 8306b2c0b4
commit a4ae3c92a0
2 changed files with 66 additions and 17 deletions
+45
View File
@@ -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 });
}
+21 -17
View File
@@ -706,24 +706,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;
@@ -734,12 +743,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)}`);