diff --git a/AGENTS.md b/AGENTS.md index 6002d150..91b3021f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -31,6 +31,10 @@ Unit tests may still mock, and should — error paths and edge cases belong ther Why this rule exists: `prisma project list` reported "No projects found." and exited 0 for a workspace holding 15 projects, and every project-scoped command was broken with it. The unit suite covered that command thoroughly and passed throughout, because its fixtures supplied both sides of every comparison — the credential's workspace id and the API's were the same hand-written string, while the real API returns a `wksp_` prefix that the credential does not carry. A test that writes both sides of a comparison can only confirm what its author already believed. Mocks are still the right tool for error paths and edge cases; they cannot tell you what the API actually returns. +## Help Text + +Hold every new or changed command's help (summary, description, flag briefs, group brief) to `docs/product/cli-help-standard.md`. Write for a reader who has never used Prisma: each summary stands alone, defines any Prisma term in place (usually by stating the consequence), and never uses internal vocabulary such as "binding", "resolved", or "pinned". Card structure and formatting rules are in `docs/product/cli-style-guide.md`. + ## Pre-Commit Verification - `pnpm typecheck` diff --git a/docs/product/cli-help-standard.md b/docs/product/cli-help-standard.md new file mode 100644 index 00000000..56dbbab8 --- /dev/null +++ b/docs/product/cli-help-standard.md @@ -0,0 +1,37 @@ +# CLI Help Standard + +## Purpose + +This document defines the standard every command's `--help` text must meet, and why the bar is set where it is. The formatting and card-structure rules live in `cli-style-guide.md`; this document is the contract for the words. + +## Why Help Is a Manual, Not a Summary + +The CLI's help is the one interface every consumer is guaranteed to have. Documentation sites go stale or unread, and agent skills may not be installed. `--help` ships with the binary, matches the installed version exactly, and is the first thing both a new user and an AI agent reach for. So the CLI must be self-documenting: help text carries enough of the product model that a reader can act correctly without any other source. + +The reader to write for is an AI coding agent that has never heard of Prisma. That reader is also the strictest proxy for a new human user. It does not know what a workspace, Project, Branch, contract, or skill is. It cannot infer that "linking" stores a local pointer, or that a "version" is immutable and one serves traffic. Every term it cannot resolve from the card in front of it costs a round trip: a doc lookup, a wrong invocation, a retried command. That churn is the thing this standard exists to eliminate. When help defines its own terms, an agent reads one card and issues the right command; when it does not, the agent guesses. + +The same rule protects against a quieter failure: help that paraphrases the command name. "prisma deploy — Deploy" tells the reader nothing the invocation did not. Text earns its place by adding intent (when to run this, what it operates on, what happens next), never by restating the grammar. + +## The Standard + +Definitions of each surface: a group's *brief* is the one-line text on its parent card; its *description* is the prose on its own card. A command's *summary* is its row on the group card; its *description* is the prose on its own card. + +1. Every row stands alone. A summary must be understandable by a reader who will never open the leaf card. If it depends on a term the reader cannot know, define the term in place, usually by stating the consequence: "Link this directory to a Project: commands run here target it by default". +2. Group briefs are a short lead, then scope. Name what lives beneath: "Manage S3-compatible object-store buckets for a project. CRUD operations and access keys". "CRUD" may stand in for the common verbs; operations a reader would not guess (link, transfer, promote) are named. +3. Group descriptions define every term their rows and flags rely on: what the resource is, what it belongs to, and the lifecycle words the subcommands use. If a row says "linked", "version", or "scope", the group card says what that means. +4. Commands that are self-describing stay short. "project list — List all projects in your workspace" needs no description. Add prose only where it carries intent the invocation does not. +5. Flag briefs state what the flag does and, when it exists for a distinct situation, when to reach for it. Defaults are spelled out in plain language: "(default: the project this directory is linked to)", never "(default: the resolved project)". +6. Internal vocabulary never leaks. "Binding", "resolved", "pinned", and "active" are implementation terms; help uses the plain phrase or defines the word in the same card. +7. Concepts are defined at first use, in one clause. A Project groups one product or codebase inside a workspace. A Branch maps to a Git branch and is an isolated environment with its own services, databases, and buckets. A service version is one immutable deploy; one serves traffic at a time. +8. Groups with a common multi-command path declare a Workflow section: ordered, copy-pastable steps whose purpose column states action and consequence, standing alone like any row. +9. Current product names only. Prisma ORM is called Prisma ORM; retired names such as "Prisma Next" never appear. + +## What This Buys + +1. Self-documenting: the installed binary is the reference for its own version; no doc site round trip. +2. Agent-ready without skills: an agent with no Prisma skill installed can go from `prisma --help` to a correct, safe invocation by reading cards, because each card teaches the model it needs. +3. Less churn for everyone: fewer wrong invocations, fewer retries, fewer support questions that are really vocabulary questions. + +## Enforcement + +New and changed commands must meet this standard before they merge; review help text against it the way behavior is reviewed against the product docs. The repository `AGENTS.md` binds agents working in this repo to the same rule. diff --git a/docs/product/cli-style-guide.md b/docs/product/cli-style-guide.md index d747b20d..ba11f880 100644 --- a/docs/product/cli-style-guide.md +++ b/docs/product/cli-style-guide.md @@ -119,6 +119,10 @@ Rules: ## Help and Usage +The wording contract for help text, and the reasoning behind it, is +`cli-help-standard.md`. This section covers the card structure and +formatting. + Help should feel like the rest of the CLI: - concise @@ -140,6 +144,26 @@ Help output should: Unknown commands should show "Did you mean ..." suggestions when there is a clear close match. +### Descriptions Describe Intent + +Help text is read by people and by agents that have never seen the platform's resource model. Help is a manual, not a summary. Write for both: + +- The summary states what the command does. When the command name already says it, the summary stays a plain restatement (`project list` → "List the projects in your workspace") and no description is added. +- A description earns its place by adding intent: when to run the command, what it operates on, and what happens next. It must not paraphrase the command name. +- A group's brief is a short lead sentence, then the scope of what lives beneath it ("Manage S3-compatible object-store buckets for a project. CRUD operations and access keys"). "CRUD" may stand in for the common verbs; operations a reader would not guess (link, transfer, promote) are named. +- A group's description defines every term its command rows rely on. If a row says "linked", the group card says what linking is before the reader opens the leaf. +- Do not assume the reader knows Prisma nouns. The first time a group or command depends on one, define it in one clause: a Project groups one product or codebase inside a workspace; a Branch maps to a Git branch and is an isolated environment with its own services, databases, and buckets. +- Internal resolution terms stay out of help: no "binding", "resolved", "pinned", or "active" without a plain-language definition in the same card. Prefer the plain phrase outright ("the project this directory is linked to" over "the resolved project"). +- Refer to Prisma ORM by that name. Do not use retired product names such as "Prisma Next" in help text. + +### Flag Briefs Say When + +A flag brief states what the flag does; when the flag exists for a distinct situation, it also says when to reach for it ("--branch: target a preview branch instead of the default branch"). Defaults render as an automatic suffix, so briefs do not repeat them. + +### Workflow Sections + +A group card may declare a workflow: the ordered commands of that group's common path, each with a short purpose column. The engine renders it as a `Workflow` section, before any examples, with each step `$`-prefixed and copy-pastable. Declare a workflow only where a real multi-command path exists; a group of independent commands has no workflow. + ## Flags Shared flag rules: diff --git a/packages/cli-engine/src/cli.ts b/packages/cli-engine/src/cli.ts index 66a7152d..5b44239b 100644 --- a/packages/cli-engine/src/cli.ts +++ b/packages/cli-engine/src/cli.ts @@ -1,4 +1,5 @@ import type { CommandFamily, MountedTree } from "./command-family"; +import type { WorkflowStep } from "./commands"; import { buildEngine } from "./execution/engine"; import type { RunSummary } from "./run-summary"; import type { Runtime } from "./runtime"; @@ -41,13 +42,23 @@ export function createCli(spec: { readonly version: string; readonly commandFamilies: readonly CommandFamily[]; readonly groups: Readonly< - Record + Record< + string, + { + readonly brief: string; + readonly description?: string; + /** The group's common path, rendered as a `Workflow` section. */ + readonly workflow?: readonly WorkflowStep[]; + } + > >; readonly commands: MountedTree; /** Words for the root help card; the engine formats. */ readonly help?: { readonly tagline?: string; readonly description?: string; + /** The CLI's common path, rendered as a `Workflow` section. */ + readonly workflow?: readonly WorkflowStep[]; readonly examples?: readonly string[]; readonly docsUrl?: string; }; diff --git a/packages/cli-engine/src/commands.ts b/packages/cli-engine/src/commands.ts index 06a0fe02..a5fc412e 100644 --- a/packages/cli-engine/src/commands.ts +++ b/packages/cli-engine/src/commands.ts @@ -30,6 +30,17 @@ import type { ChildStatusSettlement } from "./spawn"; * undefined instead of conditional properties. */ +/** + * One step of a group's common path, rendered under a `Workflow` + * heading on the group's help card. `run` follows the example + * convention: no binary name, `{bin}` substituted at render time. + */ +export interface WorkflowStep { + readonly run: string; + /** Short purpose column shown beside the invocation. */ + readonly brief: string; +} + /** The help SPI: words only — the engine formats. */ export interface HelpSpec { /** One line, imperative, shown in listings. */ diff --git a/packages/cli-engine/src/execution/engine.ts b/packages/cli-engine/src/execution/engine.ts index 32a5a424..5491496f 100644 --- a/packages/cli-engine/src/execution/engine.ts +++ b/packages/cli-engine/src/execution/engine.ts @@ -9,7 +9,7 @@ import type { CommandRedirect, MountedTree, } from "../command-family"; -import type { AnyCommand } from "../commands"; +import type { AnyCommand, WorkflowStep } from "../commands"; import type { CommandContext } from "../context"; import type { ActiveCredential } from "../credential-manager"; import type { EngineEvent, Severity, StreamEvent } from "../events"; @@ -83,7 +83,15 @@ export interface EngineSpec { readonly version: string; readonly commandFamilies: readonly CommandFamily[]; readonly groups: Readonly< - Record + Record< + string, + { + readonly brief: string; + readonly description?: string; + /** The group's common path, rendered as a `Workflow` section. */ + readonly workflow?: readonly WorkflowStep[]; + } + > >; readonly commands: MountedTree; /** Words for the root help card; the engine formats. */ @@ -92,6 +100,8 @@ export interface EngineSpec { readonly tagline?: string; /** A sentence or two under the command list. */ readonly description?: string; + /** The CLI's common path, rendered as a `Workflow` section. */ + readonly workflow?: readonly WorkflowStep[]; /** Same {bin} substitution rule as command examples. */ readonly examples?: readonly string[]; readonly docsUrl?: string; diff --git a/packages/cli-engine/src/execution/help.ts b/packages/cli-engine/src/execution/help.ts index f42f9d52..b4b45337 100644 --- a/packages/cli-engine/src/execution/help.ts +++ b/packages/cli-engine/src/execution/help.ts @@ -12,7 +12,7 @@ import { type PositionalSpec, positionalRuntime, } from "../args"; -import type { AnyCommand } from "../commands"; +import type { AnyCommand, WorkflowStep } from "../commands"; import type { CommandTreeEntry, CommandTreeNode } from "./command-tree"; import type { EngineSpec } from "./engine"; import { makePaint, type Paint, textWidth } from "./palette"; @@ -243,6 +243,35 @@ function exampleLines( } } +/** The group's common path: `$`-prefixed copy-pastable steps in mount + * order, purpose column muted, aligned like every other row block. */ +function workflowLines( + workflow: readonly WorkflowStep[] | undefined, + cliName: string, + paint: Paint, + lines: string[], +): void { + if (workflow === undefined || workflow.length === 0) { + return; + } + lines.push(rail(paint)); + lines.push(sectionLabel(paint, "Workflow")); + const steps = workflow.map((step) => ({ + run: resolveExample(step.run, cliName), + brief: step.brief, + })); + const width = Math.max(...steps.map((step) => textWidth(step.run))); + for (const step of steps) { + const pad = " ".repeat(width - textWidth(step.run)); + lines.push( + rail( + paint, + `${GAP}${paint("muted", "$")} ${step.run}${pad}${GAP}${paint("muted", step.brief)}`, + ), + ); + } +} + function docsLine( url: string | undefined, paint: Paint, @@ -429,6 +458,11 @@ function renderNodeHelp( proseLines(description, paint, lines); } + const workflow = atRoot + ? spec.help?.workflow + : spec.groups[groupPath]?.workflow; + workflowLines(workflow, spec.name, paint, lines); + if (atRoot) { lines.push(rail(paint)); lines.push(sectionLabel(paint, "Global options")); diff --git a/packages/cli-engine/src/exports/index.ts b/packages/cli-engine/src/exports/index.ts index 4e6e8375..40745f67 100644 --- a/packages/cli-engine/src/exports/index.ts +++ b/packages/cli-engine/src/exports/index.ts @@ -43,6 +43,7 @@ export { type ServerCommandDefinition, type SessionCommandDefinition, type SpawnDeclarations, + type WorkflowStep, } from "../commands"; export { defineConfig, diff --git a/packages/cli-engine/src/telemetry/commands.ts b/packages/cli-engine/src/telemetry/commands.ts index ae433402..86459f52 100644 --- a/packages/cli-engine/src/telemetry/commands.ts +++ b/packages/cli-engine/src/telemetry/commands.ts @@ -118,10 +118,7 @@ export const telemetryStatusCommand = defineCommand({ help: { summary: "Show whether anonymous CLI telemetry is enabled and why", description: - "Reports whether telemetry is currently enabled or disabled and the reason\n" + - "(default-on, stored opt-out, environment opt-out, or CI), the path to your\n" + - "user-level config file, and whether an installation ID has been stored.\n" + - "Read-only: never sends an event, never mints an ID, never writes anything.", + "Reports whether telemetry is currently enabled or disabled and the reason (default-on, stored opt-out, environment opt-out, or CI), the path to your user-level config file, and whether an installation ID has been stored. Read-only: never sends an event, never mints an ID, never writes anything.", examples: ["telemetry status", "telemetry status --json"], }, handler: async (_args, ctx) => { @@ -142,8 +139,7 @@ export const telemetryEnableCommand = defineCommand({ help: { summary: "Enable anonymous CLI telemetry", description: - 'Stores "enableTelemetry": true in your user-level config and mints an\n' + - "installation ID if one is not already stored.", + 'Stores "enableTelemetry": true in your user-level config and mints an installation ID if one is not already stored.', examples: ["telemetry enable"], }, handler: async (_args, ctx) => { @@ -169,8 +165,7 @@ export const telemetryDisableCommand = defineCommand({ help: { summary: "Disable anonymous CLI telemetry", description: - 'Stores "enableTelemetry": false in your user-level config. No installation\n' + - "ID is minted and no event is sent.", + 'Stores "enableTelemetry": false in your user-level config. No installation ID is minted and no event is sent.', examples: ["telemetry disable"], }, handler: async (_args, ctx) => { diff --git a/packages/cli-engine/src/testing.ts b/packages/cli-engine/src/testing.ts index 4f73671c..fe4dd8e9 100644 --- a/packages/cli-engine/src/testing.ts +++ b/packages/cli-engine/src/testing.ts @@ -1,4 +1,5 @@ import type { CommandFamily, MountedTree } from "./command-family"; +import type { WorkflowStep } from "./commands"; import { CONFIG_FILE_NAME } from "./config-loader"; import type { Credential } from "./credential-manager"; import type { EngineEvent, StreamEvent } from "./events"; @@ -206,7 +207,24 @@ function inputStreamFromString(text: string) { export function createTestCli(spec: { readonly commandFamilies?: readonly CommandFamily[]; readonly commands: MountedTree; - readonly groups?: Readonly>; + readonly groups?: Readonly< + Record< + string, + { + readonly brief: string; + readonly description?: string; + readonly workflow?: readonly WorkflowStep[]; + } + > + >; + /** Words for the root help card, exactly as `createCli` takes them. */ + readonly help?: { + readonly tagline?: string; + readonly description?: string; + readonly workflow?: readonly WorkflowStep[]; + readonly examples?: readonly string[]; + readonly docsUrl?: string; + }; /** Seeds the sections the config file would have held. The engine * asks for them only when the command declares a config section, and * checks them as it would a real file's: a section name no mounted @@ -313,6 +331,7 @@ export function createTestCli(spec: { commandFamilies: spec.commandFamilies ?? [], groups: spec.groups ?? {}, commands: spec.commands, + help: spec.help, telemetry: spec.telemetry, }, /** Waiting is instant under test: browserWait's polling is driven diff --git a/packages/cli-engine/tests/execution.test.ts b/packages/cli-engine/tests/execution.test.ts index e6467183..124c4884 100644 --- a/packages/cli-engine/tests/execution.test.ts +++ b/packages/cli-engine/tests/execution.test.ts @@ -1264,6 +1264,66 @@ describe("help examples", () => { }); }); +describe("help workflow sections", () => { + test("a group's workflow renders as $-prefixed steps with briefs, {bin} substituted", async () => { + const cli = createTestCli({ + commands: { "auth login": greet }, + groups: { + auth: { + brief: "Authentication", + workflow: [ + { run: "auth login", brief: "Sign in once" }, + { run: "{bin} auth whoami | cat", brief: "Check the session" }, + ], + }, + }, + now: EPOCH, + }); + const result = await cli.run(["auth", "--help"], { + isTty: { stdout: true }, + }); + + expect(result.exitCode).toBe(0); + expect(result.stdout).toContain("Workflow"); + expect(result.stdout).toContain("prisma-test auth login"); + expect(result.stdout).toContain("Sign in once"); + expect(result.stdout).toContain("prisma-test auth whoami | cat"); + expect(result.stdout).not.toContain("{bin}"); + }); + + test("the root workflow renders before the root examples", async () => { + const cli = createTestCli({ + commands: { greet }, + help: { + workflow: [{ run: "greet", brief: "Say hello" }], + examples: ["greet --loud"], + }, + now: EPOCH, + }); + const result = await cli.run([], { isTty: { stdout: true } }); + + expect(result.exitCode).toBe(0); + expect(result.stdout).toContain("Workflow"); + expect(result.stdout.indexOf("Workflow")).toBeLessThan( + result.stdout.indexOf("Examples"), + ); + }); + + test("a group without a workflow renders no Workflow section", async () => { + const cli = createTestCli({ + commands: { "auth login": greet }, + groups: { auth: { brief: "Authentication" } }, + now: EPOCH, + }); + const result = await cli.run(["auth", "--help"], { + isTty: { stdout: true }, + }); + + expect(result.exitCode).toBe(0); + expect(result.stdout).not.toContain("Workflow"); + }); +}); + describe("--version", () => { test("prints the version and exits 0 in human mode", async () => { const cli = createTestCli({ commands: { greet: greet }, now: EPOCH }); diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index 8454f6ff..e48d9eee 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -5,6 +5,7 @@ import { createCli, defineCommandFamily, telemetryCommandGroup, + type WorkflowStep, } from "@prisma/cli-engine"; import { createComposerFamily } from "@prisma/composer-cli/family"; import { ormCommandFamily as ormToolchainFamily } from "@prisma/orm-toolchain/cli"; @@ -161,34 +162,192 @@ export { skillsCommandFamily }; const telemetry = telemetryCommandGroup({ docsUrl: CLI_DOCS_URL }); export const cliGroups: Readonly< - Record + Record< + string, + { + brief: string; + description?: string; + workflow?: readonly WorkflowStep[]; + } + > > = { - auth: { brief: "Manage local authentication for the CLI" }, - project: { brief: "Manage and inspect your Prisma projects" }, + auth: { + brief: + "Manage authentication for the Prisma Platform. Sign in and out, inspect identity, switch workspaces", + description: + "Signing in opens a browser flow and stores a session for one workspace: the account-level container that holds your Projects, members, and billing. Commands act on the current session's workspace; log in once per workspace and switch with the 'workspace' subcommands. A PRISMA_SERVICE_TOKEN environment credential overrides stored sessions, which is the way to authenticate CI.", + }, + project: { + brief: + "Manage Prisma Platform projects. CRUD, link a directory, transfer ownership, manage environment variables", + description: + "A Project groups one product or codebase. It is the child of a workspace and the parent of Branches: isolated environments, one per Git branch, each holding its own services, databases, and buckets. 'Linking' connects a local directory to a Project: the link is stored locally, and every project-scoped command run in that directory targets the linked project unless --project names another. 'create' links automatically; 'link' points a directory at an existing Project; 'show' reports the link.", + workflow: [ + { + run: "project create my-app", + brief: "Create a Project, link this directory", + }, + { run: "git connect", brief: "Connect GitHub so every push deploys" }, + { + run: "project env add KEY=value --role preview", + brief: "Set env vars services get at deploy", + }, + ], + }, "project env": { - brief: "Manage environment variables for the active project", + brief: + "Manage a project's environment variables. Add, update, list, and delete values per scope", + description: + "Variables live in scopes: production, preview (shared by every preview branch), or a single branch's override. Values reach services when they deploy, and are write-only afterwards: list shows metadata, never values.", + }, + postgres: { + brief: + "Manage Prisma Postgres databases. CRUD, usage metrics, backups, and connection credentials", + description: + "Databases are branch-bound: each belongs to a Branch, the isolated environment for one Git branch of a project, so commands take --branch to target one. Address a database by its id (db_...) or name. Connection URLs are secrets that print exactly once, at create or rotate; nothing shows them again. 'backup' restores platform-taken backups, and 'connection' manages per-consumer credentials.", + workflow: [ + { + run: "postgres create app-db", + brief: "Create a database; its URL prints once", + }, + { + run: "postgres connection create app-db --name ci", + brief: "Mint one credential per consumer", + }, + { + run: "postgres connection rotate conn_123", + brief: "Replace a leaked credential", + }, + ], }, - postgres: { brief: "Manage Prisma Postgres databases for a project" }, "postgres backup": { - brief: "Inspect and restore platform-created database backups", + brief: + "Inspect and restore database backups. The platform takes them automatically", + description: + "The platform takes backups automatically; there is no backup-create command. Restore replaces a database's current state with a backup's contents after exact id confirmation.", }, "postgres connection": { - brief: "Manage one-time-view database connection strings", + brief: + "Manage database connection credentials. Create, rotate, and revoke per-consumer connection URLs", + description: + "A connection is one independent credential (a connection URL) for one database. Give each consumer (an app, CI, a teammate) its own, so one can be rotated or revoked without breaking the others. URLs print once, at create or rotate; list shows metadata only.", + }, + bucket: { + brief: + "Manage S3-compatible object-store buckets for a project. CRUD operations and access keys", + description: + "A bucket is blob storage for files and uploads, bound to one Branch of a project and reachable through the standard S3 API. Access goes through S3-compatible keys minted per consumer with 'bucket key create', which prints credentials (endpoint, key id, secret) exactly once.", + workflow: [ + { + run: "bucket create --name uploads", + brief: "Create a bucket in the branch", + }, + { + run: "bucket key create bkt_123", + brief: "Mint S3 credentials, shown once", + }, + ], + }, + "bucket key": { + brief: + "Manage a bucket's access keys. Create, list, and revoke per-consumer S3 credentials", + description: + "A key is one consumer's S3-compatible credentials for one bucket, with role read or read_write. Secrets print once at create; delete revokes access immediately.", + }, + branch: { + brief: + "View Platform branches: the isolated environment behind each Git branch", + description: + "A Branch maps to a Git branch of the connected repository. Each is an isolated environment with its own services, databases, buckets, and environment variables: the production branch serves live traffic, every other branch is a preview.", + }, + git: { + brief: + "Manage the GitHub connection that deploys on push. Connect or disconnect a repository", + description: + "Connecting a GitHub repository turns on deploy-on-push: pushing a Git branch builds and deploys it to a matching Platform Branch. Disconnecting stops push deploys without touching anything already deployed.", + }, + service: { + brief: + "Manage deployed services. Logs, versions, promote and rollback releases, custom domains", + description: + "A service is one HTTP application (a frontend or a backend) deployed on a Branch. Every deploy produces an immutable service version; at most one serves traffic at a time. The 'version' subcommands move which one that is: promote releases a preview build into production, rollback returns production to a previous version without rebuilding. 'logs' reads and streams output, and 'domain' attaches hostnames you own.", + workflow: [ + { + run: "service logs my-api --follow", + brief: "Stream the live version's logs", + }, + { + run: "service version promote cpv_123", + brief: "Release a preview build to production", + }, + { + run: "service version rollback my-api", + brief: "Put production back on the previous version", + }, + ], + }, + "service domain": { + brief: + "Manage custom domains for a service. Register hostnames, drive DNS and TLS verification, inspect status", + description: + "Custom domains point hostnames you own at a service's production branch. After add, create the DNS record the platform reports; wait and retry drive DNS verification and TLS provisioning to done.", + workflow: [ + { + run: "service domain add shop.acme.com --service my-api", + brief: "Register the hostname", + }, + { + run: "service domain wait shop.acme.com --service my-api", + brief: "Block until active or failed", + }, + { + run: "service domain show shop.acme.com --service my-api", + brief: "Inspect status and certificate", + }, + ], + }, + "service version": { + brief: + "Manage a service's deploy versions. List, inspect, promote, roll back, start, stop, delete", + description: + "Every deploy produces an immutable version; at most one serves traffic at a time on each branch. List and show inspect them. Promote and rollback choose which version serves traffic; start, stop, and delete drive one version's lifecycle.", + }, + "auth workspace": { + brief: + "Manage stored workspace sessions. List them, switch the current one, end one", + description: + "One session is stored per workspace you log in to. List them, switch the current one, or end one without touching the others.", + }, + contract: { + brief: + "Author your data contract: the PSL source of your data model. Emit, infer, format", + description: + "A contract is the declarative description of your application's data model, authored in PSL (Prisma Schema Language). Migrations are planned from it, and live databases are verified and signed against it. Emit generates its artifacts, infer derives a contract from an existing database, and format normalizes the source.", + }, + db: { + brief: + "Run contract operations against a live database. Verify, sign, update, migrate", + description: + "These commands run against a live database, addressed with --db . Verify checks the database against the contract (the PSL description of your data model), sign marks it as matching, and update and migrate advance its schema.", + }, + migration: { + brief: + "Manage on-disk migrations derived from contract changes. Plan, inspect, check, track history", + description: + "A migration is an on-disk package describing one schema change, derived from edits to your contract (the PSL description of your data model). Plan writes one; the rest inspect, check integrity, and track what has run where. Apply them with 'db migrate'.", + }, + "migration ref": { + brief: "Manage refs: named pointers to contracts. Set, list, delete", + description: + "A ref is a named pointer to a contract, letting commands target a contract by a stable name. Set, list, and delete refs here.", }, - bucket: { brief: "Manage object-store buckets for a project" }, - "bucket key": { brief: "Manage access keys for an object-store bucket" }, - branch: { brief: "View your Platform branches" }, - git: { brief: "Manage Git repository connections for a project" }, - service: { brief: "Manage services and their versions for a project" }, - "service domain": { brief: "Manage custom domains for a service" }, - "service version": { brief: "Manage the versions of a service" }, - "auth workspace": { brief: "Manage local workspace sessions" }, - contract: { brief: "Define and emit your application data contract" }, - db: { brief: "Verify, sign and update your database against the contract" }, - migration: { brief: "Plan, inspect and scaffold on-disk migrations" }, - "migration ref": { brief: "Manage named refs that point at contracts" }, orm: { brief: "Initialize a Prisma ORM project" }, - skills: { brief: "Keep this project's Prisma agent skills current" }, + skills: { + brief: + "Manage Prisma skills for AI coding agents. Sync and list the instruction files", + description: + "Agent skills are instruction files that teach AI coding agents (Claude Code, Cursor, and others) how to use the installed Prisma packages. They ship inside the packages; sync copies them into the directories the agent harnesses read.", + }, ...telemetry.groups, }; @@ -302,7 +461,19 @@ export function buildCli(): Cli { help: { tagline: "The Prisma Developer Platform, from your terminal", description: - "Deploy your app with isolated infrastructure for every branch.", + "Deploy your app with isolated infrastructure for every branch: a Project groups one product, and each of its Branches maps to a Git branch with its own services, databases, and buckets. The production branch serves live traffic; every other branch is a preview.", + workflow: [ + { run: "auth login", brief: "Sign in to your Prisma workspace" }, + { + run: "project create my-app", + brief: "Create a Project, link this directory", + }, + { run: "git connect", brief: "Connect GitHub so every push deploys" }, + { + run: "deploy", + brief: "Or build and deploy straight from this machine", + }, + ], examples: ["auth login", "project list", "deploy"], docsUrl: CLI_DOCS_URL, }, diff --git a/packages/cli/src/commands/auth/login.ts b/packages/cli/src/commands/auth/login.ts index dccbbaee..2b7b6cac 100644 --- a/packages/cli/src/commands/auth/login.ts +++ b/packages/cli/src/commands/auth/login.ts @@ -114,6 +114,8 @@ export const authLoginCommand = defineCommand({ managesCredentials: true, help: { summary: "Log in to your Prisma platform account", + description: + "Opens a browser sign-in and stores a session for one workspace, the account-level container that holds your Projects. Run it again to add a session for another workspace; 'auth workspace use' switches between stored sessions. In CI or other non-interactive environments, skip login and set PRISMA_SERVICE_TOKEN instead.", examples: ["auth login"], }, handler: async (_args, ctx) => { diff --git a/packages/cli/src/commands/auth/logout.ts b/packages/cli/src/commands/auth/logout.ts index 170c0f9a..ade46ac8 100644 --- a/packages/cli/src/commands/auth/logout.ts +++ b/packages/cli/src/commands/auth/logout.ts @@ -53,6 +53,8 @@ export const authLogoutCommand = defineCommand({ managesCredentials: true, help: { summary: "Clear stored authentication credentials", + description: + "Ends every workspace session stored on this machine. To end just one and keep the others, use 'auth workspace logout' instead. A credential supplied via PRISMA_SERVICE_TOKEN is not stored here and stays in force.", examples: ["auth logout"], }, handler: async (_args, ctx) => { diff --git a/packages/cli/src/commands/auth/whoami.ts b/packages/cli/src/commands/auth/whoami.ts index 07879567..be9ebfad 100644 --- a/packages/cli/src/commands/auth/whoami.ts +++ b/packages/cli/src/commands/auth/whoami.ts @@ -128,7 +128,9 @@ function presentationsFor( export const authWhoamiCommand = defineCommand({ help: { - summary: "Show the authenticated user and accessible workspace", + summary: "Show who is signed in and which workspace commands target", + description: + "Shows which identity the CLI is acting as and which workspace its commands will target. Run it to check you are in the right workspace before creating or deleting resources, or to see whether a PRISMA_SERVICE_TOKEN credential is overriding your stored sessions.", examples: ["auth whoami", "auth whoami --json"], }, handler: async (_args, ctx) => { diff --git a/packages/cli/src/commands/auth/workspace-list.ts b/packages/cli/src/commands/auth/workspace-list.ts index b1634242..75d5792a 100644 --- a/packages/cli/src/commands/auth/workspace-list.ts +++ b/packages/cli/src/commands/auth/workspace-list.ts @@ -81,6 +81,8 @@ export const authWorkspaceListCommand = defineCommand({ managesCredentials: true, help: { summary: "List your workspace sessions", + description: + "Each 'auth login' stores one session per workspace. This lists the sessions on this machine and marks the current one, which every workspace-scoped command targets unless a PRISMA_SERVICE_TOKEN credential is set; that credential overrides stored sessions.", examples: ["auth workspace list", "auth workspace list --json"], }, handler: async (_args, ctx) => { diff --git a/packages/cli/src/commands/auth/workspace-logout.ts b/packages/cli/src/commands/auth/workspace-logout.ts index 8ab41e81..90fc048f 100644 --- a/packages/cli/src/commands/auth/workspace-logout.ts +++ b/packages/cli/src/commands/auth/workspace-logout.ts @@ -82,6 +82,8 @@ export const authWorkspaceLogoutCommand = defineCommand({ }, help: { summary: "End one workspace session", + description: + "Removes the stored session for one workspace and leaves the others in place. To clear every session on this machine, use 'auth logout'.", examples: ["auth workspace logout my-workspace"], }, handler: async (args, ctx) => { diff --git a/packages/cli/src/commands/auth/workspace-use.ts b/packages/cli/src/commands/auth/workspace-use.ts index a8a1a03c..4ee2b825 100644 --- a/packages/cli/src/commands/auth/workspace-use.ts +++ b/packages/cli/src/commands/auth/workspace-use.ts @@ -99,7 +99,9 @@ export const authWorkspaceUseCommand = defineCommand({ }, }, help: { - summary: "Make one of your workspace sessions current", + summary: "Make a workspace session current: commands target that workspace", + description: + "Switches which workspace subsequent commands target. This changes local CLI state only; nothing is created or modified on the platform. Run without an argument to pick from your stored sessions. A PRISMA_SERVICE_TOKEN credential, when set, overrides stored sessions: commands keep targeting its workspace regardless of the selection here.", examples: ["auth workspace use", "auth workspace use my-workspace"], }, handler: async (args, ctx) => { diff --git a/packages/cli/src/commands/branch/list.ts b/packages/cli/src/commands/branch/list.ts index 677efa93..a04a14d4 100644 --- a/packages/cli/src/commands/branch/list.ts +++ b/packages/cli/src/commands/branch/list.ts @@ -54,13 +54,16 @@ function listPresentations(result: BranchListResult): Presentations { export const branchListCommand = defineCommand({ help: { - summary: "List Platform branches for the resolved project", + summary: "List a project's Platform branches", + description: + "A Branch maps to a Git branch of the connected repository. Each one is an isolated environment with its own services, databases, buckets, and environment variables: the production branch serves live traffic, every other branch is a preview.", examples: ["branch list", "branch list --project my-app"], }, args: { flags: { project: flag.string({ - brief: "Project id or name", + brief: + "Project id or name (default: the project this directory is linked to)", placeholder: "id-or-name", }), }, diff --git a/packages/cli/src/commands/bucket/context.ts b/packages/cli/src/commands/bucket/context.ts index 3af9909a..cba961b9 100644 --- a/packages/cli/src/commands/bucket/context.ts +++ b/packages/cli/src/commands/bucket/context.ts @@ -14,12 +14,13 @@ export type BucketCommandContext = CommandContext; export const LIST_BUCKETS_COMMAND = `${CLI_NAME} bucket list`; export const projectFlag = flag.string({ - brief: "Project id or name", + brief: + "Project id or name (default: the project this directory is linked to)", placeholder: "id-or-name", }); export const branchFlag = flag.string({ - brief: "Branch git name", + brief: "Branch git name; use to target one branch's environment", placeholder: "git-name", }); diff --git a/packages/cli/src/commands/bucket/create.ts b/packages/cli/src/commands/bucket/create.ts index 3b8ae577..852671e3 100644 --- a/packages/cli/src/commands/bucket/create.ts +++ b/packages/cli/src/commands/bucket/create.ts @@ -18,6 +18,8 @@ export const bucketCreateCommand = defineCommand({ }, help: { summary: "Create an object-store bucket", + description: + "Creates blob storage in a Branch of the project. A bucket holds no credentials of its own: mint them with 'bucket key create', which prints them once.", examples: [ "bucket create", "bucket create --name my-store", diff --git a/packages/cli/src/commands/bucket/delete.ts b/packages/cli/src/commands/bucket/delete.ts index a5f04fcb..0ceeefb2 100644 --- a/packages/cli/src/commands/bucket/delete.ts +++ b/packages/cli/src/commands/bucket/delete.ts @@ -35,6 +35,8 @@ export const bucketDeleteCommand = defineCommand({ args: { positionals: { bucketId: bucketPositional } }, help: { summary: "Delete a bucket and all its access keys", + description: + "Deletion is permanent: the stored objects are destroyed and every access key stops working. The exact bucket id is the consent token; pass it with --confirm to run non-interactively.", examples: ["bucket delete bkt_123 --confirm bkt_123"], }, needs: { credentials: true }, diff --git a/packages/cli/src/commands/bucket/key-create.ts b/packages/cli/src/commands/bucket/key-create.ts index cac2795e..31ac0554 100644 --- a/packages/cli/src/commands/bucket/key-create.ts +++ b/packages/cli/src/commands/bucket/key-create.ts @@ -60,17 +60,21 @@ export const bucketKeyCreateCommand = defineCommand({ positionals: { bucketId: bucketPositional }, flags: { role: flag.enum({ - brief: "Access role (default: read_write)", + brief: + "Access role; read_write unless set, use read for consumers that only download", values: ["read", "read_write"], }), name: flag.string({ - brief: "Key display name (auto-generated if omitted)", + brief: + "Key display name; use it to record which consumer holds the key (auto-generated if omitted)", placeholder: "name", }), }, }, help: { summary: "Create a bucket access key and print its one-time credentials", + description: + "Mints S3-compatible credentials for one bucket, shown exactly once; store them now, nothing prints them again. Give each consumer its own key so one can be revoked without breaking the others, and grant read-only consumers --role read.", examples: [ "bucket key create bkt_123", "bucket key create bkt_123 --role read", diff --git a/packages/cli/src/commands/bucket/key-delete.ts b/packages/cli/src/commands/bucket/key-delete.ts index f8895f75..d32cd8d9 100644 --- a/packages/cli/src/commands/bucket/key-delete.ts +++ b/packages/cli/src/commands/bucket/key-delete.ts @@ -32,6 +32,8 @@ export const bucketKeyDeleteCommand = defineCommand({ }, help: { summary: "Revoke and delete a bucket access key", + description: + "The key's credentials stop working immediately; anything still using them loses access to the bucket. The bucket and its other keys are untouched.", examples: ["bucket key delete bkt_123 bkey_456"], }, needs: { credentials: true }, diff --git a/packages/cli/src/commands/bucket/key-list.ts b/packages/cli/src/commands/bucket/key-list.ts index 247cd2c4..7bacb399 100644 --- a/packages/cli/src/commands/bucket/key-list.ts +++ b/packages/cli/src/commands/bucket/key-list.ts @@ -48,6 +48,8 @@ export const bucketKeyListCommand = defineCommand({ args: { positionals: { bucketId: bucketPositional } }, help: { summary: "List access keys for a bucket", + description: + "Shows each key's id, name, and role. Key secrets print once, at 'bucket key create'; listing never reveals them.", examples: ["bucket key list bkt_123", "bucket key list bkt_123 --json"], }, needs: { credentials: true }, diff --git a/packages/cli/src/commands/bucket/list.ts b/packages/cli/src/commands/bucket/list.ts index 7f88bd40..6c49e21a 100644 --- a/packages/cli/src/commands/bucket/list.ts +++ b/packages/cli/src/commands/bucket/list.ts @@ -52,7 +52,9 @@ function listPresentations(result: BucketListResult): Presentations { export const bucketListCommand = defineCommand({ args: { flags: { project: projectFlag, branch: branchFlag } }, help: { - summary: "List object-store buckets for the resolved project", + summary: "List a project's object-store buckets", + description: + "A bucket is blob storage for files and uploads. Buckets are branch-bound: each belongs to one Branch, the isolated environment for one Git branch.", examples: [ "bucket list", "bucket list --branch preview", diff --git a/packages/cli/src/commands/feedback.ts b/packages/cli/src/commands/feedback.ts index 3033fe59..3bdfeda6 100644 --- a/packages/cli/src/commands/feedback.ts +++ b/packages/cli/src/commands/feedback.ts @@ -201,8 +201,7 @@ export const feedbackCommand = defineCommand({ help: { summary: "Send feedback to the Prisma CLI team", description: - "Anonymous unless --email is passed. Every submission includes the CLI\n" + - "version, node version, and OS platform/arch, and nothing else.", + "Anonymous unless --email is passed. Every submission includes the CLI version, node version, and OS platform/arch, and nothing else.", examples: [ 'feedback "the deploy flow is great"', 'feedback "please add X" --email you@example.com', diff --git a/packages/cli/src/commands/git/connect.ts b/packages/cli/src/commands/git/connect.ts index 0594fdbf..ddf212dc 100644 --- a/packages/cli/src/commands/git/connect.ts +++ b/packages/cli/src/commands/git/connect.ts @@ -152,14 +152,16 @@ export const gitConnectCommand = defineCommand({ args: { positionals: { gitUrl: positional.optionalString({ - brief: "GitHub repository URL", + brief: "GitHub repository URL (default: this repository's origin)", placeholder: "git-url", }), }, flags: { project: projectFlag }, }, help: { - summary: "Connect the resolved project to a GitHub repository", + summary: "Connect a project to a GitHub repository so every push deploys", + description: + "Turns on deploy-on-push: once connected, pushing a Git branch builds and deploys it to a matching Platform Branch, an isolated environment with its own services, databases, and buckets. If the Prisma GitHub App does not cover the repository yet, the command opens the install page and waits. Run without a URL to use this repository's origin remote.", examples: [ "git connect", "git connect git@github.com:prisma/prisma-cli.git", diff --git a/packages/cli/src/commands/git/context.ts b/packages/cli/src/commands/git/context.ts index 403ceb86..725730c1 100644 --- a/packages/cli/src/commands/git/context.ts +++ b/packages/cli/src/commands/git/context.ts @@ -9,7 +9,8 @@ import { resolveActiveWorkspace } from "../resources-shared/workspace"; export type GitCommandContext = CommandContext; export const projectFlag = flag.string({ - brief: "Project id or name", + brief: + "Project id or name (default: the project this directory is linked to)", placeholder: "id-or-name", }); diff --git a/packages/cli/src/commands/git/disconnect.ts b/packages/cli/src/commands/git/disconnect.ts index 4c4eb103..ce4cc6ce 100644 --- a/packages/cli/src/commands/git/disconnect.ts +++ b/packages/cli/src/commands/git/disconnect.ts @@ -51,7 +51,9 @@ function disconnectPresentations( export const gitDisconnectCommand = defineCommand({ args: { flags: { project: projectFlag } }, help: { - summary: "Disconnect the GitHub repository from the resolved project", + summary: "Disconnect the GitHub repository: pushes stop deploying", + description: + "Stops deploy-on-push by removing the repository connection. Nothing already deployed changes: existing branches, services, and databases stay as they are.", examples: ["git disconnect", "git disconnect --project proj_123"], }, needs: { credentials: true }, diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 5aa282f4..cd4ceaf8 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -776,7 +776,8 @@ function initPresentations( export const initCommand = defineCommand({ help: { - summary: "Prepare this repository for Prisma development", + summary: + "Prepare this repository for Prisma: config file, dev dependency, AI-agent skills", description: "Runs locally and calls no platform API. Adds a postinstall script to package.json that keeps the Prisma agent skills in sync on every install, adds prisma to devDependencies at this CLI's exact version when no dependency field declares it, scaffolds a prisma.config.ts recording which agents to install skills for, then syncs the skills once now. Everything lands in the current directory; a prisma.config.ts or postinstall script that already exists is never edited. Rerunning is safe: each step reports what is already done.", examples: [ diff --git a/packages/cli/src/commands/postgres/backup-list.ts b/packages/cli/src/commands/postgres/backup-list.ts index 16e0b6b8..b46f4e80 100644 --- a/packages/cli/src/commands/postgres/backup-list.ts +++ b/packages/cli/src/commands/postgres/backup-list.ts @@ -80,6 +80,8 @@ export const postgresBackupListCommand = defineCommand({ }, help: { summary: "List backups for a database", + description: + "Backups are created automatically by the platform. Use the listed backup ids with 'postgres backup restore'.", examples: [ "postgres backup list db_123", "postgres backup list acme-production --limit 50", diff --git a/packages/cli/src/commands/postgres/backup-restore.ts b/packages/cli/src/commands/postgres/backup-restore.ts index ad4a2bf7..43d8e464 100644 --- a/packages/cli/src/commands/postgres/backup-restore.ts +++ b/packages/cli/src/commands/postgres/backup-restore.ts @@ -67,11 +67,12 @@ export const postgresBackupRestoreCommand = defineCommand({ }, flags: { backup: flag.string({ - brief: "Backup to restore from", + brief: "Backup to restore from, listed by 'postgres backup list'", placeholder: "backup-id", }), sourceDatabase: flag.string({ - brief: "Database the backup belongs to (defaults to the target)", + brief: + "Database the backup belongs to; use when restoring another database's backup into the target", placeholder: "database", }), project: projectFlag, @@ -80,6 +81,8 @@ export const postgresBackupRestoreCommand = defineCommand({ }, help: { summary: "Restore a database from a backup after exact id confirmation", + description: + "Replaces the target database's current state with the backup's contents; anything written since that backup is lost, so the exact target database id is the consent token. To restore another database's backup into this one, name where the backup came from with --source-database.", examples: [ "postgres backup restore db_123 --backup bkp_456 --confirm db_123", ], diff --git a/packages/cli/src/commands/postgres/connection-create.ts b/packages/cli/src/commands/postgres/connection-create.ts index ab0dbe3c..03fccbb0 100644 --- a/packages/cli/src/commands/postgres/connection-create.ts +++ b/packages/cli/src/commands/postgres/connection-create.ts @@ -18,14 +18,20 @@ export const postgresConnectionCreateCommand = defineCommand({ args: { positionals: { database: databasePositional }, flags: { - name: flag.string({ brief: "Connection name", placeholder: "name" }), + name: flag.string({ + brief: + "Connection name; use it to record which consumer holds the credential", + placeholder: "name", + }), project: projectFlag, branch: branchFlag, }, }, help: { summary: - "Create a database connection and print its one-time connection URL", + "Create an additional connection URL for a database; printed exactly once", + description: + "Mints an additional credential for an existing database, shown exactly once. Give each consumer (an app, CI, a teammate) its own named connection, so its access can later be rotated or revoked without touching the others.", examples: [ "postgres connection create db_123", "postgres connection create db_123 --name readonly", diff --git a/packages/cli/src/commands/postgres/connection-delete.ts b/packages/cli/src/commands/postgres/connection-delete.ts index 74c5253b..c936eb65 100644 --- a/packages/cli/src/commands/postgres/connection-delete.ts +++ b/packages/cli/src/commands/postgres/connection-delete.ts @@ -19,6 +19,8 @@ export const postgresConnectionDeleteCommand = defineCommand({ }, help: { summary: "Delete a database connection after exact id confirmation", + description: + "Revokes the credential: anything still using its URL loses access to the database. The database itself and its other connections are untouched. The exact connection id is the consent token.", examples: ["postgres connection delete conn_123 --confirm conn_123"], }, needs: { credentials: true }, diff --git a/packages/cli/src/commands/postgres/connection-list.ts b/packages/cli/src/commands/postgres/connection-list.ts index fee6cec4..68bd03db 100644 --- a/packages/cli/src/commands/postgres/connection-list.ts +++ b/packages/cli/src/commands/postgres/connection-list.ts @@ -77,6 +77,8 @@ export const postgresConnectionListCommand = defineCommand({ }, help: { summary: "List database connection metadata without secret values", + description: + "Each connection is an independent credential for the same database, so one consumer's access can be rotated or revoked without breaking the others. Listing shows metadata only; connection URLs print once, at create or rotate, and never again.", examples: [ "postgres connection list db_123", "postgres connection list acme-preview --branch preview --json", diff --git a/packages/cli/src/commands/postgres/connection-rotate.ts b/packages/cli/src/commands/postgres/connection-rotate.ts index 8a57504f..f68fca43 100644 --- a/packages/cli/src/commands/postgres/connection-rotate.ts +++ b/packages/cli/src/commands/postgres/connection-rotate.ts @@ -21,6 +21,8 @@ export const postgresConnectionRotateCommand = defineCommand({ help: { summary: "Rotate connection credentials and print the new one-time connection URL", + description: + "Replaces the connection's credentials: the old URL stops working and the new one prints exactly once. Use it after a suspected leak or on a rotation schedule. The exact connection id is the consent token.", examples: ["postgres connection rotate conn_123 --confirm conn_123"], }, needs: { credentials: true }, diff --git a/packages/cli/src/commands/postgres/context.ts b/packages/cli/src/commands/postgres/context.ts index 36b7c4fd..0d671db5 100644 --- a/packages/cli/src/commands/postgres/context.ts +++ b/packages/cli/src/commands/postgres/context.ts @@ -11,12 +11,13 @@ import { resolveActiveWorkspace } from "../resources-shared/workspace"; export type PostgresCommandContext = CommandContext; export const projectFlag = flag.string({ - brief: "Project id or name", + brief: + "Project id or name (default: the project this directory is linked to)", placeholder: "id-or-name", }); export const branchFlag = flag.string({ - brief: "Branch git name", + brief: "Branch git name; use to target one branch's environment", placeholder: "git-name", }); diff --git a/packages/cli/src/commands/postgres/create.ts b/packages/cli/src/commands/postgres/create.ts index 778ba111..d2a584f2 100644 --- a/packages/cli/src/commands/postgres/create.ts +++ b/packages/cli/src/commands/postgres/create.ts @@ -17,7 +17,8 @@ export const postgresCreateCommand = defineCommand({ }, flags: { region: flag.string({ - brief: "Prisma Postgres region id", + brief: + "Prisma Postgres region id; set it when the data must live near a location", placeholder: "region", }), project: projectFlag, @@ -27,6 +28,8 @@ export const postgresCreateCommand = defineCommand({ help: { summary: "Create a Prisma Postgres database and print its one-time connection URL", + description: + "Creates a database in a Branch of the project and prints its connection URL exactly once; store it now, nothing prints it again. Mint further URLs for other consumers with 'postgres connection create'.", examples: [ "postgres create my-db", "postgres create my-db --branch feature/foo --region eu-central-1", diff --git a/packages/cli/src/commands/postgres/delete.ts b/packages/cli/src/commands/postgres/delete.ts index 6995379d..6a14c0bd 100644 --- a/packages/cli/src/commands/postgres/delete.ts +++ b/packages/cli/src/commands/postgres/delete.ts @@ -47,6 +47,8 @@ export const postgresDeleteCommand = defineCommand({ }, help: { summary: "Delete a database after exact id confirmation", + description: + "Deletion is permanent and destroys the data. The exact database id is the consent token; pass it with --confirm to run non-interactively.", examples: ["postgres delete db_123 --confirm db_123"], }, needs: { credentials: true }, diff --git a/packages/cli/src/commands/postgres/list.ts b/packages/cli/src/commands/postgres/list.ts index d1b7411d..4935d25a 100644 --- a/packages/cli/src/commands/postgres/list.ts +++ b/packages/cli/src/commands/postgres/list.ts @@ -75,7 +75,9 @@ function listPresentations(result: DatabaseListResult): Presentations { export const postgresListCommand = defineCommand({ args: { flags: { project: projectFlag, branch: branchFlag } }, help: { - summary: "List Prisma Postgres databases for the resolved project", + summary: "List a project's Prisma Postgres databases", + description: + "Databases are branch-bound: each belongs to one Branch, the isolated environment for one Git branch. Without --branch, this lists databases across every branch of the project.", examples: [ "postgres list", "postgres list --branch feature/foo", diff --git a/packages/cli/src/commands/postgres/show.ts b/packages/cli/src/commands/postgres/show.ts index 4701ae7d..2f6b17fb 100644 --- a/packages/cli/src/commands/postgres/show.ts +++ b/packages/cli/src/commands/postgres/show.ts @@ -70,6 +70,8 @@ export const postgresShowCommand = defineCommand({ }, help: { summary: "Show database metadata without secret values", + description: + "Shows a database's name, branch, region, and status. Connection URLs are secrets printed once, by 'postgres create' or 'postgres connection rotate'; this command never reveals them.", examples: [ "postgres show db_123", "postgres show acme-preview --branch preview --json", diff --git a/packages/cli/src/commands/project/create.ts b/packages/cli/src/commands/project/create.ts index 9fd0eee4..a0f8626f 100644 --- a/packages/cli/src/commands/project/create.ts +++ b/packages/cli/src/commands/project/create.ts @@ -21,13 +21,17 @@ export const projectCreateCommand = defineCommand({ }, flags: { region: flag.string({ - brief: "Prisma Compute region id", + brief: + "Prisma Compute region for the project's infrastructure; set it when data or users must stay near a location", placeholder: "region", }), }, }, help: { - summary: "Create a Project and link this directory", + summary: + "Create a Project and link this directory: commands run here target it", + description: + "Creates a Project in your workspace and links the current directory to it, so later commands resolve it without --project. A Project groups one product or codebase; its Branches are isolated environments, each with its own services, databases, and buckets. This is usually the first command after 'auth login' in a new repository.", examples: ["project create my-app", "project create my-app --json"], }, needs: { credentials: true }, diff --git a/packages/cli/src/commands/project/delete.ts b/packages/cli/src/commands/project/delete.ts index 13fb2030..cdceca24 100644 --- a/packages/cli/src/commands/project/delete.ts +++ b/packages/cli/src/commands/project/delete.ts @@ -60,6 +60,8 @@ export const projectDeleteCommand = defineCommand({ }, help: { summary: "Delete a Project permanently after exact id confirmation", + description: + "Deletion is permanent: it destroys the project's databases and stops its deployed services. Because of that, the command asks for the exact project id as a consent token; pass it with --confirm to run non-interactively.", examples: ["project delete proj_123 --confirm proj_123"], }, needs: { credentials: true }, diff --git a/packages/cli/src/commands/project/env-add.ts b/packages/cli/src/commands/project/env-add.ts index 11b05a4c..76dbb12d 100644 --- a/packages/cli/src/commands/project/env-add.ts +++ b/packages/cli/src/commands/project/env-add.ts @@ -73,7 +73,10 @@ export const projectEnvAddCommand = defineCommand({ }, }, help: { - summary: "Create a new environment variable.", + summary: + "Create an environment variable in one scope: production, preview, or one branch", + description: + "Variables reach a service's environment when it deploys. Writes always name their scope so production is never targeted by accident: --role production, --role preview (shared by every preview branch), or --branch for one branch's override. The value comes from KEY=VALUE, from the current shell environment when only KEY is given, or from a dotenv file with --file.", examples: [ "project env add STRIPE_KEY=sk_test_xxx --role production", "project env add STRIPE_KEY=sk_test_xxx --role preview", diff --git a/packages/cli/src/commands/project/env-delete.ts b/packages/cli/src/commands/project/env-delete.ts index 2a0b920c..9c1c036a 100644 --- a/packages/cli/src/commands/project/env-delete.ts +++ b/packages/cli/src/commands/project/env-delete.ts @@ -59,7 +59,10 @@ export const projectEnvDeleteCommand = defineCommand({ }, }, help: { - summary: "Delete an environment variable from a scope.", + summary: + "Delete an environment variable from one scope: production, preview, or one branch", + description: + "Removes the variable from the named scope only; the same key in other scopes is untouched. Services lose the value on their next deploy.", examples: [ "project env delete STRIPE_KEY --role production", "project env delete STRIPE_KEY --role preview", diff --git a/packages/cli/src/commands/project/env-list.ts b/packages/cli/src/commands/project/env-list.ts index 67b5d904..3ea0c658 100644 --- a/packages/cli/src/commands/project/env-list.ts +++ b/packages/cli/src/commands/project/env-list.ts @@ -77,14 +77,18 @@ export const projectEnvListCommand = defineCommand({ flags: { role: roleFlag, branch: flag.string({ - brief: "Preview branch resolved scope", + brief: + "Show what one preview branch resolves: the preview scope plus its overrides", placeholder: "git-name", }), project: projectFlag, }, }, help: { - summary: "List environment variable metadata for a scope (no values).", + summary: + "List environment variables in a scope: names and ids, never values", + description: + "Values are write-only through the CLI: listing shows names, ids, and status, never values. Without a scope flag it shows an overview of every scope; --role narrows to the production or preview scope, and --branch shows what one preview branch resolves.", examples: [ "project env list", "project env list --role production", diff --git a/packages/cli/src/commands/project/env-shared.ts b/packages/cli/src/commands/project/env-shared.ts index dfeff07e..4789cf13 100644 --- a/packages/cli/src/commands/project/env-shared.ts +++ b/packages/cli/src/commands/project/env-shared.ts @@ -16,22 +16,26 @@ import { resolveActiveWorkspace } from "../resources-shared/workspace"; import { type ProjectCommandContext, resolvePinnedProject } from "./context"; export const roleFlag = flag.enum({ - brief: "Project template scope (production or preview)", + brief: + "Project-wide scope: production, or preview (shared by every preview branch)", values: ["production", "preview"], }); export const projectFlag = flag.string({ - brief: "Project id or name", + brief: + "Project id or name (default: the project this directory is linked to)", placeholder: "id-or-name", }); export const branchFlag = flag.string({ - brief: "Preview branch override scope", + brief: + "Scope to one preview branch's override; use for values only that branch needs", placeholder: "git-name", }); export const fileFlag = flag.string({ - brief: "Read KEY=VALUE assignments from a dotenv file", + brief: + "Read KEY=VALUE assignments from a dotenv file; use to import many variables at once", placeholder: "path", }); diff --git a/packages/cli/src/commands/project/env-update.ts b/packages/cli/src/commands/project/env-update.ts index 03d62be2..38845b30 100644 --- a/packages/cli/src/commands/project/env-update.ts +++ b/packages/cli/src/commands/project/env-update.ts @@ -72,7 +72,9 @@ export const projectEnvUpdateCommand = defineCommand({ }, }, help: { - summary: "Replace an existing environment variable's value.", + summary: "Replace an existing environment variable's value", + description: + "Overwrites one variable's value in one scope; the variable must already exist there (use 'project env add' to create it). With --file, overwrites every variable the dotenv file lists. New values reach services on their next deploy.", examples: [ "project env update STRIPE_KEY=sk_new_xxx --role production", "project env update STRIPE_KEY=sk_new_xxx --role preview", diff --git a/packages/cli/src/commands/project/link.ts b/packages/cli/src/commands/project/link.ts index f62bc6ba..e9c10bc3 100644 --- a/packages/cli/src/commands/project/link.ts +++ b/packages/cli/src/commands/project/link.ts @@ -180,7 +180,10 @@ export const projectLinkCommand = defineCommand({ }, }, help: { - summary: "Link this directory to a Project", + summary: + "Link this directory to a Project: commands run here target it by default", + description: + "Records locally that this directory belongs to an existing Project, so later commands resolve it without --project. Linking itself changes local state only. Run without an argument to pick from your workspace's projects; that picker also offers creating a new Project, which does create one on the platform.", examples: [ "project link", "project link proj_123", diff --git a/packages/cli/src/commands/project/list.ts b/packages/cli/src/commands/project/list.ts index a66a9a2b..c5f98a79 100644 --- a/packages/cli/src/commands/project/list.ts +++ b/packages/cli/src/commands/project/list.ts @@ -83,6 +83,8 @@ function listPresentations(result: ProjectListResult): Presentations { export const projectListCommand = defineCommand({ help: { summary: "List all projects in your workspace", + description: + "A Project groups one product or codebase. It is the child of a workspace and the parent of Branches: isolated environments that hold your services, databases, and buckets.", examples: ["project list", "project list --json"], }, needs: { credentials: true }, diff --git a/packages/cli/src/commands/project/rename.ts b/packages/cli/src/commands/project/rename.ts index 2505a0a5..3c3a6ce6 100644 --- a/packages/cli/src/commands/project/rename.ts +++ b/packages/cli/src/commands/project/rename.ts @@ -50,13 +50,16 @@ export const projectRenameCommand = defineCommand({ }, flags: { project: flag.string({ - brief: "Project id or name", + brief: + "Project id or name (default: the project this directory is linked to)", placeholder: "id-or-name", }), }, }, help: { - summary: "Rename the resolved Project", + summary: "Rename a Project", + description: + "Changes the display name of the linked project, or of the one named by --project. The project id stays the same, so existing links and automation keep working.", examples: [ 'project rename "Acme Dashboard v2"', "project rename billing-api --project proj_123", diff --git a/packages/cli/src/commands/project/show.ts b/packages/cli/src/commands/project/show.ts index a1025d07..78990ebc 100644 --- a/packages/cli/src/commands/project/show.ts +++ b/packages/cli/src/commands/project/show.ts @@ -111,13 +111,16 @@ export const projectShowCommand = defineCommand({ args: { positionals: { project: positional.optionalString({ - brief: "Project id or name (default: the linked project)", + brief: + "Project id or name (default: the project this directory is linked to)", placeholder: "id-or-name", }), }, }, help: { - summary: "Show this directory's Project binding", + summary: "Show which Project this directory is linked to", + description: + "Reports which Project this directory is linked to, or that it is not linked, with next steps. Pass an id or name to inspect any project without changing the local link.", examples: ["project show", "project show proj_123 --json"], }, needs: { credentials: true }, diff --git a/packages/cli/src/commands/project/transfer.ts b/packages/cli/src/commands/project/transfer.ts index 33d77e2c..aa95b7bd 100644 --- a/packages/cli/src/commands/project/transfer.ts +++ b/packages/cli/src/commands/project/transfer.ts @@ -177,11 +177,13 @@ export const projectTransferCommand = defineCommand({ }, flags: { toWorkspace: flag.string({ - brief: "Locally authenticated workspace to receive the project", + brief: + "Receiving workspace, when it is one of your own stored sessions", placeholder: "id-or-name", }), recipientToken: flag.string({ - brief: "Access token for the receiving workspace", + brief: + "Access token for the receiving workspace, when you are not a member of it", placeholder: "token", }), }, @@ -189,6 +191,8 @@ export const projectTransferCommand = defineCommand({ help: { summary: "Transfer a Project to another workspace after exact id confirmation", + description: + "Moves a project, with everything in it, out of the current workspace. Name the receiving workspace with --to-workspace when you are logged in to it too, or pass --recipient-token when someone else owns it. The exact project id is the consent token.", examples: [ 'project transfer proj_123 --to-workspace "Prisma Labs" --confirm proj_123', "project transfer proj_123 --recipient-token --confirm proj_123", diff --git a/packages/cli/src/commands/service/create.ts b/packages/cli/src/commands/service/create.ts index 81a0a118..dc177e27 100644 --- a/packages/cli/src/commands/service/create.ts +++ b/packages/cli/src/commands/service/create.ts @@ -16,6 +16,8 @@ import { export const serviceCreateCommand = defineCommand({ help: { summary: "Create a service in a project", + description: + "Registers a service on a Branch ahead of its first deploy. Deploying through a connected repository or 'deploy' creates services automatically, so reach for this only when a service must exist before anything has been deployed to it.", examples: [ "service create my-service", "service create my-service --region us-east-1 --branch main", @@ -30,15 +32,17 @@ export const serviceCreateCommand = defineCommand({ }, flags: { project: flag.string({ - brief: "Project id or name", + brief: + "Project id or name (default: the project this directory is linked to)", placeholder: "id-or-name", }), region: flag.string({ - brief: "Prisma Compute region id", + brief: + "Prisma Compute region id; set it when the service must run near a location", placeholder: "region", }), branch: flag.string({ - brief: "Branch name", + brief: "Branch to create the service on (default: the default branch)", placeholder: "branch", }), }, diff --git a/packages/cli/src/commands/service/delete.ts b/packages/cli/src/commands/service/delete.ts index a5189c6d..cf2b53b3 100644 --- a/packages/cli/src/commands/service/delete.ts +++ b/packages/cli/src/commands/service/delete.ts @@ -8,7 +8,9 @@ import { resolveServiceReadState, toServiceSummary } from "./target"; export const serviceDeleteCommand = defineCommand({ help: { - summary: "Delete the service from the resolved branch", + summary: "Delete a service from one branch", + description: + "Removes the service from the targeted Branch only: its versions stop serving and its URL goes away. The same service on other branches is untouched. Pass the confirmation token with --confirm to run non-interactively.", examples: [ "service delete my-service", "service delete my-service --confirm my-service", diff --git a/packages/cli/src/commands/service/domain-add.ts b/packages/cli/src/commands/service/domain-add.ts index 21d9e514..854de396 100644 --- a/packages/cli/src/commands/service/domain-add.ts +++ b/packages/cli/src/commands/service/domain-add.ts @@ -13,6 +13,8 @@ import { export const serviceDomainAddCommand = defineCommand({ help: { summary: "Register a custom domain on the service's production branch", + description: + "Points a hostname you own at the service instead of its generated URL. After registering, create the DNS record the platform asks for, then 'service domain wait' blocks until verification and TLS provisioning finish.", examples: ["service domain add shop.acme.com --service my-service"], }, args: domainTargetArgs(), diff --git a/packages/cli/src/commands/service/domain-delete.ts b/packages/cli/src/commands/service/domain-delete.ts index 008cb7d6..7ba8ae0e 100644 --- a/packages/cli/src/commands/service/domain-delete.ts +++ b/packages/cli/src/commands/service/domain-delete.ts @@ -13,6 +13,8 @@ import { export const serviceDomainDeleteCommand = defineCommand({ help: { summary: "Delete a custom domain from the service", + description: + "The hostname stops routing to the service immediately; the service itself and its generated URL keep working. Pass the hostname with --confirm to run non-interactively.", examples: [ "service domain delete shop.acme.com --service my-service", "service domain delete shop.acme.com --service my-service --confirm shop.acme.com", diff --git a/packages/cli/src/commands/service/domain-retry.ts b/packages/cli/src/commands/service/domain-retry.ts index a19cce4c..df6bf6db 100644 --- a/packages/cli/src/commands/service/domain-retry.ts +++ b/packages/cli/src/commands/service/domain-retry.ts @@ -14,6 +14,8 @@ import { export const serviceDomainRetryCommand = defineCommand({ help: { summary: "Retry custom domain DNS verification and TLS provisioning", + description: + "Run it after fixing what made the domain fail, typically a missing or wrong DNS record reported by 'service domain show'.", examples: ["service domain retry shop.acme.com --service my-service"], }, args: domainTargetArgs(), diff --git a/packages/cli/src/commands/service/domain-shared.ts b/packages/cli/src/commands/service/domain-shared.ts index ef708503..31154fdd 100644 --- a/packages/cli/src/commands/service/domain-shared.ts +++ b/packages/cli/src/commands/service/domain-shared.ts @@ -5,11 +5,12 @@ export function domainTargetArgs() { return { flags: { service: flag.string({ - brief: "Service id or name", + brief: "Service id or name the domain belongs to", placeholder: "name", }), project: flag.string({ - brief: "Project id or name", + brief: + "Project id or name (default: the project this directory is linked to)", placeholder: "id-or-name", }), branch: flag.string({ diff --git a/packages/cli/src/commands/service/domain-show.ts b/packages/cli/src/commands/service/domain-show.ts index 424e1b9e..1c264ce1 100644 --- a/packages/cli/src/commands/service/domain-show.ts +++ b/packages/cli/src/commands/service/domain-show.ts @@ -14,6 +14,8 @@ import { export const serviceDomainShowCommand = defineCommand({ help: { summary: "Show custom domain status and certificate details", + description: + "Reports where the domain is in its lifecycle: DNS verification, TLS certificate issuance, active, or failed with the fix to apply. Check here first when a domain is not serving.", examples: ["service domain show shop.acme.com --service my-service"], }, args: domainTargetArgs(), diff --git a/packages/cli/src/commands/service/domain-wait.ts b/packages/cli/src/commands/service/domain-wait.ts index cf59c3f3..8e0f489b 100644 --- a/packages/cli/src/commands/service/domain-wait.ts +++ b/packages/cli/src/commands/service/domain-wait.ts @@ -77,6 +77,8 @@ const DURATION = /^(\d+)(ms|s|m|h)$/; export const serviceDomainWaitCommand = defineCommand({ help: { summary: "Wait until a custom domain is active or failed", + description: + "Blocks while DNS verification and TLS provisioning run, then exits with the outcome; useful in scripts and CI after 'service domain add'.", examples: [ "service domain wait shop.acme.com --service my-service", "service domain wait shop.acme.com --service my-service --timeout 30m", diff --git a/packages/cli/src/commands/service/list.ts b/packages/cli/src/commands/service/list.ts index fdb5b720..3ec58648 100644 --- a/packages/cli/src/commands/service/list.ts +++ b/packages/cli/src/commands/service/list.ts @@ -12,16 +12,19 @@ import { export const serviceListCommand = defineCommand({ help: { summary: "List the services in a project", + description: + "A service is one HTTP application (a frontend or a backend) deployed on a Branch of the project. Each deploy produces a service version; at most one version serves traffic at a time.", examples: ["service list", "service list --project my-app --json"], }, args: { flags: { project: flag.string({ - brief: "Project id or name", + brief: + "Project id or name (default: the project this directory is linked to)", placeholder: "id-or-name", }), branch: flag.string({ - brief: "Branch name", + brief: "Branch the services live on (default: the default branch)", placeholder: "branch", }), }, diff --git a/packages/cli/src/commands/service/logs.ts b/packages/cli/src/commands/service/logs.ts index fe34f59f..f0ec6d7b 100644 --- a/packages/cli/src/commands/service/logs.ts +++ b/packages/cli/src/commands/service/logs.ts @@ -381,6 +381,8 @@ async function resolveLogsTarget( export const serviceLogsCommand = defineSessionCommand({ help: { summary: "Read logs for a version of the service", + description: + "Reads the live version's logs by default; point --version-id at any other version, such as a failed build being debugged. --follow keeps streaming new lines, and --tail or --from-start choose where reading starts.", examples: [ "service logs my-service", "service logs my-service --tail 500", @@ -405,7 +407,8 @@ export const serviceLogsCommand = defineSessionCommand({ placeholder: "name", }), versionId: flag.string({ - brief: "Service version id to read (default: the live version)", + brief: + "Service version id to read; use for a failed or stopped version (default: the live version)", placeholder: "id", }), tail: flag.number({ diff --git a/packages/cli/src/commands/service/show.ts b/packages/cli/src/commands/service/show.ts index 214791f9..0be6e2f2 100644 --- a/packages/cli/src/commands/service/show.ts +++ b/packages/cli/src/commands/service/show.ts @@ -13,7 +13,9 @@ import { export const serviceShowCommand = defineCommand({ help: { - summary: "Show the service and its current version", + summary: "Show a service and the version currently serving traffic", + description: + "Shows the version serving traffic and the service's live URL, when the service has a live version; a service that has never deployed has none yet. Inspect other versions with 'service version list' and 'service version show'.", examples: [ "service show my-service", "service show my-service --branch feature-x", diff --git a/packages/cli/src/commands/service/version-delete.ts b/packages/cli/src/commands/service/version-delete.ts index 31047033..e133627e 100644 --- a/packages/cli/src/commands/service/version-delete.ts +++ b/packages/cli/src/commands/service/version-delete.ts @@ -12,6 +12,8 @@ import { resolveVersionSubject, toServiceSummary } from "./target"; export const serviceVersionDeleteCommand = defineCommand({ help: { summary: "Delete a service version and the artifact it holds", + description: + "Permanently removes the version and its built artifact, so it can no longer be started or rolled back to. Pass the exact version id with --confirm to run non-interactively.", examples: [ "service version delete cpv_123", "service version delete cpv_123 --confirm cpv_123", diff --git a/packages/cli/src/commands/service/version-list.ts b/packages/cli/src/commands/service/version-list.ts index a9fa2975..71c133f6 100644 --- a/packages/cli/src/commands/service/version-list.ts +++ b/packages/cli/src/commands/service/version-list.ts @@ -14,6 +14,8 @@ import { export const serviceVersionListCommand = defineCommand({ help: { summary: "List versions of the service", + description: + "Every deploy produces an immutable service version; at most one serves traffic at a time. Use the listed ids with 'service version' promote, rollback, start, stop, and delete.", examples: [ "service version list my-service", "service version list my-service --branch feature-x", diff --git a/packages/cli/src/commands/service/version-promote.ts b/packages/cli/src/commands/service/version-promote.ts index d218c83a..8b6d58b2 100644 --- a/packages/cli/src/commands/service/version-promote.ts +++ b/packages/cli/src/commands/service/version-promote.ts @@ -11,6 +11,8 @@ export const serviceVersionPromoteCommand = defineCommand({ help: { summary: "Promote a service version to production by rebuilding with production env vars", + description: + "The normal release path: take a version that proved itself on a preview branch, rebuild its source with production environment variables, and switch production traffic to the result. To undo a release, use 'service version rollback', which needs no rebuild.", examples: ["service version promote cpv_123"], }, args: { diff --git a/packages/cli/src/commands/service/version-rollback.ts b/packages/cli/src/commands/service/version-rollback.ts index fcbcfb77..1e3abbb0 100644 --- a/packages/cli/src/commands/service/version-rollback.ts +++ b/packages/cli/src/commands/service/version-rollback.ts @@ -22,6 +22,8 @@ import { export const serviceVersionRollbackCommand = defineCommand({ help: { summary: "Roll back production to a previous service version", + description: + "Returns production traffic to an already-built version without rebuilding, which makes it the fastest way out of a bad release. Defaults to the version before the live one; name an exact target with --to.", examples: [ "service version rollback my-service", "service version rollback my-service --to cpv_123", @@ -40,7 +42,7 @@ export const serviceVersionRollbackCommand = defineCommand({ }), to: flag.string({ brief: - "Version id to roll back to (default: the version before the live one)", + "Version id to roll back to; use when the last good version is further back (default: the version before the live one)", placeholder: "version", }), }, diff --git a/packages/cli/src/commands/service/version-show.ts b/packages/cli/src/commands/service/version-show.ts index ee05855e..1068fd72 100644 --- a/packages/cli/src/commands/service/version-show.ts +++ b/packages/cli/src/commands/service/version-show.ts @@ -12,6 +12,8 @@ import { serviceProvider, toServiceSummary } from "./target"; export const serviceVersionShowCommand = defineCommand({ help: { summary: "Show a service version in detail", + description: + "Version ids are globally unique, so the id alone is the complete target: no --project or --branch scope is needed.", examples: ["service version show cpv_123"], }, args: { diff --git a/packages/cli/src/commands/skills/list.ts b/packages/cli/src/commands/skills/list.ts index 361f17fc..c604e816 100644 --- a/packages/cli/src/commands/skills/list.ts +++ b/packages/cli/src/commands/skills/list.ts @@ -9,6 +9,8 @@ import { packageReports, versionConflictDiagnostics } from "./sync"; export const skillsListCommand = defineCommand({ help: { summary: "Show which Prisma agent skills are installed in this project", + description: + "Shows each skill, the package and version it came from, and whether the synced copies are current. Run 'skills sync' to bring stale copies up to date.", examples: ["skills list", "skills list --json"], }, needs: { config: skillsConfigSection }, diff --git a/packages/cli/src/commands/skills/sync.ts b/packages/cli/src/commands/skills/sync.ts index 15f3953f..1d5abbcc 100644 --- a/packages/cli/src/commands/skills/sync.ts +++ b/packages/cli/src/commands/skills/sync.ts @@ -81,9 +81,9 @@ function bothSwitchesError(): CliStructuredError { export const skillsSyncCommand = defineCommand({ help: { summary: - "Copy the agent skills from installed Prisma packages into this project", + "Copy the AI-agent instruction files (skills) from installed Prisma packages into this project", description: - "Skills come from the Prisma packages the project installs, so they always describe the version in use. Sync copies them into the skill directories the agent harnesses read, and removes copies whose package is gone. It does nothing, and exits 0, when everything is already current.", + "Agent skills are instruction files that teach AI coding agents (Claude Code, Cursor, and others) how to use the installed Prisma packages. They ship inside the packages themselves, so they always describe the version in use. Sync copies them into the skill directories the agent harnesses read, and removes copies whose package is gone. It does nothing, and exits 0, when everything is already current.", examples: ["skills sync", "skills sync --disable"], }, needs: { config: skillsConfigSection },