From 57b6ba7e7f45f14b5d1242efad40083867d95d57 Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Mon, 7 Sep 2026 15:51:27 +0530 Subject: [PATCH 1/3] fix: forward Prisma init consent and preserve setup errors Signed-off-by: Aman Varshney --- README.md | 2 +- src/commands/create.ts | 1 + src/index.ts | 5 +- src/tasks/composer/auth.ts | 2 +- src/tasks/composer/projects.ts | 2 +- src/tasks/deploy-with-composer.ts | 4 +- src/tasks/{composer => }/prisma-cli.ts | 17 +++-- src/tasks/prisma-setup/commands.ts | 24 +++--- src/tasks/prisma-setup/types.ts | 1 + src/tasks/setup-prisma.ts | 2 +- src/utils/errors.ts | 3 + tests/deploy-with-composer.test.ts | 10 +++ tests/e2e/create-prisma.e2e.test.ts | 46 +++++++++++ tests/setup-prisma.test.ts | 101 ++++++++++++++++++++++++- 14 files changed, 195 insertions(+), 25 deletions(-) rename src/tasks/{composer => }/prisma-cli.ts (86%) diff --git a/README.md b/README.md index 1abe565..ee2edd4 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ older cached version. Prisma Compute does not support Deno deployments yet. - `--deploy` / `--no-deploy` - `--workspace ` - `--yes` -- `--force` +- `--force`: overwrite generated starter and Prisma files in a non-empty directory. This replaces existing Prisma config, contract, and database-client files; back up edits first. - `--verbose` - `--json` diff --git a/src/commands/create.ts b/src/commands/create.ts index b487160..7076c9a 100644 --- a/src/commands/create.ts +++ b/src/commands/create.ts @@ -93,6 +93,7 @@ const executeCreateContext = Effect.fn("Create.execute")(function* (context: Cre template: context.template, createdProjectPath: context.targetDirectory, includeDevNextStep: true, + force: context.force, initializeGit: !context.targetPathState.exists || context.targetPathState.isEmptyDirectory, progressSpinner: createSpinner, }); diff --git a/src/index.ts b/src/index.ts index db26d60..1bb3f0e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -64,7 +64,10 @@ export const createPrismaCommand = Command.make( ), deploy: optionalBoolean("deploy", "Deploy the generated app to Prisma immediately"), workspace: optionalString("workspace", "Prisma workspace id or name to deploy into"), - force: optionalBoolean("force", "Allow scaffolding into a non-empty target directory"), + force: optionalBoolean( + "force", + "Overwrite generated starter and Prisma files in a non-empty directory", + ), yes: optionalBoolean("yes", "Skip prompts and accept default choices"), verbose: optionalBoolean("verbose", "Show verbose command output during setup"), json: optionalBoolean( diff --git a/src/tasks/composer/auth.ts b/src/tasks/composer/auth.ts index 606ed84..a5fa16d 100644 --- a/src/tasks/composer/auth.ts +++ b/src/tasks/composer/auth.ts @@ -11,7 +11,7 @@ import { getLocalPackageBinaryCommand, getRunScriptCommand, } from "../../utils/package-manager"; -import { decodePrismaCommandResult, runPrismaJsonCommandEffect } from "./prisma-cli"; +import { decodePrismaCommandResult, runPrismaJsonCommandEffect } from "../prisma-cli"; import { getWorkspaceLabel } from "./workspace"; const WhoamiResultSchema = Schema.Struct({ diff --git a/src/tasks/composer/projects.ts b/src/tasks/composer/projects.ts index b5a3797..4e68ea7 100644 --- a/src/tasks/composer/projects.ts +++ b/src/tasks/composer/projects.ts @@ -3,7 +3,7 @@ import { Effect, Schema } from "effect"; import { CreateFailure } from "../../create-outcome"; import { PrismaWorkspaceSchema, type PrismaWorkspace } from "../../result"; import type { PackageManager } from "../../types"; -import { decodePrismaCommandResult, runPrismaJsonCommandEffect } from "./prisma-cli"; +import { decodePrismaCommandResult, runPrismaJsonCommandEffect } from "../prisma-cli"; import { getWorkspaceLabel } from "./workspace"; const ProjectShowResultSchema = Schema.Struct({ diff --git a/src/tasks/deploy-with-composer.ts b/src/tasks/deploy-with-composer.ts index 3981c5f..d908bac 100644 --- a/src/tasks/deploy-with-composer.ts +++ b/src/tasks/deploy-with-composer.ts @@ -20,7 +20,7 @@ import { ComposerDeployCommandResultSchema, parseComposerDeployResult, } from "./composer/deployment-result"; -import { decodePrismaCommandResult, runPrismaJsonCommandEffect } from "./composer/prisma-cli"; +import { decodePrismaCommandResult, runPrismaJsonCommandEffect } from "./prisma-cli"; import { ensureProjectNameAvailable, getProjectDetails } from "./composer/projects"; export type ComposerDeployExecutionResult = @@ -259,6 +259,6 @@ export async function deployNewProjectWithComposer( } export { parseComposerDeployResult } from "./composer/deployment-result"; -export { parsePrismaCliEnvelope, PrismaCliCommandError } from "./composer/prisma-cli"; +export { parsePrismaCliEnvelope, PrismaCliCommandError } from "./prisma-cli"; export { findProjectNameCollisions, getConsoleProjectUrl } from "./composer/projects"; export type { ComposerDeployResult } from "../result"; diff --git a/src/tasks/composer/prisma-cli.ts b/src/tasks/prisma-cli.ts similarity index 86% rename from src/tasks/composer/prisma-cli.ts rename to src/tasks/prisma-cli.ts index 4a6b3b6..227a354 100644 --- a/src/tasks/composer/prisma-cli.ts +++ b/src/tasks/prisma-cli.ts @@ -1,10 +1,10 @@ import { Effect, Schema } from "effect"; -import { PrismaCliCommandError } from "../../create-outcome"; -import { CommandRunner } from "../../services/command-runner"; -import type { PackageManager } from "../../types"; -import { getErrorMessage } from "../../utils/errors"; -import { getLocalPackageBinaryArgs } from "../../utils/package-manager"; +import { PrismaCliCommandError } from "../create-outcome"; +import { CommandRunner } from "../services/command-runner"; +import type { PackageManager } from "../types"; +import { getErrorMessage } from "../utils/errors"; +import { getLocalPackageBinaryArgs } from "../utils/package-manager"; const PrismaCliEnvelopeSchema = Schema.Struct({ ok: Schema.Boolean, @@ -47,6 +47,7 @@ export const runPrismaJsonCommandEffect = Effect.fn("PrismaCli.runJson")(functio packageManager: PackageManager; projectDir: string; args: string[]; + env?: NodeJS.ProcessEnv; onStderrLine?: (line: string) => void; }) { const runner = yield* CommandRunner; @@ -59,7 +60,7 @@ export const runPrismaJsonCommandEffect = Effect.fn("PrismaCli.runJson")(functio command: invocation.command, args: invocation.args, cwd: options.projectDir, - env: process.env, + env: options.env ?? process.env, ...(options.onStderrLine ? { onStderrLine: options.onStderrLine } : {}), }); @@ -68,7 +69,7 @@ export const runPrismaJsonCommandEffect = Effect.fn("PrismaCli.runJson")(functio envelope = parsePrismaCliEnvelope(result.stdout); } catch (cause) { return yield* new PrismaCliCommandError({ - message: result.stderr.trim() || getErrorMessage(cause), + message: result.stderr.trim() || result.stdout.trim() || getErrorMessage(cause), stderr: result.stderr, exitCode: result.exitCode, }); @@ -102,4 +103,4 @@ export const decodePrismaCommandResult = (schema: Schema.Codec, value: unk ), ); -export { PrismaCliCommandError } from "../../create-outcome"; +export { PrismaCliCommandError } from "../create-outcome"; diff --git a/src/tasks/prisma-setup/commands.ts b/src/tasks/prisma-setup/commands.ts index e555016..6f1f1b4 100644 --- a/src/tasks/prisma-setup/commands.ts +++ b/src/tasks/prisma-setup/commands.ts @@ -4,7 +4,8 @@ import path from "node:path"; import type { AuthoringStyle, DatabaseProvider } from "../../types"; import { getLocalPackageBinaryArgs } from "../../utils/package-manager"; -import { runSetupCommand } from "../../utils/run-command"; +import { redactSecrets } from "../../utils/errors"; +import { runPrismaJsonCommandEffect } from "../prisma-cli"; import type { PrismaSetupContext } from "./types"; const getContractPath = (authoring: AuthoringStyle) => @@ -24,25 +25,30 @@ export const runPrismaCli = Effect.fn("PrismaSetup.runCli")(function* ( log.step([invocation.command, ...invocation.args].join(" "), { output: context.output }); } }); - yield* runSetupCommand({ - command: invocation.command, - args: invocation.args, - cwd: projectDir, + yield* runPrismaJsonCommandEffect({ + packageManager: context.packageManager, + projectDir, + args, env: { ...process.env, CI: "1" }, - verbose: context.verbose, - json: context.json, + ...(context.verbose + ? { + onStderrLine: (line: string) => + log.message(redactSecrets(line), { output: context.output }), + } + : {}), }); }); export const runPrismaInit = Effect.fn("PrismaSetup.init")(function* ( context: PrismaSetupContext, projectDir: string, + force = false, ) { yield* runPrismaCli(context, projectDir, [ "orm", "init", "--yes", - "--no-interactive", + ...(force ? ["--confirm", path.basename(projectDir).trim() || projectDir.trim()] : []), "--target", getInitTarget(context.databaseProvider), "--authoring", @@ -62,5 +68,5 @@ export const initializeAgentSkills = Effect.fn("PrismaSetup.initializeSkills")(f projectDir: string, ) { if (context.packageManager === "deno") return; - yield* runPrismaCli(context, projectDir, ["init", "--yes", "--no-interactive"]); + yield* runPrismaCli(context, projectDir, ["init", "--yes"]); }); diff --git a/src/tasks/prisma-setup/types.ts b/src/tasks/prisma-setup/types.ts index 4e70fe0..0f9b502 100644 --- a/src/tasks/prisma-setup/types.ts +++ b/src/tasks/prisma-setup/types.ts @@ -13,6 +13,7 @@ export type PrismaSetupRunOptions = { createdProjectPath?: string; includeDevNextStep?: boolean; initializeGit?: boolean; + force?: boolean; progressSpinner?: ReturnType; }; diff --git a/src/tasks/setup-prisma.ts b/src/tasks/setup-prisma.ts index 6716f4a..1b7788e 100644 --- a/src/tasks/setup-prisma.ts +++ b/src/tasks/setup-prisma.ts @@ -83,7 +83,7 @@ export const executePrismaSetupContextEffect = Effect.fn("PrismaSetup.execute")( yield* Effect.sync(() => progress?.message("Preparing Prisma 8 project files...")); yield* atCreateStage( - runPrismaInit(context, projectDir), + runPrismaInit(context, projectDir, options.force), "initialize_prisma", "prisma_init_failed", ); diff --git a/src/utils/errors.ts b/src/utils/errors.ts index 314c791..ddd4c6d 100644 --- a/src/utils/errors.ts +++ b/src/utils/errors.ts @@ -1,3 +1,5 @@ +import { PrismaCliCommandError } from "../create-outcome"; + export function redactSecrets(message: string): string { return message .replace( @@ -12,6 +14,7 @@ export function redactSecrets(message: string): string { } export function getErrorMessage(error: unknown): string { + if (error instanceof PrismaCliCommandError) return redactSecrets(error.message); if (error instanceof Error && "stderr" in error) { const stderr = String((error as { stderr?: string }).stderr ?? "").trim(); if (stderr) return redactSecrets(stderr); diff --git a/tests/deploy-with-composer.test.ts b/tests/deploy-with-composer.test.ts index 37f5fb4..5d3036b 100644 --- a/tests/deploy-with-composer.test.ts +++ b/tests/deploy-with-composer.test.ts @@ -43,6 +43,16 @@ describe("redactSecrets", () => { expect(getErrorMessage(error)).toBe("DATABASE_URL="); }); + + test("prefers a structured Prisma error over package-manager stderr", () => { + const error = new PrismaCliCommandError({ + message: "Explicit overwrite consent is required", + code: "CLI.CONSENT_REQUIRED", + stderr: 'error: "prisma" exited with code 2', + exitCode: 2, + }); + expect(getErrorMessage(error)).toBe("Explicit overwrite consent is required"); + }); }); describe("findProjectNameCollisions", () => { diff --git a/tests/e2e/create-prisma.e2e.test.ts b/tests/e2e/create-prisma.e2e.test.ts index d2dcbf8..111f07f 100644 --- a/tests/e2e/create-prisma.e2e.test.ts +++ b/tests/e2e/create-prisma.e2e.test.ts @@ -222,6 +222,52 @@ afterEach(async () => { }); describe("create-prisma e2e", () => { + test( + "resumes a partially scaffolded app only with explicit --force", + async () => { + const rootDir = await mkdtemp(path.join(tmpdir(), "create-prisma-force-e2e-")); + tempRoots.push(rootDir); + const args = [ + "retry app", + "--template", + "minimal", + "--authoring", + "psl", + "--package-manager", + "bun", + "--no-deploy", + "--json", + ]; + const projectDir = path.join(rootDir, "retry app"); + await scaffoldCreateTemplate({ + projectDir, + projectName: "retry-app", + template: "minimal", + provider: "postgres", + authoring: "psl", + packageManager: "bun", + }); + const contractPath = path.join(projectDir, "src/prisma/contract.prisma"); + await writeFile(contractPath, `${TEST_PSL_CONTRACT}\n// User modification\n`); + await writeFile(path.join(projectDir, "keep.txt"), "Unrelated user file\n"); + + const refused = await runCreatePrismaJson(rootDir, args); + expect(refused.exitCode).toBe(1); + expect(refused.result).toMatchObject({ ok: false, error: { stage: "collect_context" } }); + expect(await readFile(contractPath, "utf8")).toContain("// User modification"); + + const retried = await runCreatePrismaJson(rootDir, [...args, "--force"]); + expect(retried.result).toMatchObject({ ok: true }); + expect(retried.exitCode).toBe(0); + expect(await readFile(contractPath, "utf8")).not.toContain("// User modification"); + expect(await readFile(path.join(projectDir, "keep.txt"), "utf8")).toBe( + "Unrelated user file\n", + ); + await runCommand(projectDir, ["bun", "run", "build"]); + }, + TEST_TIMEOUT, + ); + test("returns a non-zero exit code when project setup fails", async () => { const rootDir = await mkdtemp(path.join(tmpdir(), "create-prisma-exit-code-e2e-")); tempRoots.push(rootDir); diff --git a/tests/setup-prisma.test.ts b/tests/setup-prisma.test.ts index e4dc781..43473d4 100644 --- a/tests/setup-prisma.test.ts +++ b/tests/setup-prisma.test.ts @@ -6,7 +6,12 @@ import path from "node:path"; import { runCreateCommandEffect } from "../src/commands/create"; import { applicationRuntime } from "../src/runtime"; -import { CommandExecutionError, CommandRunner } from "../src/services/command-runner"; +import { + CommandExecutionError, + CommandRunner, + type CommandSpec, +} from "../src/services/command-runner"; +import { initializeAgentSkills, runPrismaInit } from "../src/tasks/prisma-setup/commands"; import { collectPrismaSetupContext } from "../src/tasks/setup-prisma"; async function withTempProject(run: (projectDir: string) => Promise): Promise { @@ -65,6 +70,100 @@ test("writes pnpm build permissions before the first dependency installation", a }); }); +describe("Prisma setup commands", () => { + test("keeps package-manager errors when Prisma cannot start", async () => { + await withTempProject(async (projectDir) => { + const context = await collectPrismaSetupContext( + { json: true, deploy: false, packageManager: "pnpm" }, + { projectDir }, + ); + const message = "ERR_PNPM_OUTDATED_LOCKFILE Cannot install with frozen-lockfile"; + const error = await applicationRuntime.runPromise( + initializeAgentSkills(context, projectDir).pipe( + Effect.provideService(CommandRunner, { + run: () => Effect.succeed({ exitCode: 1, stdout: message, stderr: "" }), + runChecked: () => Effect.die("Expected structured Prisma execution"), + }), + Effect.flip, + ), + ); + expect(error).toMatchObject({ message, exitCode: 1 }); + }); + }); + + test("grants overwrite consent only with explicit force", async () => { + await withTempProject(async (root) => { + const projectDir = path.join(root, "retry app"); + const context = await collectPrismaSetupContext( + { json: true, deploy: false, packageManager: "npm" }, + { projectDir }, + ); + const commands: CommandSpec[] = []; + const runner = { + run: (spec: CommandSpec) => { + commands.push(spec); + return Effect.succeed({ + exitCode: 0, + stdout: '{"kind":"result","envelope":{"ok":true,"result":{}}}', + stderr: "", + }); + }, + runChecked: () => Effect.die("Expected structured Prisma execution"), + }; + await applicationRuntime.runPromise( + Effect.gen(function* () { + yield* runPrismaInit(context, projectDir); + yield* runPrismaInit(context, projectDir, true); + }).pipe(Effect.provideService(CommandRunner, runner)), + ); + expect(commands[0]?.args).not.toContain("--confirm"); + expect(commands[1]?.args).toContain("--confirm"); + expect(commands[1]?.args[commands[1].args.indexOf("--confirm") + 1]).toBe("retry app"); + expect(commands[0]?.args).toContain("--json"); + expect(commands[0]?.env?.CI).toBe("1"); + }); + }); + + test.each([ + { run: runPrismaInit, command: "orm.init", code: "CLI.CONSENT_REQUIRED" }, + { run: initializeAgentSkills, command: "init", code: "SKILLS.CONFIG_INVALID" }, + ])("preserves structured errors from $command", async ({ run, command, code }) => { + await withTempProject(async (projectDir) => { + const context = await collectPrismaSetupContext( + { json: true, deploy: false, packageManager: "pnpm" }, + { projectDir }, + ); + const error = await applicationRuntime.runPromise( + run(context, projectDir).pipe( + Effect.provideService(CommandRunner, { + run: () => + Effect.succeed({ + exitCode: 2, + stdout: JSON.stringify({ + kind: "result", + envelope: { + ok: false, + commandId: command, + error: { code, summary: "Setup rejected", why: "Explicit action is required" }, + }, + }), + stderr: "", + }), + runChecked: () => Effect.die("Expected structured Prisma execution"), + }), + Effect.flip, + ), + ); + expect(error).toMatchObject({ + message: "Setup rejected: Explicit action is required", + exitCode: 2, + prismaCliCommand: command, + prismaCliErrorCode: code, + }); + }); + }); +}); + describe("collectPrismaSetupContext", () => { test("--yes uses Prisma Postgres defaults without deploying", async () => { await withTempProject(async (projectDir) => { From 69522a01bb3a6f32ae609d6c3981acabb523a922 Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Mon, 7 Sep 2026 15:51:49 +0530 Subject: [PATCH 2/3] test: use the verified Prisma skills config error code Signed-off-by: Aman Varshney --- tests/setup-prisma.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/setup-prisma.test.ts b/tests/setup-prisma.test.ts index 43473d4..97915f0 100644 --- a/tests/setup-prisma.test.ts +++ b/tests/setup-prisma.test.ts @@ -126,7 +126,7 @@ describe("Prisma setup commands", () => { test.each([ { run: runPrismaInit, command: "orm.init", code: "CLI.CONSENT_REQUIRED" }, - { run: initializeAgentSkills, command: "init", code: "SKILLS.CONFIG_INVALID" }, + { run: initializeAgentSkills, command: "init", code: "CLI.CONFIG_SECTION_INVALID" }, ])("preserves structured errors from $command", async ({ run, command, code }) => { await withTempProject(async (projectDir) => { const context = await collectPrismaSetupContext( From fb3e732333214c65ddb8918858aa9ca8e6abebcd Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Mon, 7 Sep 2026 16:23:55 +0530 Subject: [PATCH 3/3] fix: redact stored Prisma command errors Signed-off-by: Aman Varshney --- src/tasks/prisma-cli.ts | 16 +++++++------- tests/setup-prisma.test.ts | 43 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/tasks/prisma-cli.ts b/src/tasks/prisma-cli.ts index 227a354..4f9afb3 100644 --- a/src/tasks/prisma-cli.ts +++ b/src/tasks/prisma-cli.ts @@ -3,7 +3,7 @@ import { Effect, Schema } from "effect"; import { PrismaCliCommandError } from "../create-outcome"; import { CommandRunner } from "../services/command-runner"; import type { PackageManager } from "../types"; -import { getErrorMessage } from "../utils/errors"; +import { getErrorMessage, redactSecrets } from "../utils/errors"; import { getLocalPackageBinaryArgs } from "../utils/package-manager"; const PrismaCliEnvelopeSchema = Schema.Struct({ @@ -69,8 +69,9 @@ export const runPrismaJsonCommandEffect = Effect.fn("PrismaCli.runJson")(functio envelope = parsePrismaCliEnvelope(result.stdout); } catch (cause) { return yield* new PrismaCliCommandError({ - message: result.stderr.trim() || result.stdout.trim() || getErrorMessage(cause), - stderr: result.stderr, + message: + redactSecrets(result.stderr.trim() || result.stdout.trim()) || getErrorMessage(cause), + stderr: redactSecrets(result.stderr), exitCode: result.exitCode, }); } @@ -78,15 +79,16 @@ export const runPrismaJsonCommandEffect = Effect.fn("PrismaCli.runJson")(functio if (result.exitCode !== 0 || !envelope.ok || envelope.result === undefined) { const summary = envelope.error?.summary ?? envelope.error?.message; return yield* new PrismaCliCommandError({ - message: + message: redactSecrets( [summary, envelope.error?.why].filter(Boolean).join(": ") || - result.stderr.trim() || - "Prisma CLI command failed.", + result.stderr.trim() || + "Prisma CLI command failed.", + ), ...(envelope.commandId || envelope.command ? { command: envelope.commandId ?? envelope.command } : {}), ...(envelope.error?.code ? { code: envelope.error.code } : {}), - stderr: result.stderr, + stderr: redactSecrets(result.stderr), exitCode: result.exitCode, }); } diff --git a/tests/setup-prisma.test.ts b/tests/setup-prisma.test.ts index 97915f0..25f1ac8 100644 --- a/tests/setup-prisma.test.ts +++ b/tests/setup-prisma.test.ts @@ -12,6 +12,7 @@ import { type CommandSpec, } from "../src/services/command-runner"; import { initializeAgentSkills, runPrismaInit } from "../src/tasks/prisma-setup/commands"; +import { runPrismaJsonCommandEffect } from "../src/tasks/prisma-cli"; import { collectPrismaSetupContext } from "../src/tasks/setup-prisma"; async function withTempProject(run: (projectDir: string) => Promise): Promise { @@ -71,6 +72,45 @@ test("writes pnpm build permissions before the first dependency installation", a }); describe("Prisma setup commands", () => { + test.each([ + { source: "stdout", stdout: "DATABASE_URL=postgres://user:secret@localhost/db", stderr: "" }, + { source: "stderr", stdout: "", stderr: "DATABASE_URL=postgres://user:secret@localhost/db" }, + { + source: "envelope fallback", + stdout: JSON.stringify({ ok: false }), + stderr: "DATABASE_URL=postgres://user:secret@localhost/db", + }, + { + source: "structured error", + stdout: JSON.stringify({ + ok: false, + error: { + summary: "Connection failed", + why: "DATABASE_URL=postgres://user:secret@localhost/db", + }, + }), + stderr: "Authorization: Bearer private-token", + }, + ])("redacts stored errors from $source", async ({ stdout, stderr }) => { + const error = await applicationRuntime.runPromise( + runPrismaJsonCommandEffect({ + packageManager: "npm", + projectDir: process.cwd(), + args: ["init", "--yes"], + }).pipe( + Effect.provideService(CommandRunner, { + run: () => Effect.succeed({ exitCode: 1, stdout, stderr }), + runChecked: () => Effect.die("Expected structured Prisma execution"), + }), + Effect.flip, + ), + ); + expect(error).toMatchObject({ exitCode: 1, message: expect.stringContaining("") }); + expect(JSON.stringify(error)).not.toContain("user:secret"); + expect(JSON.stringify(error)).not.toContain("private-token"); + if (stderr) expect(error).toMatchObject({ stderr: expect.stringContaining("") }); + }); + test("keeps package-manager errors when Prisma cannot start", async () => { await withTempProject(async (projectDir) => { const context = await collectPrismaSetupContext( @@ -114,6 +154,7 @@ describe("Prisma setup commands", () => { Effect.gen(function* () { yield* runPrismaInit(context, projectDir); yield* runPrismaInit(context, projectDir, true); + yield* initializeAgentSkills(context, projectDir); }).pipe(Effect.provideService(CommandRunner, runner)), ); expect(commands[0]?.args).not.toContain("--confirm"); @@ -121,6 +162,8 @@ describe("Prisma setup commands", () => { expect(commands[1]?.args[commands[1].args.indexOf("--confirm") + 1]).toBe("retry app"); expect(commands[0]?.args).toContain("--json"); expect(commands[0]?.env?.CI).toBe("1"); + expect(commands[2]?.args.slice(-4)).toEqual(["init", "--yes", "--json", "--no-interactive"]); + expect(commands[2]?.args.filter((arg) => arg === "--no-interactive")).toHaveLength(1); }); });