Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/product/command-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ Purpose:
Behavior:

- init is the config formalizer: `app deploy` works with zero config, and init writes down what deploy would infer so the setup is committed, reviewable, and stable for teammates and CI
- writing the config requires no auth and no network; linking is the only remote step
- writing the config requires no auth and no network; the only steps that go remote are an accepted link and an accepted agent skill install, both optional and both after the config is written
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
- fails with `INIT_CONFIG_EXISTS` when a compute config already exists in the invocation directory or any ancestor up to the repository or workspace root; the error names the existing file, and init never overwrites or merges — editing a committed config is the user's editor's job
- `--format <ts|json>` selects the config serialization; `ts` (alias `typescript`) is the default and writes `prisma.compute.ts`, `json` writes `prisma.compute.json` via the shared SDK serializer, a dependency-free static document (a `$schema` reference will be emitted once the schema is hosted; the loader already accepts and strips one); both formats pin exactly the same resolved values, and the CLI's global `--json` output flag is unrelated to the config format
- with `--format json`, the types install step never runs: the JSON format exists to be dependency-free, so `--install` is a usage error; without `--install`, the result reports the step as skipped with no install hint
Expand All @@ -418,6 +418,7 @@ Behavior:
- interactive mode asks `Link this directory to a Prisma Project now? (Y/n)` when the directory has no project binding; accepting enters the same picker `project link` uses
- `--no-link` suppresses the question; `--link` requires the step; `--project <id-or-name>` links to that project without prompting
- link failures and cancellations after the config is written downgrade to warnings and `nextSteps`; the config write stands and init exits 0
- agent skill step, after the link step: interactive runs prompt once to install the Prisma Compute skill for the project when `prisma-compute` is missing, the same shared prompt `app deploy` uses; accepting installs `prisma-compute` from `prisma/skills` from the config directory, equivalent to the detected package-runner command such as `pnpm dlx @prisma/cli@latest agent install --skill prisma-compute`; declining records the prompt as dismissed in local CLI state, so neither init nor deploy asks again; a failed install downgrades to a warning with the retry command, records no dismissal (a later interactive init or deploy offers again), and the config write stands; does not prompt in `--json`, `--quiet`, CI, non-interactive, or `--yes` runs
- `nextSteps` includes the deploy command, plus the project link command when the directory is still unlinked
- user-facing command hints in init output (next steps, link hints, error recovery commands) use the package runner detected from the project, such as `pnpm dlx @prisma/cli@latest project link` or `npx -y @prisma/cli@latest app deploy`, matching the `agent` group's convention
- in `--json`, `result` includes `configPath`, `format` (`typescript` or `json`), `converted` (true only for the `--format ts` conversion path), the written `app` values (null when a conversion transported a config that does not pin a single fully-resolved app), per-value `settings` sources, and `link` state; `--json` never prompts
Expand Down Expand Up @@ -1411,7 +1412,7 @@ Behavior:
- maps user-facing framework names to deploy build strategies
- does not accept `--build-command` or `--output-directory`; custom build settings live in the `build` block of `prisma.compute.ts`
- deploys arbitrary framework output with `framework: "custom"` when the config provides a built output directory and entrypoint; `build.command` is optional for prebuilt artifacts
- in interactive human deploys, prompts once to install the Prisma Compute skill for the project when `prisma-compute` is missing; accepting installs `prisma-compute` from `prisma/skills` from the project directory, equivalent to the detected package-runner command such as `pnpm dlx @prisma/cli@latest agent install --skill prisma-compute`; declining records the prompt as dismissed in local CLI state
- in interactive human deploys, prompts once to install the Prisma Compute skill for the project when `prisma-compute` is missing; accepting installs `prisma-compute` from `prisma/skills` from the project directory, equivalent to the detected package-runner command such as `pnpm dlx @prisma/cli@latest agent install --skill prisma-compute`; declining records the prompt as dismissed in local CLI state, while a failed install records no dismissal, so a later interactive run offers again; the prompt and its dismissal state are shared with `init`, so whichever command runs interactively first asks and neither asks twice
- does not prompt for agent setup in `--json`, `--quiet`, CI, `--no-interactive`, or `--yes`
- uses `src/index.ts` as the Hono deploy entrypoint when the app has no `package.json#main` or `package.json#module` and that file exists
- supports vanilla Bun apps with `--framework bun` using `package.json#main` or `package.json#module`, or with `--entry <path>`
Expand Down
90 changes: 90 additions & 0 deletions packages/cli/src/controllers/agent-setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { resolvePrismaCliPackageCommand } from "../lib/agent/cli-command";
import {
PRISMA_AGENT_INSTALL_ARGS,
PRISMA_COMPUTE_AGENT_SKILL,
} from "../lib/agent/constants";
import {
readPrismaAgentSetupStatus,
shouldOfferPrismaAgentSetup,
} from "../lib/agent/setup-status";
import { confirmPrompt } from "../shell/prompt";
import { type CommandContext, canPrompt } from "../shell/runtime";
import { renderSummaryLine } from "../shell/ui";
import { runAgentInstall } from "./agent";

/**
* One-time interactive offer to install the Prisma Compute skill for the
* project. Shared by the commands that establish a project setup (init,
* deploy); the answer is remembered, so whichever command runs first asks.
* Returns warnings; a failed install must not fail the calling command.
*/
export async function maybePromptForAgentSetup(
context: CommandContext,
projectDir: string,
): Promise<string[]> {
// canPrompt covers json/CI/non-TTY/--no-interactive; quiet and yes runs
// must also stay prompt-free even on a TTY.
if (!canPrompt(context) || context.flags.yes || context.flags.quiet) {
return [];
}

const status = await readPrismaAgentSetupStatus({
cwd: projectDir,
stateStore: context.stateStore,
signal: context.runtime.signal,
requiredSkill: PRISMA_COMPUTE_AGENT_SKILL,
});
if (!shouldOfferPrismaAgentSetup(status)) {
return [];
}

const shouldInstall = await confirmPrompt({
input: context.runtime.stdin,
output: context.runtime.stderr,
signal: context.runtime.signal,
message: "Install the Prisma Compute skill for this project?",
initialValue: true,
});

if (!shouldInstall) {
await context.stateStore.setAgentSetupPromptDismissedAt(
new Date().toISOString(),
);
return [];
}

try {
await runAgentInstall(
context,
{ skill: [PRISMA_COMPUTE_AGENT_SKILL] },
"install",
{ cwd: projectDir },
);
if (!context.flags.quiet && !context.flags.json) {
context.output.stderr.write(
`${renderSummaryLine(context.ui, "success", "Installed the Prisma Compute skill for this project.")}\n\n`,
);
}
return [];
} catch (error) {
const message =
error instanceof Error ? error.message : "Prisma skill install failed.";
if (!context.flags.quiet && !context.flags.json) {
context.output.stderr.write(
`${renderSummaryLine(context.ui, "warning", `Skipped Prisma skill install: ${message}`)}\n\n`,
);
}
const retryCommand = await resolvePrismaCliPackageCommand({
cwd: projectDir,
signal: context.runtime.signal,
args: [
...PRISMA_AGENT_INSTALL_ARGS,
"--skill",
PRISMA_COMPUTE_AGENT_SKILL,
],
});
return [
`The Prisma Compute skill was not installed. Run ${retryCommand} to try again.`,
];
}
}
82 changes: 2 additions & 80 deletions packages/cli/src/controllers/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,6 @@
import { matchError, Result } from "better-result";
import open from "open";
import { FileTokenStorage } from "../adapters/token-storage";
import { resolvePrismaCliPackageCommand } from "../lib/agent/cli-command";
import {
PRISMA_AGENT_INSTALL_ARGS,
PRISMA_COMPUTE_AGENT_SKILL,
} from "../lib/agent/constants";
import {
readPrismaAgentSetupStatus,
shouldOfferPrismaAgentSetup,
} from "../lib/agent/setup-status";
import { DEFAULT_REGION } from "../lib/app/app-interaction";
import {
type AppRecord,
Expand Down Expand Up @@ -139,7 +130,7 @@
import { type CommandSuccess, writeJsonEvent } from "../shell/output";
import { confirmPrompt, selectPrompt, textPrompt } from "../shell/prompt";
import { type CommandContext, canPrompt } from "../shell/runtime";
import { renderCommandHeader, renderSummaryLine } from "../shell/ui";
import { renderCommandHeader } from "../shell/ui";
import type {
AppBuildResult,
AppDeployAllResult,
Expand All @@ -166,7 +157,7 @@
import type { AuthWorkspace } from "../types/auth";
import type { BranchKind } from "../types/branch";
import type { ProjectResolution, ProjectSummary } from "../types/project";
import { runAgentInstall } from "./agent";
import { maybePromptForAgentSetup } from "./agent-setup";
import { requireAuthenticatedAuthState } from "./auth";
import { listRealWorkspaceProjects } from "./project";
import { createSelectPromptPort } from "./select-prompt-port";
Expand Down Expand Up @@ -584,7 +575,7 @@
});
}

async function runSingleAppDeploy(

Check notice on line 578 in packages/cli/src/controllers/app.ts

View workflow job for this annotation

GitHub Actions / Lint

lint/complexity/noExcessiveCognitiveComplexity

Excessive complexity of 18 detected (max: 15).
context: CommandContext,
appName: string | undefined,
options: AppDeployOptions | undefined,
Expand Down Expand Up @@ -1179,9 +1170,9 @@
...deployment.deployment,
live: providerLiveDeploymentId
? deployment.deployment.id === providerLiveDeploymentId
: knownLiveDeploymentId
? deployment.deployment.id === knownLiveDeploymentId
: deployment.deployment.live,

Check notice on line 1175 in packages/cli/src/controllers/app.ts

View workflow job for this annotation

GitHub Actions / Lint

lint/style/noNestedTernary

Do not nest ternary expressions.
},
},
warnings: [],
Expand Down Expand Up @@ -1560,10 +1551,10 @@
});
}

await sleep(
Math.min(pollIntervalMs, Math.max(deadline - Date.now(), 0)),
context.runtime.signal,
);

Check notice on line 1557 in packages/cli/src/controllers/app.ts

View workflow job for this annotation

GitHub Actions / Lint

lint/performance/noAwaitInLoops

Avoid using await inside loops.
current = await target.provider
.showDomain(current.id, { signal: context.runtime.signal })
.catch((error) => {
Expand Down Expand Up @@ -2125,7 +2116,7 @@
const compute = await resolveComputeManagementContext(
context,
options?.configTarget,
commandName.replace(/^app /, ""),

Check warning on line 2119 in packages/cli/src/controllers/app.ts

View workflow job for this annotation

GitHub Actions / Lint

lint/performance/useTopLevelRegex

This regex literal is not defined in the top level scope. This can lead to performance issues if this function is called frequently.
);
const branch = resolveDomainBranch(options?.branchName);
if (toBranchKind(branch.name) !== "production") {
Expand Down Expand Up @@ -2258,7 +2249,7 @@
}

function normalizeDomainHostname(hostname: string): string {
const normalized = hostname.trim().replace(/\.$/, "").toLowerCase();

Check warning on line 2252 in packages/cli/src/controllers/app.ts

View workflow job for this annotation

GitHub Actions / Lint

lint/performance/useTopLevelRegex

This regex literal is not defined in the top level scope. This can lead to performance issues if this function is called frequently.
if (!isValidDomainHostname(normalized)) {
throw new CliError({
code: "DOMAIN_HOSTNAME_INVALID",
Expand Down Expand Up @@ -2293,14 +2284,14 @@
}

return labels.every((label) =>
/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(label),

Check warning on line 2287 in packages/cli/src/controllers/app.ts

View workflow job for this annotation

GitHub Actions / Lint

lint/performance/useTopLevelRegex

This regex literal is not defined in the top level scope. This can lead to performance issues if this function is called frequently.
);
}

function sameDomainHostname(left: string, right: string): boolean {
return (
left.trim().replace(/\.$/, "").toLowerCase() ===

Check warning on line 2293 in packages/cli/src/controllers/app.ts

View workflow job for this annotation

GitHub Actions / Lint

lint/performance/useTopLevelRegex

This regex literal is not defined in the top level scope. This can lead to performance issues if this function is called frequently.
right.trim().replace(/\.$/, "").toLowerCase()

Check warning on line 2294 in packages/cli/src/controllers/app.ts

View workflow job for this annotation

GitHub Actions / Lint

lint/performance/useTopLevelRegex

This regex literal is not defined in the top level scope. This can lead to performance issues if this function is called frequently.
);
}

Expand Down Expand Up @@ -2388,7 +2379,7 @@
}
}

function domainCommandError(

Check notice on line 2382 in packages/cli/src/controllers/app.ts

View workflow job for this annotation

GitHub Actions / Lint

lint/complexity/noExcessiveCognitiveComplexity

Excessive complexity of 26 detected (max: 15).
command: AppDomainCommand,
error: unknown,
hostname: string,
Expand Down Expand Up @@ -2525,7 +2516,7 @@
text.includes("no cname") ||
text.includes("cname record") ||
text.includes("no a/aaaa") ||
/\bcname(?:s)?\s+to\b/.test(text)

Check warning on line 2519 in packages/cli/src/controllers/app.ts

View workflow job for this annotation

GitHub Actions / Lint

lint/performance/useTopLevelRegex

This regex literal is not defined in the top level scope. This can lead to performance issues if this function is called frequently.
);
}

Expand Down Expand Up @@ -2554,7 +2545,7 @@

function extractDomainDnsTarget(error: DomainApiError): string | null {
const text = `${error.hint ?? ""} ${error.message}`;
const match = /\b((?:[a-z0-9-]+\.)+prisma\.build)\b/i.exec(text);

Check warning on line 2548 in packages/cli/src/controllers/app.ts

View workflow job for this annotation

GitHub Actions / Lint

lint/performance/useTopLevelRegex

This regex literal is not defined in the top level scope. This can lead to performance issues if this function is called frequently.
return match?.[1]?.toLowerCase() ?? null;
}

Expand Down Expand Up @@ -2590,7 +2581,7 @@
return 0;
}

const match = /^(\d+)(ms|s|m|h)$/.exec(trimmed);

Check warning on line 2584 in packages/cli/src/controllers/app.ts

View workflow job for this annotation

GitHub Actions / Lint

lint/performance/useTopLevelRegex

This regex literal is not defined in the top level scope. This can lead to performance issues if this function is called frequently.
if (!match) {
throw usageError(
`Invalid timeout "${value}"`,
Expand All @@ -2606,11 +2597,11 @@
const multiplier =
unit === "h"
? 60 * 60 * 1000
: unit === "m"
? 60 * 1000
: unit === "s"
? 1000
: 1;

Check notice on line 2604 in packages/cli/src/controllers/app.ts

View workflow job for this annotation

GitHub Actions / Lint

lint/style/noNestedTernary

Do not nest ternary expressions.

Check notice on line 2604 in packages/cli/src/controllers/app.ts

View workflow job for this annotation

GitHub Actions / Lint

lint/style/noNestedTernary

Do not nest ternary expressions.
return amount * multiplier;
}

Expand Down Expand Up @@ -3384,7 +3375,7 @@
listProjects: () =>
listRealWorkspaceProjects(
client,
authState.workspace!,

Check warning on line 3378 in packages/cli/src/controllers/app.ts

View workflow job for this annotation

GitHub Actions / Lint

lint/style/noNonNullAssertion

Forbidden non-null assertion.
context.runtime.signal,
),
commandName: options?.commandName,
Expand Down Expand Up @@ -3418,7 +3409,7 @@
};
}

async function resolveDeployProjectContext(

Check notice on line 3412 in packages/cli/src/controllers/app.ts

View workflow job for this annotation

GitHub Actions / Lint

lint/complexity/noExcessiveCognitiveComplexity

Excessive complexity of 18 detected (max: 15).
context: CommandContext,
client: ManagementApiClient,
provider: ReturnType<typeof createAppProvider>,
Expand Down Expand Up @@ -4093,7 +4084,7 @@
continue;
}

const configFile = await detectFrameworkConfigFile(cwd, framework, signal);

Check notice on line 4087 in packages/cli/src/controllers/app.ts

View workflow job for this annotation

GitHub Actions / Lint

lint/performance/noAwaitInLoops

Avoid using await inside loops.
if (
!configFile.exists &&
!hasAnyPackageDependency(packageJson, framework.detectPackages)
Expand All @@ -4106,8 +4097,8 @@
const annotation =
framework.key === "nextjs" && configFile.standalone
? "standalone output detected"
: configFile.exists
? `detected from ${path.basename(configFile.path!)}`

Check warning on line 4101 in packages/cli/src/controllers/app.ts

View workflow job for this annotation

GitHub Actions / Lint

lint/style/noNonNullAssertion

Forbidden non-null assertion.
: "detected from package.json";

Check notice on line 4102 in packages/cli/src/controllers/app.ts

View workflow job for this annotation

GitHub Actions / Lint

lint/style/noNestedTernary

Do not nest ternary expressions.

return {
Expand All @@ -4130,7 +4121,7 @@
const filePath = path.join(cwd, candidate);
signal.throwIfAborted();
try {
const content = await readFile(filePath, { encoding: "utf8", signal });

Check notice on line 4124 in packages/cli/src/controllers/app.ts

View workflow job for this annotation

GitHub Actions / Lint

lint/performance/noAwaitInLoops

Avoid using await inside loops.
return {
exists: true,
standalone: /\boutput\s*:\s*["'`]standalone["'`]/.test(content),
Expand Down Expand Up @@ -4321,75 +4312,6 @@
);
}

async function maybePromptForAgentSetup(
context: CommandContext,
projectDir: string,
): Promise<string[]> {
if (!canPrompt(context) || context.flags.yes) {
return [];
}

const status = await readPrismaAgentSetupStatus({
cwd: projectDir,
stateStore: context.stateStore,
signal: context.runtime.signal,
requiredSkill: PRISMA_COMPUTE_AGENT_SKILL,
});
if (!shouldOfferPrismaAgentSetup(status)) {
return [];
}

const shouldInstall = await confirmPrompt({
input: context.runtime.stdin,
output: context.runtime.stderr,
signal: context.runtime.signal,
message: "Install the Prisma Compute skill for this project?",
initialValue: true,
});

if (!shouldInstall) {
await context.stateStore.setAgentSetupPromptDismissedAt(
new Date().toISOString(),
);
return [];
}

try {
await runAgentInstall(
context,
{ skill: [PRISMA_COMPUTE_AGENT_SKILL] },
"install",
{ cwd: projectDir },
);
if (!context.flags.quiet && !context.flags.json) {
context.output.stderr.write(
`${renderSummaryLine(context.ui, "success", "Installed the Prisma Compute skill for this project.")}\n\n`,
);
}
return [];
} catch (error) {
const message =
error instanceof Error ? error.message : "Prisma skill install failed.";
if (!context.flags.quiet && !context.flags.json) {
context.output.stderr.write(
`${renderSummaryLine(context.ui, "warning", `Skipped Prisma skill install: ${message}`)}\n\n`,
);
}
const retryCommand = await resolvePrismaCliPackageCommand({
cwd: projectDir,
signal: context.runtime.signal,
args: [
...PRISMA_AGENT_INSTALL_ARGS,
"--skill",
PRISMA_COMPUTE_AGENT_SKILL,
],
});
return [
`The Prisma Compute skill was not installed. Run ${retryCommand} to try again.`,
];
}
}

async function maybeCustomizeDeploySettings(
context: CommandContext,
options: {
Expand Down
5 changes: 5 additions & 0 deletions packages/cli/src/controllers/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import type {
InitSettingRow,
InitTypesState,
} from "../types/init";
import { maybePromptForAgentSetup } from "./agent-setup";
import { detectDeployFramework } from "./app";
import { runProjectLink } from "./project";

Expand Down Expand Up @@ -216,6 +217,7 @@ export async function runInit(
onWarning: (message) => warnings.push(message),
formatCommand,
});
warnings.push(...(await maybePromptForAgentSetup(context, cwd)));

const unlinked = link.status !== "linked" && link.status !== "already-linked";
const typesMissing =
Expand Down Expand Up @@ -600,6 +602,9 @@ async function runInitConversion(
onWarning: (message) => warnings.push(message),
formatCommand,
});
warnings.push(
...(await maybePromptForAgentSetup(stepContext, loaded.configDir)),
);

const unlinked = link.status !== "already-linked" && link.status !== "linked";
const typesMissing =
Expand Down
33 changes: 3 additions & 30 deletions packages/cli/tests/agent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import path from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";

import { createTempCwd, executeCli } from "./helpers";
import { writeSkillsLockWithSkill } from "./helpers/skills-lock";

function expectSkillsCommandPrefix(
command: string[],
Expand Down Expand Up @@ -348,21 +349,7 @@ describe("agent commands", () => {

it("checks required Prisma skills from the skills lock", async () => {
const cwd = await createTempCwd();
await writeFile(
path.join(cwd, "skills-lock.json"),
JSON.stringify({
version: 1,
skills: {
"prisma-client-api": {
source: "prisma/skills",
sourceType: "github",
skillPath: "prisma-client-api/SKILL.md",
computedHash: "test",
},
},
}),
"utf8",
);
await writeSkillsLockWithSkill(cwd, "prisma-client-api");

const { readPrismaAgentSetupStatus } = await import(
"../src/lib/agent/setup-status"
Expand All @@ -380,21 +367,7 @@ describe("agent commands", () => {
}),
).resolves.toMatchObject({ skillsInstalled: false });

await writeFile(
path.join(cwd, "skills-lock.json"),
JSON.stringify({
version: 1,
skills: {
"prisma-compute": {
source: "prisma/skills",
sourceType: "github",
skillPath: "prisma-compute/SKILL.md",
computedHash: "test",
},
},
}),
"utf8",
);
await writeSkillsLockWithSkill(cwd, "prisma-compute");

await expect(
readPrismaAgentSetupStatus({
Expand Down
23 changes: 3 additions & 20 deletions packages/cli/tests/app-controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
createProjectClient,
createResolveBranch,
} from "./helpers/mock-factories";
import { writeSkillsLockWithSkill } from "./helpers/skills-lock";

beforeEach(() => {
process.env.PRISMA_CLI_TEST_REMEMBER_PROJECT_ID = "proj_123";
Expand Down Expand Up @@ -179,24 +180,6 @@ async function writeLocalPin(
);
}

async function writeAgentSetupInstalled(cwd: string): Promise<void> {
await writeFile(
path.join(cwd, "skills-lock.json"),
JSON.stringify({
version: 1,
skills: {
"prisma-compute": {
source: "prisma/skills",
sourceType: "github",
skillPath: "prisma-compute/SKILL.md",
computedHash: "test",
},
},
}),
"utf8",
);
}

async function setupAgentPromptDeployTest(options: {
confirmPrompt: ReturnType<typeof vi.fn>;
deployApp?: ReturnType<typeof vi.fn>;
Expand Down Expand Up @@ -3221,7 +3204,7 @@ describe("app controller", () => {
next: "15.0.0",
},
});
await writeAgentSetupInstalled(cwd);
await writeSkillsLockWithSkill(cwd);
const stateDir = path.join(cwd, ".state");
const { context, stderr } = await createTestCommandContext({
cwd,
Expand Down Expand Up @@ -3528,7 +3511,7 @@ describe("app controller", () => {
await writePackageJson(cwd, {
name: "suggested-name",
});
await writeAgentSetupInstalled(cwd);
await writeSkillsLockWithSkill(cwd);
const stateDir = path.join(cwd, ".state");
const { context, stderr } = await createTestCommandContext({
cwd,
Expand Down
27 changes: 27 additions & 0 deletions packages/cli/tests/helpers/skills-lock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { writeFile } from "node:fs/promises";
import path from "node:path";

/**
* Writes a skills-lock.json recording the given skill as installed from
* prisma/skills, the shape the agent setup status check reads.
*/
export async function writeSkillsLockWithSkill(
cwd: string,
skillName = "prisma-compute",
): Promise<void> {
await writeFile(
path.join(cwd, "skills-lock.json"),
JSON.stringify({
version: 1,
skills: {
[skillName]: {
source: "prisma/skills",
sourceType: "github",
skillPath: `${skillName}/SKILL.md`,
computedHash: "test",
},
},
}),
"utf8",
);
}
Loading
Loading