@prisma/cli 3.0.0-dev.104.1 → 3.0.0-dev.107.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/adapters/local-state.js +14 -3
  2. package/dist/adapters/token-storage.js +1 -1
  3. package/dist/cli2.js +4 -2
  4. package/dist/commands/agent/index.js +60 -0
  5. package/dist/commands/app/index.js +2 -2
  6. package/dist/commands/auth/index.js +1 -1
  7. package/dist/commands/git/index.js +1 -1
  8. package/dist/commands/project/index.js +1 -1
  9. package/dist/controllers/agent.js +228 -0
  10. package/dist/controllers/app.js +59 -9
  11. package/dist/controllers/auth.js +38 -3
  12. package/dist/controllers/project.js +2 -2
  13. package/dist/controllers/select-prompt-port.js +1 -0
  14. package/dist/lib/agent/cli-command.js +17 -0
  15. package/dist/lib/agent/constants.js +12 -0
  16. package/dist/lib/agent/package-manager.js +99 -0
  17. package/dist/lib/agent/setup-status.js +83 -0
  18. package/dist/lib/app/app-provider.js +3 -3
  19. package/dist/lib/app/branch-database-deploy.js +3 -2
  20. package/dist/lib/app/branch-database.js +2 -1
  21. package/dist/lib/app/build-settings.js +1 -1
  22. package/dist/lib/app/build.js +2 -2
  23. package/dist/lib/app/bun-project.js +1 -1
  24. package/dist/lib/app/compute-config.js +1 -1
  25. package/dist/lib/app/env-file.js +1 -1
  26. package/dist/lib/app/local-dev.js +1 -1
  27. package/dist/lib/app/production-deploy-gate.js +2 -1
  28. package/dist/lib/auth/login.js +1 -1
  29. package/dist/lib/git/local-branch.js +1 -1
  30. package/dist/lib/project/interactive-setup.js +1 -0
  31. package/dist/lib/project/local-pin.js +1 -1
  32. package/dist/lib/project/resolution.js +2 -2
  33. package/dist/lib/project/setup.js +1 -1
  34. package/dist/presenters/agent.js +74 -0
  35. package/dist/presenters/auth.js +1 -1
  36. package/dist/presenters/project.js +1 -1
  37. package/dist/shell/cli-command.js +12 -0
  38. package/dist/shell/command-arguments.js +7 -1
  39. package/dist/shell/command-meta.js +71 -0
  40. package/dist/shell/command-runner.js +1 -1
  41. package/dist/shell/help.js +6 -5
  42. package/dist/shell/prompt.js +7 -3
  43. package/dist/shell/runtime.js +2 -2
  44. package/dist/shell/update-check.js +2 -2
  45. package/package.json +3 -2
@@ -0,0 +1,17 @@
1
+ import { formatPrismaCliCommand } from "../../shell/cli-command.js";
2
+ import { resolvePackageRunner, resolvePackageRunnerSync } from "./package-manager.js";
3
+ //#region src/lib/agent/cli-command.ts
4
+ async function resolvePrismaCliPackageCommandFormatter(options) {
5
+ return createPrismaCliPackageCommandFormatter(await resolvePackageRunner(options));
6
+ }
7
+ async function resolvePrismaCliPackageCommand(options) {
8
+ return (await resolvePrismaCliPackageCommandFormatter(options))(options.args);
9
+ }
10
+ function resolvePrismaCliPackageCommandFormatterSync(cwd) {
11
+ return createPrismaCliPackageCommandFormatter(resolvePackageRunnerSync(cwd));
12
+ }
13
+ function createPrismaCliPackageCommandFormatter(packageRunner) {
14
+ return (args) => formatPrismaCliCommand(args, { packageRunner });
15
+ }
16
+ //#endregion
17
+ export { resolvePrismaCliPackageCommand, resolvePrismaCliPackageCommandFormatterSync };
@@ -0,0 +1,12 @@
1
+ //#region src/lib/agent/constants.ts
2
+ const PRISMA_SKILLS_SOURCE = "prisma/skills";
3
+ const PRISMA_SKILLS_LOCK_FILENAME = "skills-lock.json";
4
+ const SKILLS_CLI_PACKAGE = "skills@latest";
5
+ const DEFAULT_PRISMA_AGENT_SKILLS = ["*"];
6
+ const PRISMA_COMPUTE_AGENT_SKILL = "prisma-compute";
7
+ const DEFAULT_PRISMA_AGENT_TARGETS = ["codex", "claude-code"];
8
+ const PRISMA_AGENT_INSTALL_ARGS = ["agent", "install"];
9
+ const PRISMA_AGENT_UPDATE_ARGS = ["agent", "update"];
10
+ const PRISMA_AGENT_STATUS_ARGS = ["agent", "status"];
11
+ //#endregion
12
+ export { DEFAULT_PRISMA_AGENT_SKILLS, DEFAULT_PRISMA_AGENT_TARGETS, PRISMA_AGENT_INSTALL_ARGS, PRISMA_AGENT_STATUS_ARGS, PRISMA_AGENT_UPDATE_ARGS, PRISMA_COMPUTE_AGENT_SKILL, PRISMA_SKILLS_LOCK_FILENAME, PRISMA_SKILLS_SOURCE, SKILLS_CLI_PACKAGE };
@@ -0,0 +1,99 @@
1
+ import { readFileSync, statSync } from "node:fs";
2
+ import path from "node:path";
3
+ //#region src/lib/agent/package-manager.ts
4
+ const LOCKFILE_PACKAGE_MANAGERS = [
5
+ {
6
+ packageManager: "bun",
7
+ fileNames: ["bun.lock", "bun.lockb"]
8
+ },
9
+ {
10
+ packageManager: "pnpm",
11
+ fileNames: ["pnpm-lock.yaml", "pnpm-workspace.yaml"]
12
+ },
13
+ {
14
+ packageManager: "yarn",
15
+ fileNames: ["yarn.lock"]
16
+ },
17
+ {
18
+ packageManager: "npm",
19
+ fileNames: ["package-lock.json", "npm-shrinkwrap.json"]
20
+ }
21
+ ];
22
+ async function resolveSkillsPackageRunner(options) {
23
+ return resolvePackageRunner(options);
24
+ }
25
+ async function resolvePackageRunner(options) {
26
+ options.signal.throwIfAborted();
27
+ const packageManager = detectPackageManagerSync(options.cwd, options.signal) ?? "npm";
28
+ options.signal.throwIfAborted();
29
+ return packageRunnerForPackageManager(packageManager);
30
+ }
31
+ function resolvePackageRunnerSync(cwd) {
32
+ return packageRunnerForPackageManager(detectPackageManagerSync(cwd) ?? "npm");
33
+ }
34
+ function detectPackageManagerSync(cwd, signal) {
35
+ let directory = path.resolve(cwd);
36
+ while (true) {
37
+ signal?.throwIfAborted();
38
+ const packageJsonManager = readPackageJsonPackageManager(directory);
39
+ if (packageJsonManager) return packageJsonManager;
40
+ const lockfileManager = readLockfilePackageManager(directory, signal);
41
+ if (lockfileManager) return lockfileManager;
42
+ const parent = path.dirname(directory);
43
+ if (parent === directory) return null;
44
+ directory = parent;
45
+ }
46
+ }
47
+ function readPackageJsonPackageManager(directory) {
48
+ const packageJsonPath = path.join(directory, "package.json");
49
+ let content;
50
+ try {
51
+ content = readFileSync(packageJsonPath, "utf8");
52
+ } catch (error) {
53
+ if (isMissingFileError(error)) return null;
54
+ throw error;
55
+ }
56
+ try {
57
+ return parsePackageManager(JSON.parse(content).packageManager);
58
+ } catch {
59
+ return null;
60
+ }
61
+ }
62
+ function readLockfilePackageManager(directory, signal) {
63
+ for (const candidate of LOCKFILE_PACKAGE_MANAGERS) for (const fileName of candidate.fileNames) {
64
+ signal?.throwIfAborted();
65
+ if (fileExists(path.join(directory, fileName))) return candidate.packageManager;
66
+ }
67
+ return null;
68
+ }
69
+ function fileExists(filePath) {
70
+ try {
71
+ return statSync(filePath).isFile();
72
+ } catch (error) {
73
+ if (isMissingFileError(error)) return false;
74
+ throw error;
75
+ }
76
+ }
77
+ function parsePackageManager(value) {
78
+ if (typeof value !== "string") return null;
79
+ const normalized = value.trim().toLowerCase();
80
+ if (normalized === "bun" || normalized.startsWith("bun@")) return "bun";
81
+ if (normalized === "pnpm" || normalized.startsWith("pnpm@")) return "pnpm";
82
+ if (normalized === "yarn" || normalized.startsWith("yarn@")) return "yarn";
83
+ if (normalized === "npm" || normalized.startsWith("npm@")) return "npm";
84
+ return null;
85
+ }
86
+ function packageRunnerForPackageManager(packageManager) {
87
+ switch (packageManager) {
88
+ case "bun": return ["bunx"];
89
+ case "pnpm": return ["pnpm", "dlx"];
90
+ case "yarn": return ["yarn", "dlx"];
91
+ case "npm": return ["npx", "-y"];
92
+ }
93
+ }
94
+ function isMissingFileError(error) {
95
+ const code = error.code;
96
+ return code === "ENOENT" || code === "ENOTDIR";
97
+ }
98
+ //#endregion
99
+ export { resolvePackageRunner, resolvePackageRunnerSync, resolveSkillsPackageRunner };
@@ -0,0 +1,83 @@
1
+ import { PRISMA_SKILLS_LOCK_FILENAME } from "./constants.js";
2
+ import path from "node:path";
3
+ import { readFile, stat } from "node:fs/promises";
4
+ import { findComputeConfigDir } from "@prisma/compute-sdk/config";
5
+ //#region src/lib/agent/setup-status.ts
6
+ async function readPrismaAgentSetupStatus(options) {
7
+ const setupCwd = await resolvePrismaAgentSetupCwd(options);
8
+ const skillsLockPath = path.join(setupCwd, PRISMA_SKILLS_LOCK_FILENAME);
9
+ const [skillsInstalled, promptDismissedAt] = await Promise.all([hasPrismaSkillsLock(skillsLockPath, options.signal, options.requiredSkill), options.stateStore?.readAgentSetupPromptDismissedAt() ?? null]);
10
+ return {
11
+ skillsLockPath: path.basename(skillsLockPath),
12
+ skillsInstalled,
13
+ promptDismissedAt
14
+ };
15
+ }
16
+ async function resolvePrismaAgentSetupCwd(options) {
17
+ options.signal.throwIfAborted();
18
+ const configDir = await findComputeConfigDir(options.cwd, options.signal);
19
+ options.signal.throwIfAborted();
20
+ return configDir ?? options.cwd;
21
+ }
22
+ function isPrismaAgentSetupComplete(status) {
23
+ return status.skillsInstalled;
24
+ }
25
+ function shouldOfferPrismaAgentSetup(status) {
26
+ return !isPrismaAgentSetupComplete(status) && !status.promptDismissedAt;
27
+ }
28
+ async function isLikelyProjectDirectory(options) {
29
+ return (await Promise.all([
30
+ "package.json",
31
+ "prisma.compute.ts",
32
+ "prisma.config.ts",
33
+ ".git"
34
+ ].map((fileName) => pathExists(path.join(options.cwd, fileName), options.signal)))).some(Boolean);
35
+ }
36
+ async function hasPrismaSkillsLock(filePath, signal, requiredSkill) {
37
+ try {
38
+ const raw = await readFile(filePath, {
39
+ encoding: "utf8",
40
+ signal
41
+ });
42
+ return hasPrismaSkillsLockEntry(JSON.parse(raw), requiredSkill);
43
+ } catch (error) {
44
+ if (isNotFoundError(error) || error instanceof SyntaxError) return false;
45
+ throw error;
46
+ }
47
+ }
48
+ function hasPrismaSkillsLockEntry(value, requiredSkill) {
49
+ if (!isRecord(value)) return false;
50
+ if (requiredSkill) return skillLockEntryUsesPrismaSource(readSkillLockEntries(value)[requiredSkill]);
51
+ if (readLegacySources(value).includes("prisma/skills")) return true;
52
+ return Object.values(readSkillLockEntries(value)).some(skillLockEntryUsesPrismaSource);
53
+ }
54
+ function readLegacySources(value) {
55
+ return Array.isArray(value.sources) ? value.sources.filter((source) => typeof source === "string") : [];
56
+ }
57
+ function readSkillLockEntries(value) {
58
+ return isRecord(value.skills) ? value.skills : {};
59
+ }
60
+ function skillLockEntryUsesPrismaSource(value) {
61
+ return isRecord(value) && value.source === "prisma/skills";
62
+ }
63
+ function isRecord(value) {
64
+ return typeof value === "object" && value !== null;
65
+ }
66
+ async function pathExists(filePath, signal) {
67
+ signal.throwIfAborted();
68
+ try {
69
+ await stat(filePath);
70
+ signal.throwIfAborted();
71
+ return true;
72
+ } catch (error) {
73
+ if (signal.aborted) throw error;
74
+ if (isNotFoundError(error)) return false;
75
+ throw error;
76
+ }
77
+ }
78
+ function isNotFoundError(error) {
79
+ const code = error.code;
80
+ return code === "ENOENT" || code === "ENOTDIR";
81
+ }
82
+ //#endregion
83
+ export { isLikelyProjectDirectory, readPrismaAgentSetupStatus, resolvePrismaAgentSetupCwd, shouldOfferPrismaAgentSetup };
@@ -404,9 +404,9 @@ async function listComputeServices(client, options) {
404
404
  liveUrl: toAbsoluteUrl(service.appEndpointDomain ?? null)
405
405
  }));
406
406
  }
407
- async function listComputeServiceDomains(client, computeServiceId, signal) {
407
+ async function listComputeServiceDomains(client, appId, signal) {
408
408
  const result = await client.GET("/v1/apps/{appId}/domains", {
409
- params: { path: { appId: computeServiceId } },
409
+ params: { path: { appId } },
410
410
  signal
411
411
  });
412
412
  if (result.error || !result.data) throw domainApiCallError("Failed to list custom domains", result.response, result.error);
@@ -418,7 +418,7 @@ function normalizeDomainRecord(domain) {
418
418
  type: domain.type,
419
419
  url: domain.url,
420
420
  hostname: domain.hostname,
421
- computeServiceId: domain.computeServiceId,
421
+ appId: domain.appId,
422
422
  status: domain.status,
423
423
  foundryStatus: domain.foundryStatus,
424
424
  failureReason: domain.failureReason,
@@ -1,8 +1,8 @@
1
+ import { formatCommandArgument } from "../../shell/command-arguments.js";
1
2
  import { CliError, usageError } from "../../shell/errors.js";
2
- import { confirmPrompt } from "../../shell/prompt.js";
3
3
  import { renderSummaryLine } from "../../shell/ui.js";
4
4
  import { canPrompt } from "../../shell/runtime.js";
5
- import { formatCommandArgument } from "../../shell/command-arguments.js";
5
+ import { confirmPrompt } from "../../shell/prompt.js";
6
6
  import "../project/setup.js";
7
7
  import { hasBranchDatabaseSignal, inspectBranchDatabaseSignal } from "./branch-database.js";
8
8
  import path from "node:path";
@@ -51,6 +51,7 @@ async function maybeSetupBranchDatabase(context, provider, projectId, branch, op
51
51
  if (!await confirmPrompt({
52
52
  input: context.runtime.stdin,
53
53
  output: context.output.stderr,
54
+ signal: context.runtime.signal,
54
55
  message: databasePromptMessage(branch),
55
56
  initialValue: false
56
57
  })) return emptyBranchDatabaseSetupOutcome();
@@ -1,8 +1,9 @@
1
- import { access, readFile, readdir, stat } from "node:fs/promises";
2
1
  import path from "node:path";
2
+ import { access, readFile, readdir, stat } from "node:fs/promises";
3
3
  //#region src/lib/app/branch-database.ts
4
4
  const SKIPPED_DIRECTORIES = new Set([
5
5
  ".git",
6
+ ".agents",
6
7
  ".next",
7
8
  ".nuxt",
8
9
  ".output",
@@ -1,5 +1,5 @@
1
- import { readFile } from "node:fs/promises";
2
1
  import path from "node:path";
2
+ import { readFile } from "node:fs/promises";
3
3
  import { resolveBuildSettings, resolveConfiguredBuildSettings } from "@prisma/compute-sdk";
4
4
  //#region src/lib/app/build-settings.ts
5
5
  /** Legacy build-settings file: no longer read or written, only detected for migration. */
@@ -1,7 +1,7 @@
1
1
  import "./build-settings.js";
2
- import { FRAMEWORKS } from "@prisma/compute-sdk/config";
3
- import "node:fs/promises";
4
2
  import "node:path";
3
+ import "node:fs/promises";
4
+ import { FRAMEWORKS } from "@prisma/compute-sdk/config";
5
5
  import { normalizeArtifactSymlinks, resolveBuildStrategy } from "@prisma/compute-sdk";
6
6
  //#region src/lib/app/build.ts
7
7
  const RESOLVED_APP_BUILD_TYPES = [...frameworkBuildTypesByLastOccurrence()];
@@ -1,5 +1,5 @@
1
- import { access, readFile } from "node:fs/promises";
2
1
  import path from "node:path";
2
+ import { access, readFile } from "node:fs/promises";
3
3
  //#region src/lib/app/bun-project.ts
4
4
  async function readBunPackageJson(appPath, signal) {
5
5
  const packageJsonPath = path.join(appPath, "package.json");
@@ -1,6 +1,6 @@
1
1
  import { CliError } from "../../shell/errors.js";
2
- import { COMPUTE_CONFIG_FILENAME, COMPUTE_CONFIG_FILENAME as COMPUTE_CONFIG_FILENAME$1, ComputeConfigTargetRequiredError, computeTargetAppDir, frameworkByKey, inferComputeTargetFromCwd, loadComputeConfig, selectComputeDeployTarget } from "@prisma/compute-sdk/config";
3
2
  import path from "node:path";
3
+ import { COMPUTE_CONFIG_FILENAME, COMPUTE_CONFIG_FILENAME as COMPUTE_CONFIG_FILENAME$1, ComputeConfigTargetRequiredError, computeTargetAppDir, frameworkByKey, inferComputeTargetFromCwd, loadComputeConfig, selectComputeDeployTarget } from "@prisma/compute-sdk/config";
4
4
  import { matchError } from "better-result";
5
5
  //#region src/lib/app/compute-config.ts
6
6
  /**
@@ -1,7 +1,7 @@
1
1
  import { usageError } from "../../shell/errors.js";
2
2
  import { validateKey } from "./env-config.js";
3
- import { readFile } from "node:fs/promises";
4
3
  import path from "node:path";
4
+ import { readFile } from "node:fs/promises";
5
5
  import { parse } from "dotenv";
6
6
  //#region src/lib/app/env-file.ts
7
7
  const ASSIGNMENT_KEY_PATTERN = /^\s*(?:export\s+)?([^#=\s]+)\s*=/;
@@ -1,6 +1,6 @@
1
1
  import { resolveBunEntrypoint } from "./bun-project.js";
2
- import "node:fs/promises";
3
2
  import path from "node:path";
3
+ import "node:fs/promises";
4
4
  import { spawn } from "node:child_process";
5
5
  //#region src/lib/app/local-dev.ts
6
6
  const DEFAULT_LOCAL_DEV_PORT = 3e3;
@@ -1,6 +1,6 @@
1
1
  import { CliError } from "../../shell/errors.js";
2
- import { confirmPrompt } from "../../shell/prompt.js";
3
2
  import { canPrompt } from "../../shell/runtime.js";
3
+ import { confirmPrompt } from "../../shell/prompt.js";
4
4
  //#region src/lib/app/production-deploy-gate.ts
5
5
  async function enforceProductionDeployGate(context, provider, options) {
6
6
  if (options.branchKind !== "production") return { firstProductionDeploy: false };
@@ -25,6 +25,7 @@ async function enforceProductionDeployGate(context, provider, options) {
25
25
  if (!await confirmPrompt({
26
26
  input: context.runtime.stdin,
27
27
  output: context.output.stderr,
28
+ signal: context.runtime.signal,
28
29
  message: "Deploy to production?",
29
30
  initialValue: false
30
31
  })) throw productionDeployCancelledError();
@@ -1,7 +1,7 @@
1
1
  import { getApiBaseUrl } from "./client.js";
2
2
  import { FileTokenStorage } from "../../adapters/token-storage.js";
3
- import open from "open";
4
3
  import { AuthError, createManagementApiSdk } from "@prisma/management-api-sdk";
4
+ import open from "open";
5
5
  import events from "node:events";
6
6
  import http from "node:http";
7
7
  import readline from "node:readline/promises";
@@ -1,5 +1,5 @@
1
- import { access, readFile } from "node:fs/promises";
2
1
  import path from "node:path";
2
+ import { access, readFile } from "node:fs/promises";
3
3
  //#region src/lib/git/local-branch.ts
4
4
  /**
5
5
  * Resolves the checked-out branch the way git does: the nearest `.git`
@@ -40,6 +40,7 @@ async function promptForProjectSetupChoice(options) {
40
40
  const rawName = await textPrompt({
41
41
  input: options.context.runtime.stdin,
42
42
  output: options.context.runtime.stderr,
43
+ signal: options.context.runtime.signal,
43
44
  message: "Project name",
44
45
  placeholder: suggestedName.name,
45
46
  validate: (value) => validateProjectSetupNameText(value, suggestedName.name)
@@ -1,5 +1,5 @@
1
- import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
2
1
  import path from "node:path";
2
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
3
3
  import { Result, TaggedError, UnhandledException } from "better-result";
4
4
  //#region src/lib/project/local-pin.ts
5
5
  const LOCAL_RESOLUTION_PIN_RELATIVE_PATH = ".prisma/local.json";
@@ -1,8 +1,8 @@
1
+ import { formatCommandArgument } from "../../shell/command-arguments.js";
1
2
  import { CliError } from "../../shell/errors.js";
2
3
  import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "./local-pin.js";
3
- import { formatCommandArgument } from "../../shell/command-arguments.js";
4
- import { readFile } from "node:fs/promises";
5
4
  import path from "node:path";
5
+ import { readFile } from "node:fs/promises";
6
6
  import { Result, TaggedError, matchError } from "better-result";
7
7
  //#region src/lib/project/resolution.ts
8
8
  var ProjectNotFoundError = class extends TaggedError("ProjectNotFoundError")() {
@@ -1,7 +1,7 @@
1
+ import "../../shell/command-arguments.js";
1
2
  import { CliError, usageError } from "../../shell/errors.js";
2
3
  import { shortenHomePath } from "../fs/home-path.js";
3
4
  import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, ensureLocalResolutionPinGitignore, writeLocalResolutionPin } from "./local-pin.js";
4
- import "../../shell/command-arguments.js";
5
5
  import { projectAmbiguousError, projectNotFoundError } from "./resolution.js";
6
6
  import path from "node:path";
7
7
  import { Result, matchError } from "better-result";
@@ -0,0 +1,74 @@
1
+ import { formatShellCommand } from "../shell/command-arguments.js";
2
+ import { formatDescriptorLabel } from "../shell/command-meta.js";
3
+ import { renderSummaryLine } from "../shell/ui.js";
4
+ //#region src/presenters/agent.ts
5
+ function renderAgentInstall(context, descriptor, result) {
6
+ const ui = context.ui;
7
+ const rail = ui.dim("│");
8
+ return [
9
+ renderSummaryLine(ui, "success", `${formatDescriptorLabel(descriptor)} → ${operationSummary(result)} Prisma skills.`),
10
+ "",
11
+ `${rail} ${ui.accent("skills:")} ${result.skills.status.replace("-", " ")}`,
12
+ `${rail} ${ui.dim(formatShellCommand(result.skills.command))}`
13
+ ];
14
+ }
15
+ function renderAgentStatus(context, descriptor, result) {
16
+ const ui = context.ui;
17
+ const rail = ui.dim("│");
18
+ return [
19
+ `${ui.strong(formatDescriptorLabel(descriptor))} ${ui.dim("→")} ${ui.dim(`Checking ${result.statusScope} Prisma skills.`)}`,
20
+ "",
21
+ `${rail} ${ui.accent("skills:")} ${result.skillsInstalled ? "installed" : "not found"}`,
22
+ ...renderInstalledSkills(context, result.skills),
23
+ `${rail}`,
24
+ `${rail} ${ui.accent("source:")} ${formatStatusSource(result)}`,
25
+ `${rail} ${ui.dim(formatShellCommand(result.skillsListCommand))}`,
26
+ ...renderProjectStatusDetails(context, result)
27
+ ];
28
+ }
29
+ function serializeAgentInstall(result) {
30
+ return result;
31
+ }
32
+ function serializeAgentStatus(result) {
33
+ return result;
34
+ }
35
+ function formatSetupPromptStatus(result) {
36
+ if (result.skillsInstalled) return "not needed";
37
+ if (result.promptDismissedAt) return `dismissed ${result.promptDismissedAt}`;
38
+ return "active";
39
+ }
40
+ function formatStatusSource(result) {
41
+ if (result.statusSource === "skills-cli") return result.statusScope === "global" ? "skills list -g --json" : "skills list --json";
42
+ if (result.statusSource === "skills-lock") return result.skillsLockPath;
43
+ return "unavailable";
44
+ }
45
+ function renderProjectStatusDetails(context, result) {
46
+ if (result.statusScope !== "project") return [];
47
+ const ui = context.ui;
48
+ const rail = ui.dim("│");
49
+ return [
50
+ `${rail}`,
51
+ `${rail} ${ui.accent("skills lock:")} ${result.skillsLockInstalled ? "installed" : "not found"}`,
52
+ `${rail} ${ui.dim(result.skillsLockPath)}`,
53
+ `${rail}`,
54
+ `${rail} ${ui.accent("setup prompt:")} ${formatSetupPromptStatus(result)}`
55
+ ];
56
+ }
57
+ function renderInstalledSkills(context, skills) {
58
+ const ui = context.ui;
59
+ const rail = ui.dim("│");
60
+ if (skills.length === 0) return [`${rail} ${ui.dim("No Prisma skills reported.")}`];
61
+ return skills.map((skill) => {
62
+ const agents = skill.agents.length > 0 ? skill.agents.join(", ") : "no agents reported";
63
+ return `${rail} ${skill.name} ${ui.dim(`${skill.scope}; ${agents}`)}`;
64
+ });
65
+ }
66
+ function operationSummary(result) {
67
+ if (result.skills.status === "would-install") return "Would install";
68
+ return operationLabel(result.operation);
69
+ }
70
+ function operationLabel(operation) {
71
+ return operation === "update" ? "Updated" : "Installed";
72
+ }
73
+ //#endregion
74
+ export { renderAgentInstall, renderAgentStatus, serializeAgentInstall, serializeAgentStatus };
@@ -25,7 +25,7 @@ function renderAuthSuccess(context, descriptor, command, result) {
25
25
  context: rows,
26
26
  operationDescription: "Applying authentication session changes",
27
27
  operationCount: 1,
28
- details: ["Session stored in local CLI state."]
28
+ details: ["Session stored in local CLI state.", ...result.agentSetupTip ? [`Install Prisma skills for this project with ${result.agentSetupTip.command}.`] : []]
29
29
  }, context.ui);
30
30
  }
31
31
  if (command === "auth.logout") return renderMutate({
@@ -1,7 +1,7 @@
1
+ import { formatCommandArgument } from "../shell/command-arguments.js";
1
2
  import { formatDescriptorLabel } from "../shell/command-meta.js";
2
3
  import { padDisplay, renderNextSteps, renderSummaryLine, renderVerboseBlock } from "../shell/ui.js";
3
4
  import { shortenHomePath } from "../lib/fs/home-path.js";
4
- import { formatCommandArgument } from "../shell/command-arguments.js";
5
5
  import { renderMutate, renderShow, serializeList } from "../output/patterns.js";
6
6
  import { renderResolvedProjectContextBlock } from "./verbose-context.js";
7
7
  import stringWidth from "string-width";
@@ -0,0 +1,12 @@
1
+ const PRISMA_CLI_PACKAGE_SPEC = `@prisma/cli@latest`;
2
+ const DEFAULT_PRISMA_CLI_PACKAGE_RUNNER = ["npx", "-y"];
3
+ const PRISMA_CLI_BINARY = "prisma-cli";
4
+ function formatPrismaCliCommand(args, options = {}) {
5
+ return [...getPrismaCliCommandPrefix(options), ...args].join(" ");
6
+ }
7
+ function getPrismaCliCommandPrefix({ invocation = "package", packageRunner = DEFAULT_PRISMA_CLI_PACKAGE_RUNNER }) {
8
+ if (invocation === "binary") return [PRISMA_CLI_BINARY];
9
+ return [...packageRunner, PRISMA_CLI_PACKAGE_SPEC];
10
+ }
11
+ //#endregion
12
+ export { formatPrismaCliCommand };
@@ -2,5 +2,11 @@
2
2
  function formatCommandArgument(value) {
3
3
  return /^[A-Za-z0-9._/-]+$/.test(value) && !value.startsWith("-") ? value : `'${value.replace(/'/g, "'\\''")}'`;
4
4
  }
5
+ function formatShellCommand(command) {
6
+ return command.map(formatShellCommandWord).join(" ");
7
+ }
8
+ function formatShellCommandWord(value) {
9
+ return /^[A-Za-z0-9_./:@=-]+$/.test(value) ? value : `'${value.replace(/'/g, "'\\''")}'`;
10
+ }
5
11
  //#endregion
6
- export { formatCommandArgument };
12
+ export { formatCommandArgument, formatShellCommand };
@@ -1,5 +1,11 @@
1
+ import { resolvePrismaCliPackageCommandFormatterSync } from "../lib/agent/cli-command.js";
2
+ import { PRISMA_AGENT_INSTALL_ARGS, PRISMA_AGENT_STATUS_ARGS, PRISMA_AGENT_UPDATE_ARGS, PRISMA_COMPUTE_AGENT_SKILL } from "../lib/agent/constants.js";
1
3
  //#region src/shell/command-meta.ts
2
4
  const COMMAND_DESCRIPTOR_ID = Symbol("prisma.commandDescriptorId");
5
+ function agentCommandExamples(runtime, commands) {
6
+ const formatCommand = resolvePrismaCliPackageCommandFormatterSync(runtime.cwd);
7
+ return commands.map((command) => formatCommand(command));
8
+ }
3
9
  const DESCRIPTORS = [
4
10
  {
5
11
  id: "root",
@@ -14,6 +20,71 @@ const DESCRIPTORS = [
14
20
  description: "Show CLI build and environment",
15
21
  examples: ["prisma-cli version", "prisma-cli version --json"]
16
22
  },
23
+ {
24
+ id: "agent",
25
+ path: ["prisma", "agent"],
26
+ description: "Install Prisma context for AI coding agents",
27
+ examples: (runtime) => agentCommandExamples(runtime, [
28
+ PRISMA_AGENT_INSTALL_ARGS,
29
+ PRISMA_AGENT_UPDATE_ARGS,
30
+ PRISMA_AGENT_STATUS_ARGS
31
+ ])
32
+ },
33
+ {
34
+ id: "agent.install",
35
+ path: [
36
+ "prisma",
37
+ "agent",
38
+ "install"
39
+ ],
40
+ description: "Install Prisma skills for AI coding agents",
41
+ examples: (runtime) => agentCommandExamples(runtime, [
42
+ PRISMA_AGENT_INSTALL_ARGS,
43
+ [
44
+ ...PRISMA_AGENT_INSTALL_ARGS,
45
+ "--agent",
46
+ "codex"
47
+ ],
48
+ [...PRISMA_AGENT_INSTALL_ARGS, "--all-agents"],
49
+ [
50
+ ...PRISMA_AGENT_INSTALL_ARGS,
51
+ "--skill",
52
+ PRISMA_COMPUTE_AGENT_SKILL
53
+ ]
54
+ ])
55
+ },
56
+ {
57
+ id: "agent.update",
58
+ path: [
59
+ "prisma",
60
+ "agent",
61
+ "update"
62
+ ],
63
+ description: "Refresh Prisma skills for AI coding agents",
64
+ examples: (runtime) => agentCommandExamples(runtime, [
65
+ PRISMA_AGENT_UPDATE_ARGS,
66
+ [
67
+ ...PRISMA_AGENT_UPDATE_ARGS,
68
+ "--agent",
69
+ "codex"
70
+ ],
71
+ [...PRISMA_AGENT_UPDATE_ARGS, "--all-agents"]
72
+ ])
73
+ },
74
+ {
75
+ id: "agent.status",
76
+ path: [
77
+ "prisma",
78
+ "agent",
79
+ "status"
80
+ ],
81
+ description: "Show installed Prisma skills",
82
+ examples: (runtime) => agentCommandExamples(runtime, [
83
+ PRISMA_AGENT_STATUS_ARGS,
84
+ [...PRISMA_AGENT_STATUS_ARGS, "--json"],
85
+ [...PRISMA_AGENT_STATUS_ARGS, "--global"]
86
+ ])
87
+ },
17
88
  {
18
89
  id: "auth",
19
90
  path: ["prisma", "auth"],
@@ -2,9 +2,9 @@ import { CliError, authConfigInvalidError, authRequiredError, commandCanceledErr
2
2
  import { getCommandDescriptor } from "./command-meta.js";
3
3
  import { resolveGlobalFlags } from "./global-flags.js";
4
4
  import { createCommandContext } from "./runtime.js";
5
- import { cliErrorToJson, writeHumanError, writeHumanLines, writeJsonError, writeJsonEvent, writeJsonSuccess } from "./output.js";
6
5
  import { collectCommandDiagnostics } from "../lib/diagnostics.js";
7
6
  import { renderCommandDiagnostics } from "./diagnostics-output.js";
7
+ import { cliErrorToJson, writeHumanError, writeHumanLines, writeJsonError, writeJsonEvent, writeJsonSuccess } from "./output.js";
8
8
  import { AuthError } from "@prisma/management-api-sdk";
9
9
  //#region src/shell/command-runner.ts
10
10
  function toCliError(error, runtime) {
@@ -1,6 +1,6 @@
1
1
  import { formatDescriptorLabel, getDescriptorForCommand } from "./command-meta.js";
2
- import { COMPACT_GLOBAL_OPTION_FLAGS, resolveGlobalFlags } from "./global-flags.js";
3
2
  import { createShellUi, padDisplay, wrapText } from "./ui.js";
3
+ import { COMPACT_GLOBAL_OPTION_FLAGS, resolveGlobalFlags } from "./global-flags.js";
4
4
  //#region src/shell/help.ts
5
5
  function renderHelp(command, runtime) {
6
6
  const descriptor = getDescriptorForCommand(command);
@@ -12,7 +12,7 @@ function renderHelp(command, runtime) {
12
12
  if (visibleCommands.length > 0) lines.push(...renderCommandRows(rail, ui, visibleCommands));
13
13
  lines.push(...renderLongDescription(rail, ui, descriptor.longDescription));
14
14
  lines.push(...renderVisibleOptions(rail, ui, visibleCommands, visibleOptions));
15
- lines.push(...renderExamples(rail, descriptor.examples));
15
+ lines.push(...renderExamples(rail, runtime, descriptor.examples));
16
16
  lines.push(...renderDocsPath(rail, ui, descriptor.docsPath));
17
17
  lines.push("");
18
18
  return `${lines.join("\n")}`;
@@ -30,12 +30,13 @@ function renderVisibleOptions(rail, ui, visibleCommands, visibleOptions) {
30
30
  function shouldLabelGlobalOptions(visibleCommands, visibleOptions) {
31
31
  return visibleCommands.length > 0 && visibleOptions.every((option) => COMPACT_GLOBAL_OPTION_FLAGS.includes(option.flags));
32
32
  }
33
- function renderExamples(rail, examples) {
34
- if (!examples || examples.length === 0) return [];
33
+ function renderExamples(rail, runtime, examples) {
34
+ const resolvedExamples = typeof examples === "function" ? examples(runtime) : examples;
35
+ if (!resolvedExamples || resolvedExamples.length === 0) return [];
35
36
  return [
36
37
  `${rail}`,
37
38
  `${rail} Examples:`,
38
- ...examples.map((example) => `${rail} $ ${example}`)
39
+ ...resolvedExamples.map((example) => `${rail} $ ${example}`)
39
40
  ];
40
41
  }
41
42
  function renderDocsPath(rail, ui, docsPath) {