From 29541aefaa63afc57292753ac334fc392cc34fcd Mon Sep 17 00:00:00 2001 From: James Russo Date: Fri, 3 Apr 2026 10:36:08 -0400 Subject: [PATCH] feat(cli): prompt to install skills during init (#206) Replace the static "Tip" message at the end of `hyperframes init` with an interactive prompt that offers to install AI coding skills. When the user accepts, the skills command runs `npx skills add` with `--all` and `stdio: "inherit"` so the native installer output is visible. Co-authored-by: Claude Opus 4.6 (1M context) --- packages/cli/src/commands/init.ts | 17 +++++++++++---- packages/cli/src/commands/skills.ts | 34 +++++++---------------------- 2 files changed, 21 insertions(+), 30 deletions(-) diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index b486f6deb..ccf5b0677 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -692,10 +692,19 @@ export default defineCommand({ const files = readdirSync(destDir); clack.note(files.map((f) => c.accent(f)).join("\n"), c.success(`Created ${name}/`)); - clack.log.message( - `${c.dim("Tip:")} Install AI coding skills: ${c.accent("npx skills add heygen-com/hyperframes")}\n` + - `${c.dim(" Then open this project with")} ${c.accent("Claude Code")}${c.dim(",")} ${c.accent("Cursor")}${c.dim(", or your preferred agent.")}`, - ); + // Offer to install AI coding skills + const installSkills = await clack.confirm({ + message: "Install AI coding skills? (for Claude Code, Cursor, Codex, etc.)", + initialValue: true, + }); + if (clack.isCancel(installSkills)) { + clack.cancel("Setup cancelled."); + process.exit(0); + } + if (installSkills) { + const skillsCmd = await import("./skills.js").then((m) => m.default); + await runCommand(skillsCmd, { rawArgs: [] }); + } // Auto-launch studio preview clack.log.info("Opening studio preview..."); diff --git a/packages/cli/src/commands/skills.ts b/packages/cli/src/commands/skills.ts index 384895fc9..1ea917d46 100644 --- a/packages/cli/src/commands/skills.ts +++ b/packages/cli/src/commands/skills.ts @@ -14,12 +14,13 @@ function hasNpx(): boolean { function runSkillsAdd(repo: string): Promise { return new Promise((resolve, reject) => { - const child = spawn("npx", ["skills", "add", repo, "--all", "-g"], { - stdio: "ignore", + const child = spawn("npx", ["skills", "add", repo, "--all"], { + stdio: "inherit", timeout: 120_000, }); - child.on("close", (code) => { + child.on("close", (code, signal) => { if (code === 0) resolve(); + else if (signal === "SIGINT" || code === 130) process.exit(0); else reject(new Error(`npx skills add exited with code ${code}`)); }); child.on("error", reject); @@ -38,39 +39,20 @@ export default defineCommand({ }, args: {}, async run() { - clack.intro(c.bold("hyperframes skills")); - if (!hasNpx()) { clack.log.error(c.error("npx not found. Install Node.js and retry.")); - clack.outro(c.warn("No skills installed.")); return; } - const installed: string[] = []; - const skipped: string[] = []; - for (const source of SOURCES) { - const spinner = clack.spinner(); - spinner.start(`Installing ${source.name} skills...`); + console.log(); + console.log(c.bold(`Installing ${source.name} skills...`)); + console.log(); try { await runSkillsAdd(source.repo); - installed.push(source.name); - spinner.stop(c.success(`${source.name} skills installed`)); } catch { - skipped.push(source.name); - spinner.stop(c.dim(`${source.name} skills skipped (unavailable)`)); + console.log(c.dim(`${source.name} skills skipped`)); } } - - if (skipped.length > 0) { - console.log(` ${c.dim("Skipped:")} ${skipped.join(", ")}`); - console.log(); - } - - if (installed.length > 0) { - clack.outro(c.success(`${installed.join(" + ")} skills installed.`)); - } else { - clack.outro(c.warn("No skills installed.")); - } }, });