From 82529aa59c640d15b59ea49522a0eb7b06a45aba Mon Sep 17 00:00:00 2001 From: Rafael Thayto Date: Mon, 20 Apr 2026 12:53:33 -0300 Subject: [PATCH 1/3] fix(init): skip scaffold confirmation during bootstrap In bootstrap flows (`--starter` or an empty directory), the user has already consented to project creation, so the secondary "Proceed?" scaffold prompt is redundant. The scaffold preview is still printed. --- .changeset/init-adjustments.md | 5 +++ .../cli-core/src/commands/init/index.test.ts | 44 +++++++++++++++++++ packages/cli-core/src/commands/init/index.ts | 5 ++- 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 .changeset/init-adjustments.md diff --git a/.changeset/init-adjustments.md b/.changeset/init-adjustments.md new file mode 100644 index 00000000..56f0f835 --- /dev/null +++ b/.changeset/init-adjustments.md @@ -0,0 +1,5 @@ +--- +"clerk": patch +--- + +Skip the redundant "Proceed?" scaffold confirmation when `clerk init` bootstraps a new project (via `--starter` or on an empty directory). The scaffold plan is still previewed; only the now-superfluous prompt is removed since the user already opted in by starting bootstrap. diff --git a/packages/cli-core/src/commands/init/index.test.ts b/packages/cli-core/src/commands/init/index.test.ts index 90572cbb..59fd69a9 100644 --- a/packages/cli-core/src/commands/init/index.test.ts +++ b/packages/cli-core/src/commands/init/index.test.ts @@ -169,6 +169,50 @@ describe("init", () => { expect(heuristics.printKeylessInfo).not.toHaveBeenCalled(); }); + test("bootstrap flow skips scaffold Proceed? prompt (user already opted in)", async () => { + setup({ email: "test@test.com" }); + setupBootstrapSuccess(); + spyOn(scaffoldMod, "scaffold").mockResolvedValue({ + actions: [{ type: "create", path: "app/layout.tsx", content: "", description: "" }], + postInstructions: [], + }); + + await init({}); + + expect(bootstrapMod.promptAndBootstrap).toHaveBeenCalled(); + expect(previewMod.previewAndConfirm).not.toHaveBeenCalled(); + expect(previewMod.previewPlan).toHaveBeenCalled(); + }); + + test("--starter skips scaffold Proceed? prompt even without -y", async () => { + setup({ email: "test@test.com" }); + spyOn(context, "gatherContext").mockResolvedValue(FAKE_CTX); + spyOn(config, "resolveProfile").mockResolvedValue({ profile: { appId: "app_123" } } as never); + spyOn(scaffoldMod, "scaffold").mockResolvedValue({ + actions: [{ type: "create", path: "app/layout.tsx", content: "", description: "" }], + postInstructions: [], + }); + + await init({ starter: true }); + + expect(previewMod.previewAndConfirm).not.toHaveBeenCalled(); + expect(previewMod.previewPlan).toHaveBeenCalled(); + }); + + test("existing project without -y still prompts scaffold Proceed?", async () => { + setup({ email: "test@test.com" }); + spyOn(context, "gatherContext").mockResolvedValue(FAKE_CTX); + spyOn(config, "resolveProfile").mockResolvedValue({ profile: { appId: "app_123" } } as never); + spyOn(scaffoldMod, "scaffold").mockResolvedValue({ + actions: [{ type: "create", path: "app/layout.tsx", content: "", description: "" }], + postInstructions: [], + }); + + await init({}); + + expect(previewMod.previewAndConfirm).toHaveBeenCalled(); + }); + test("blank dir with keyless framework auto-selects keyless when unauthenticated", async () => { setup(); diff --git a/packages/cli-core/src/commands/init/index.ts b/packages/cli-core/src/commands/init/index.ts index cea225da..74086a91 100644 --- a/packages/cli-core/src/commands/init/index.ts +++ b/packages/cli-core/src/commands/init/index.ts @@ -100,7 +100,10 @@ export async function init(options: InitOptions = {}) { // Short-circuit on a fully-clean re-run so env pull / skills prompt don't // execute when there's nothing to do. - const { alreadySetUp } = await detectAndInstall(ctx.cwd, ctx, overrides.skipConfirm); + // Bootstrap implies consent — the user already opted into project creation, so + // skip the scaffold "Proceed?" prompt as well. + const skipScaffoldConfirm = overrides.skipConfirm || bootstrap != null; + const { alreadySetUp } = await detectAndInstall(ctx.cwd, ctx, skipScaffoldConfirm); if (alreadySetUp) { log.success("\nClerk is already set up in this project."); From 6fdcafe7c898c81cd7bcf95c1f826a0fad8343dd Mon Sep 17 00:00:00 2001 From: Rafael Thayto Date: Mon, 20 Apr 2026 13:05:03 -0300 Subject: [PATCH 2/3] fix(init): print bootstrap next steps after skills prompt Move the bootstrap next-steps block (`cd `, ` dev`, etc.) to run after the "Install agent skills?" prompt so those steps remain the last thing visible when the command finishes, instead of getting buried above the skills prompt. --- .changeset/init-adjustments.md | 5 ++++- packages/cli-core/src/commands/init/index.ts | 10 ++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.changeset/init-adjustments.md b/.changeset/init-adjustments.md index 56f0f835..627f4c75 100644 --- a/.changeset/init-adjustments.md +++ b/.changeset/init-adjustments.md @@ -2,4 +2,7 @@ "clerk": patch --- -Skip the redundant "Proceed?" scaffold confirmation when `clerk init` bootstraps a new project (via `--starter` or on an empty directory). The scaffold plan is still previewed; only the now-superfluous prompt is removed since the user already opted in by starting bootstrap. +Tighten the `clerk init` bootstrap flow: + +- Skip the redundant "Proceed?" scaffold confirmation when bootstrapping a new project (via `--starter` or on an empty directory). The scaffold plan is still previewed; only the now-superfluous prompt is removed since the user already opted in by starting bootstrap. +- Print bootstrap next steps (`cd `, ` dev`, etc.) after the optional "Install agent skills?" prompt so they remain the last thing visible when the command finishes. diff --git a/packages/cli-core/src/commands/init/index.ts b/packages/cli-core/src/commands/init/index.ts index 74086a91..6f90c72b 100644 --- a/packages/cli-core/src/commands/init/index.ts +++ b/packages/cli-core/src/commands/init/index.ts @@ -120,15 +120,17 @@ export async function init(options: InitOptions = {}) { printKeylessInfo(); } - if (bootstrap) { - printBootstrapNextSteps(bootstrap, keyless); - } - if (options.skills !== false) { bar(); await installSkills(ctx.cwd, ctx.framework.dep, ctx.packageManager, overrides.skipConfirm); } + // Next steps print last so they stay on screen as the final thing the user sees. + if (bootstrap) { + bar(); + printBootstrapNextSteps(bootstrap, keyless); + } + outro("Done"); } From 6bb53b3392a4e5c1b0397fbdce3706de9cb6dba3 Mon Sep 17 00:00:00 2001 From: Rafael Thayto Date: Mon, 20 Apr 2026 18:43:01 -0300 Subject: [PATCH 3/3] test(init): assert printBootstrapNextSteps fires after installSkills Addresses review nit: previous tests only verified call counts, not order. A call-order sentinel array now catches any future regression where next-steps printing is moved before skills installation. --- .../cli-core/src/commands/init/index.test.ts | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/cli-core/src/commands/init/index.test.ts b/packages/cli-core/src/commands/init/index.test.ts index 59fd69a9..90674dfd 100644 --- a/packages/cli-core/src/commands/init/index.test.ts +++ b/packages/cli-core/src/commands/init/index.test.ts @@ -18,6 +18,7 @@ import * as scanMod from "./scan.ts"; import * as heuristics from "./heuristics.ts"; import * as skillsMod from "./skills.ts"; import * as bootstrapMod from "./bootstrap.ts"; +import * as nextStepsMod from "../../lib/next-steps.ts"; import { init } from "./index.ts"; const FAKE_CTX = { @@ -213,6 +214,31 @@ describe("init", () => { expect(previewMod.previewAndConfirm).toHaveBeenCalled(); }); + test("bootstrap prints next steps after skills install", async () => { + setup({ email: "test@test.com" }); + setupBootstrapSuccess(); + spyOn(scaffoldMod, "scaffold").mockResolvedValue({ + actions: [{ type: "create", path: "app/layout.tsx", content: "", description: "" }], + postInstructions: [], + }); + + const callOrder: string[] = []; + spies.push( + spyOn(skillsMod, "installSkills").mockImplementation(async () => { + callOrder.push("installSkills"); + }), + ); + spies.push( + spyOn(nextStepsMod, "printNextSteps").mockImplementation(() => { + callOrder.push("printNextSteps"); + }), + ); + + await init({}); + + expect(callOrder.indexOf("installSkills")).toBeLessThan(callOrder.indexOf("printNextSteps")); + }); + test("blank dir with keyless framework auto-selects keyless when unauthenticated", async () => { setup();