Add @prisma/cli-engine — one library owns argv to exit code — proven end to end on auth whoami - #129
Conversation
Records the design constraints for the consolidated prisma CLI agreed with the operator — the interface between the CLI shell and its product packages, and why each constraint matters. Two questions stay open by design: command-tree ownership, and third-party engine vs owned. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
…ager behavior R12: the shell owns the command tree — paths are cosmetic to products (the real invocation is the command and its arguments) and structural to the shell; the tree's six months of cross-product design is the evidence. R13: the CLI never installs anything — optional peer dependencies plus a structured missing-dependency error replace the old self-installing submodule approach. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
…; no global flags R5 constrains who owns rendering, not how it is built: the engine may adopt its internal framework's renderer where the output meets the Style Guide. And there are no global flags — per-command declaration with a shared flag-set for uniformity. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
Resolves the document's one open question. Clipanion passes the rubric's nine technical criteria but fails maintenance (23 months without a publish, 4.x in RC for three years at decision time); stricli passes all ten, and its known limitations are neutralized by this document's own rules. Fully hidden per R3, so the internals remain replaceable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
…t extensions Common fields are engine-owned and mandatory-first; product extension data is product-published API the engine passes through; recurring extension shapes get promoted into the vocabulary. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
…ord, briefs The prisma-cli-v8 project's Drive artifacts, committed so parallel agent sessions across repos share one canonical record: the project spec (DoD: a publishable prisma@8.0.0-rc1 on the settled design, all three command families ported), the design-notes index, the engine interface design (v8 + full v1-v7 history + five review-round artifacts + the output-modes survey + the stricli decision record + the parked daemon-library notes), and the paused 1b/1c hand-off briefs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
Seven slices: engine + one vertical command; platform family port with the auth extraction and commander-shell retirement; Composer as the first cross-repo consumer (config sections, sessions, the child-status passthrough, tandem releases); the ADR 239 amendment in parallel; ORM last (diagnostics model, lsp, exit-code reconciliation); the three-check conformance tool; the release pipeline ending at a publishable prisma@8.0.0-rc1. Includes the coverage ledger mapping every engine surface to the slice that proves it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
…i vertical Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
New workspace package @prisma/cli-engine (tsdown build, vitest tests, private, unpublished). Exports only the ./protocol subpath carrying the protocol types from the v8 draft: Diagnostic (pure data, the error envelope minus ok), CliStructuredError ported from the prisma/prisma donor (docsUrlFor dropped per the v8 docs-base ruling; toEnvelope typed as { ok: false } & Diagnostic), Result/Ok/NotOk with ok/notOk/okVoid factories, and NextAction without the journey field. Tests pin the envelope shape, prove the subpath resolves via package self-reference, and check the module exports only the expected names; a type-only import of Diagnostic through the subpath typechecks.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
The committed engine-interface draft files under .drive/projects/ are design documents, not shipped code; they fail lint rules meant for production TypeScript and broke root pnpm lint. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
…tests The full v8 type surface from the engine interface draft now compiles in @prisma/cli-engine: defineCommand/defineSessionCommand/defineServerCommand with kind discriminants stamped by the constructors, flag/positional builders with Char alias typing, Args mapping, Outcome with exitCode-required-iff-catalogued, the PresentedResult brand, config sections, needs/help specs, envelopes, the flattened StreamEvent union, and the createCli/createTestCli/Runtime/LoadedConfig types. createCli and createTestCli throw not-implemented until the execution dispatch lands. Every compile-verified claim from the r4/r5 review records is a permanent type-test in tests/engine.type-test.ts, checked by tsc --noEmit via the package typecheck script (stale @ts-expect-error directives fail as TS2578). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
D3 of slice S1: result commands run end to end inside the package's own tests. createCli mounts the definition tree on @stricli/core@1.3.0 (exact-pinned, fully internal — no stricli type in the public surface, no process.exit, writes only to injected streams) and runs the pipeline: parse -> needs (credentials; config/dependencies/interaction land in D4/D5) -> lazy handler load -> context -> handler -> envelope -> exit code. ctx.present materializes only the active format's presentation functions at the return site. The engine injects the shared flag family (--format/--json, --log-level/-v/--verbose, -q/--quiet, -y/--yes, --interactive/--no-interactive, --color/--no-color); construction rejects reserved names/aliases, unknown groups, mount collisions, grammar violations, and exit codes outside 4-99. json mode streams one StreamEvent per line terminated by the result frame; stricli parse/route failures map to structured CLI.INVALID_ARGUMENTS / CLI.UNKNOWN_COMMAND errors with exit 2. createTestCli is the real harness: in-memory streams, injectable clock/env/tty/cwd, abort wiring, event tap, parsed stream, and the presented result. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
…, signals D4 of slice S1: the remaining execution surface per the v8 draft. - ctx.prompt (confirm/consent/select/text): --yes and Enter accept product-declared defaults; a prompt with no default halts the invocation with CLI.PROMPT_REQUIRED (errored envelope, exit 2); consent is structurally undefaultable and always halts under --yes and in non-interactive contexts; EOF cancellation maps to exit 3. Scripted harness answers via RunHooks.answers; real runs read lines from the Runtime input stream. - needs.interaction enforced in checkNeeds before the handler loads (mechanical check, not an agent barrier). - Full event vocabulary rendering in human mode (steps, progress, output channels, endpoint, status, artifact) with log-level filtering generalized beyond messages; output data lines are never filtered; remediation surfaces as nextActions at settlement, not as live transcript. - ctx.requireDependency + needs.dependencies: resolvability probe from the user's cwd; engine-phrased CLI.MISSING_DEPENDENCY install error keyed to the runtime's package manager. - Session commands: run until the signal, Result<void>, exit 0 on clean shutdown; json streams terminate with one completed result frame (result: null). - Server commands: stdio handoff, exit code passthrough, engine stays out of stdout even for pre-handoff parse failures. - Signal exit codes: 130 SIGINT, 143 SIGTERM, 3 otherwise. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
…ight Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
|
Warning Review limit reached
Next review available in: 26 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (22)
Summary by CodeRabbit
WalkthroughThis change adds the 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
✨ Simplify code
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
D5 of the S1 engine vertical: a minimal loader behind Runtime.config. loadConfig discovers prisma.config.ts in cwd (cwd only), evaluates it by dynamic import, and checks the structural defineConfig version marker ($prismaConfig). A file without the marker — a classic Prisma 7 config — yields the pinned CLI.CONFIG_MISSING_MARKER fail-early diagnostic; an unevaluable file yields CLI.CONFIG_UNREADABLE; absence is an empty LoadedConfig (section validators own absence). checkNeeds now fails every command on file-level config diagnostics, validates a command's needs.config section (validator throw settles as an engine bug, exit 1), and hands the validated value to ctx.config and server io.config; a validation failure settles as an errored envelope carrying the validator's diagnostics. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
…ains Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
… drafts Code review artifacts are never committed; the compile-verified claims survive as the permanent type-test suite in @prisma/cli-engine. Only the current normative engine-interface-draft.ts remains. References updated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
…iew artifacts Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
The slice vertical: a minimal unpublished prisma-v8 bin (tsx-run, src/v8/bin.ts, root script 'prisma-v8') assembling the engine Runtime from the real process — streams, env, TTY flags, cwd, package-manager detection, D5 loadConfig, first-signal abort / second-signal force exit — with getCredentials backed by the existing token-storage adapter in place. auth whoami is ported as an engine defineCommand with a lazy handler that calls the existing readAuthState operations layer, presents the current CLI's fields through the Block vocabulary, supplies stdout payload lines and the json envelope, and deliberately declares NO needs.credentials (parity: whoami completes when signed out). The engine's credentials early-failure path is byte-asserted through an in-test command in the harness e2e, which also pins human bytes, the json stream + terminal envelope, --quiet, errored (exit 2) and bug (exit 1) paths. Every divergence from prisma-cli auth whoami is enumerated in .drive/projects/prisma-cli-v8/assets/engine/whoami-parity-divergences.md for operator review, including the open D3-D5 interpretation items and a new D6 finding: the draft's lazy-handler annotation round trip needs an explicit type on the definition const to break TS inference circularity. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
Break the index/execution/config-loader module cycle by extracting the definition surface into an internal core module; index re-exports it, so the public API and dist layout are unchanged. Brand PRESENTED with Symbol.for so duplicate engine copies agree on it. Settle precondition failures instead of letting them escape to stricli: a rejecting getCredentials becomes a structured CLI.CREDENTIALS_UNREADABLE error (exit 2), anything else in the needs phase settles as the bug path. report() after the handler resolved no longer throws into detached async contexts: it settles as a bug while the run is live and writes a one-line internal-error note afterwards. Usage errors keep stricli's full text (first line as summary, the rest as the diagnostic's why). sniffFormat stops at the first bare -- and the stricli scanner now honors -- as the argument escape. Mask sensitive field rows in human block rendering. Reject completed exit codes outside 0 plus the command's documented set as CLI.INTERNAL_ERROR. Keep Diagnostic.fix even when it equals why. The config loader accepts only PRISMA_CONFIG_VERSION and names found vs supported otherwise. ProductManifest is now read: a mounted command whose needs.config token is not its product's configSection fails construction, and diagnostics raised for a command whose product declares docsBaseUrl get docsUrl derived as base + code (a per-raise docsUrl wins). Unify the test suites on the @prisma/cli-engine package specifier so every suite runs against the same build. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
…able Replace the fragile message-prefix match on the empty PRISMA_SERVICE_TOKEN failure with a typed EmptyServiceTokenError exported from auth-ops; the v8 whoami handler and the shell command runner both match on the class, and the whoami test mock keeps the real export instead of re-typing the literal. Restructure the v8 bin: the script entry stays thin and everything testable moves to main.ts as exported functions (package-manager detection, signal wiring over a process-like emitter, runtime assembly, exit-code propagation). A createCli construction error now prints one clean line to stderr and exits 1 instead of surfacing a bare stack, and credential token reads ignore the run signal so they still work during teardown after the first Ctrl-C. New v8-bin test suite covers all of it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
The cli package imports @prisma/cli-engine, whose exports point at the gitignored dist/, so every workflow that skipped an engine build failed on a clean checkout. A prepare script on the engine package builds dist/ during pnpm install, which every workflow already runs first. Also move @prisma/cli-engine to the cli's devDependencies: only the unpublished v8 sources import it, and the published package must not depend on a private workspace package. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
… R-A)
Operator ruling: do not dynamically import handlers. The lazy-handler
pattern (handler: () => Promise<{ default: Handler }>) is abolished for
result, session, and server commands; `handler` is now the handler
function itself. The engine drops the module-loading step and its error
handling; the whoami port inlines its handler into whoami.ts (the
handler file is deleted), which also removes the circular type-inference
friction recorded in the divergence doc. CommandHandler<D> remains for
statically imported handlers. Draft \S6 amended accordingly
(operator-authorized).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
R-B: -q/--quiet is a log-level alias (shorthand for --log-level error), consistent with --verbose: either shorthand beats an explicit --log-level given alongside it. It no longer changes presentation materialization — human mode always renders the human Blocks, and the human+--quiet → stdout-lines arm is removed. The Presentations.stdout surface stays; its purpose is an open operator question. R-C: a config-file problem no longer fails every command. File-level diagnostics (unreadable file, missing/wrong version marker) fail only commands with a needs.config section, reported as that command's early failure; commands with no config need run normally. An absent file remains a non-event. R-D: diagnostics on a successful SectionValidation are written to stderr as warning commentary in both formats, filtered by the active log level, and never enter the stream or the envelope. Draft header, \S2, \S3, and \S10 amended accordingly (operator-authorized); divergence doc \S5 rewritten and open items 1, 4, 5 updated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
Replaces the reverted cli-engine prepare-script hack with the turbo wiring used in prisma/composer and prisma/prisma. A root turbo.json defines build/test/typecheck tasks with dependsOn ^build, so any task that imports a workspace dependency's dist output gets that dependency built first. Root scripts build/test/typecheck now run through turbo, cli and compute gain typecheck scripts so the turbo task covers every package, and the CI workflows (test, pr-quality, preview-cli-package) build via the turbo graph before running dependent test and build steps. The prepare script on cli-engine is removed; nothing builds as a side effect of pnpm install anymore. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
…nups
Ruling R-F (2026-08-09): format and interactivity are independent axes.
--format/--json selects output shape only; interactivity comes from TTY
stdin detection outside CI, overridden by --interactive/--no-interactive.
An interactive json run may prompt (prompt UI on stderr, stdout stays
single-line frames); a non-interactive prompt with no default still
fails structurally. The CLI.INTERACTION_REQUIRED prose no longer blames
json. Draft header, section 4a, and NeedsSpec.interaction updated to
match.
Ruling R-G (2026-08-09): help examples never contain the binary name.
At help render time {bin} is substituted with createCli's name and an
example without {bin} gets the name prepended; examples now render in
help output under an Examples heading. whoami's examples drop the
hardcoded prisma-v8.
Cleanups from the verification review:
- C1: pin the needs evaluation order (interaction, then dependencies,
then credentials, then file-level config) with an explicit test.
- C2: remove the unreachable settles-as-bug branch in
reportAfterResolution; the test now asserts the stderr note.
- C3: align the section 6 header with ruling R-A — definitions load
their handler's import graph at startup; R9's remaining force is that
handler bodies defer heavy work to execution time.
Also apply biome's safe fix for the .drive ignore pattern in
biome.jsonc, which was the one error failing pnpm lint.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
markdownlint MD040 flags fences without a language. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
…streams.ts Stored types carry no conditional properties: definitions normalize help/args/needs/exitCodes to always-present fields, CommandFamily gains defineCommandFamily, Diagnostic and CliStructuredError always carry nextActions (empty when none), and PresentedResult materializes every presentation field. The define* specs stay ergonomic; normalization happens in the constructors. envelopes.ts and streams.ts were type groupings, not concepts: the envelopes move beside the handler definitions in commands.ts, the stream frame types into events.ts, and the stream interfaces into the runtime contract. The LogLevel alias is gone — Severity is the one severity scale (the flag stays --log-level). Flag/positional specs are phantom-typed via never-assigned optional properties instead of exported symbol brands; PRESENTED keeps its symbol brand because it enforces ctx.present as the sole constructor. Requirement-ID citations (R1-R14) in comments are replaced with the rule in plain words. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
run.ts and invocation.ts are gone. execution/engine.ts holds the Engine interface and an EngineImpl class whose methods are the execute pipeline; buildEngine constructs one. createCli lives in src/cli.ts with the Cli interface, and the test harness implementation in src/testing.ts. Every settle* function, including settleVersion and settleUnhandled, now lives in settlement.ts. Modules are named for their concept, not their mechanics: execution/events.ts (ctx.report filtering + json framing) is reporting.ts, render.ts is rendering.ts, and firstLine joins the rendering helpers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
createTestCli and TestCli leave the main entry, mirroring composer's ./testing convention: src/exports/testing.ts is the entry, wired through package.json exports, tsdown, and the tsconfig paths. The main entry additionally reflects the round-2 surface: defineCommandFamily, the normalized definition types, envelope/stream type relocations, and no FLAG/POSITIONAL/LogLevel exports. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
Tests import the harness from @prisma/cli-engine/testing, build command families with defineCommandFamily, and expect the normalized shapes: always-present nextActions on diagnostics and error envelopes, normalized help/args/needs on definitions, and the updated main-entry export list. The type-tests keep the PresentedResult brand-forgery claims and note that arg specs are phantom-typed, not symbol-branded. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
main.ts was a grab-bag under an entry name: the runtime wiring (assembleRuntime, detectPackageManager, makeOnSignal, makeGetCredentials) moves to v8/runtime.ts, tree construction (buildCli, now using defineCommandFamily) to v8/cli.ts, and main.ts keeps only the thin main(process) entry composing them. Tests follow the split and the engine's ./testing subpath. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
The normative draft reflects the round-2 rulings: normalized definition shapes (CommandHelp/CommandNeeds/CommandArgs, always-present exitCodes), defineCommandFamily, always-present Diagnostic.nextActions, one severity scale (LogLevel alias removed), phantom-typed arg specs, the normalized PresentedResult presentation, and the ./testing subpath for the harness. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
Operator ruling 2026-08-10: a subgroup is owned by exactly one command family; management-API and Composer commands never share one. The platform's cloud-project CRUD therefore leaves the project group; its new home is the open half of TML-3189. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
…mmands Operator ruling 2026-08-10: Composer's commands mount under a composer root; the platform's cloud-project CRUD keeps project. The final grammar stays open as TML-3189; moving the tree later is cosmetic. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
The base branch was changed.
The preview workflow's pnpm version step refuses an unclean tree, and turbo's per-package .turbo/ caches were untracked. Ignore them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
There was a problem hiding this comment.
Actionable comments posted: 30
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.drive/projects/prisma-cli-v8/assets/briefs/s1-handover.md:
- Around line 11-31: Update the handover to reflect completed S1 state,
including D6 completion and the current verification counts from the final
implementation. Replace the four obsolete open items covering channel rendering,
remediation events, config-error scope, and successful-validation diagnostics
with their settled rulings recorded in the parity-divergences document. Remove
guidance that could reopen resolved behavior, or mark this brief superseded and
link to the final handover.
In @.drive/projects/prisma-cli-v8/assets/engine/whoami-parity-divergences.md:
- Line 52: Update the three output code fences in
.drive/projects/prisma-cli-v8/assets/engine/whoami-parity-divergences.md at
lines 52-52, 65-65, and 73-73 to use the text language identifier on each
opening fence.
In @.drive/projects/prisma-cli-v8/assets/rollout-plan.md:
- Around line 36-40: Update the prisma7 rollout entry to specify the exact npm
dist-tag used when publishing prisma@8.0.0-rc1; if both next and rc must resolve
to that version, document the post-publish npm dist-tag add step for the second
tag, otherwise identify the single supported install channel.
In @.github/workflows/preview-cli-package.yml:
- Around line 47-52: Update the “Build workspace packages” step in the workflow
to use a single dependency-aware build path, avoiding duplicate compilation of
`@prisma/cli` and `@prisma/compute` with the later build steps around the existing
status reporting. Either remove those later package-specific builds or replace
this step with a filtered Turbo build that preserves the required status
reporting.
In `@package.json`:
- Line 10: Update the package.json build:cli script to run Turbo with the
`@prisma/cli`... filter, ensuring the CLI build includes its dependency closure
instead of invoking pnpm’s package-only build.
In `@packages/cli-engine/src/commands.ts`:
- Around line 272-282: Extend the server-command handler io type near handler to
include readonly env, matching the env surface in CommandContext. At the server
dispatch site in engine, pass invocation.runtime.env alongside cwd so server
handlers can access the runtime environment without reading process.env.
In `@packages/cli-engine/src/execution/engine.ts`:
- Around line 341-351: Update RESERVED_FLAG_NAMES in shared-flags.ts to include
"version", ensuring validateFlags rejects commands that declare a flag
intercepted by versionRequested. Leave versionRequested and other reserved flag
behavior unchanged.
- Around line 325-337: Validate the value returned by the server handler before
assigning it to state.settledExitCode in executeServer. Require a finite integer
in the valid process-exit range 0–255, and route invalid values through the same
engine-boundary failure handling used by settleCompleted rather than returning
them to the host.
In `@packages/cli-engine/src/execution/needs.ts`:
- Around line 46-130: Split checkNeeds into four focused helpers for
interaction, dependencies, credentials, and configuration, each returning
NeedsOutcome | undefined when that requirement fails and undefined otherwise.
Preserve the current check order and behavior, including credential error
handling and config diagnostic validation, then have checkNeeds invoke the
helpers sequentially and return the first defined outcome before falling back to
the existing ok result.
- Around line 191-198: Update dependencyResolvable to normalize cwd to an
absolute path before passing the probe filename to createRequire, using
resolve(cwd, "__cli_engine_probe__.js") or equivalent. Preserve the existing
dependency-resolution return behavior, and add coverage proving a relative
working directory correctly resolves an available dependency.
In `@packages/cli-engine/src/execution/prompts.ts`:
- Around line 173-174: Replace the nested ternary assigned to hint in the prompt
logic with an explicit conditional or lookup-based implementation, preserving
the existing "(y/n)", "(Y/n)", and "(y/N)" results for undefined, truthy, and
falsy fallback values respectively.
In `@packages/cli-engine/src/execution/rendering.ts`:
- Around line 152-160: Update withDocsUrl to normalize the join between
state.docsBaseUrl and diagnostic.code, ensuring exactly the required separator
is present when constructing docsUrl. Preserve the existing early returns for
diagnostics with docsUrl or missing docsBaseUrl.
- Around line 11-16: Update STEP_OUTCOME_SYMBOL to use the step outcome union as
its Record key type, and update TONE_SYMBOL to use Block’s summary tone union
instead of string. Preserve the existing symbol mappings while making
unsupported outcomes or tones fail compile-time checks like DIAGNOSTIC_SYMBOL.
In `@packages/cli-engine/src/execution/settlement.ts`:
- Around line 175-192: Update settleBug to preserve internal-error stack details
when state.logLevel is verbose: write the original Error stack to stderr or
include it in the emitted diagnostic meta, while retaining the existing
firstLine summary and behavior for non-Error causes.
In `@packages/cli-engine/src/execution/stricli-adapter.ts`:
- Around line 44-50: Update parseNumberInput to reject blank or whitespace-only
input before converting it with Number, so empty numeric arguments throw the
existing usage error instead of being accepted as zero; preserve valid numeric
parsing and the current non-finite validation.
In `@packages/cli-engine/src/exports/index.ts`:
- Around line 1-9: Update the main barrel in
packages/cli-engine/src/exports/index.ts to re-export ok, notOk, okVoid, Result,
Diagnostic, NextAction, CliStructuredError, and CliErrorEnvelope from the
existing ./protocol module, while preserving ./protocol as the shared
cross-package/process entrypoint.
In `@packages/cli-engine/src/exports/testing.ts`:
- Line 5: Configure a scoped lint override for the noBarrelFile rule covering
src/exports/, including the testing.ts, index.ts, and protocol.ts entrypoints.
Keep the existing re-export hub structure unchanged and ensure the override
suppresses only this rule within that directory.
In `@packages/cli-engine/src/presentation.ts`:
- Around line 81-88: Update the row object type in the fields member of Block so
its label, value, and optional sensitive properties are explicitly readonly,
preserving the existing ReadonlyArray and union structure.
In `@packages/cli-engine/src/protocol.ts`:
- Around line 63-89: Add an optional cause field to the options accepted by the
CliStructuredError constructor, pass it through the Error superclass
initialization, and leave toEnvelope unchanged so cause remains out of the
serialized envelope.
In `@packages/cli-engine/src/testing.ts`:
- Around line 132-154: The abort wiring around engine.execute must preserve
signals delivered before runtime.onSignal subscribes. Update the signal delivery
flow and runtime.onSignal subscription so an early abort reason is stored as
pending and replayed immediately when a listener is registered, while retaining
normal delivery for signals received after subscription.
In `@packages/cli-engine/tests/command-families.test.ts`:
- Around line 139-141: Update the stderr expectation in the command-family test
to use one template literal containing both the error text and the docs URL,
removing the string concatenation while preserving the exact expected output.
In `@packages/cli-engine/tests/engine.type-test.ts`:
- Around line 375-389: Replace the double-cast undefined value in
runtimeShape.stdin with a realistic async-iterator InputStream stub, following
the existing pattern in config.test.ts. Remove the InputStream import if
runtimeShape no longer references it elsewhere.
In `@packages/cli-engine/tests/execution.test.ts`:
- Around line 521-555: Extract the repeated Runtime literals in the affected
tests into a local makeRuntime(overrides) helper returning a complete default
Runtime. Move shared stdin, stderr, cwd, env, isTty, exit, onSignal, config,
getCredentials, and packageManager setup into the helper, then replace all three
inline constructions with makeRuntime calls that override only test-specific
fields such as stdout, credentials, interactive state, and diagnostics.
In `@packages/cli-engine/tests/lifetimes.test.ts`:
- Around line 405-416: Update the lsp fixture handler to flush the TextDecoder
after the stdin loop and append any final decoded text to received. Compute the
Content-Length header from the UTF-8 byte length of the complete response body
rather than received.length, while preserving the existing response framing.
- Around line 228-235: Update the floating promise created by stuckCli().run in
the “a second SIGTERM exits 143” test to attach an explicit rejection handler,
while preserving the test’s existing signal-delivery and exit assertions.
In `@packages/cli-engine/tests/prompts.test.ts`:
- Around line 116-125: Add test cases alongside “CI suppresses interactivity
even on a TTY” covering CI values "1" and "false". Assert the intended detection
behavior for each value, including the resulting prompt interaction, exit code,
presented data, and stderr as appropriate, so future changes to the CI detection
rule remain pinned.
In `@packages/cli-engine/tests/protocol.type-test.ts`:
- Around line 35-36: Update the envelopeFromError type assertion to use the
existing MutuallyAssignable helper, comparing CliErrorEnvelope with
ReturnType<CliStructuredError["toEnvelope"]> so the test verifies assignability
in both directions.
In `@packages/cli/src/v8/runtime.ts`:
- Around line 47-53: Update makeGetCredentials to distinguish an unset
SERVICE_TOKEN_ENV_VAR from a present-but-blank value: detect variable presence
before trimming, return unauthenticated for blank/whitespace values, and only
call FileTokenStorage.getTokens when the variable is truly absent.
In `@packages/cli/tests/v8-bin.test.ts`:
- Around line 144-150: Update the test around makeGetCredentials so it no longer
creates an unused local AbortController; either remove the abort setup and
rename the test to describe a normal stored-token read, or pass the aborted run
signal through the actual credential-retrieval runtime and retain the
aborted-signal behavior assertion.
In `@turbo.json`:
- Around line 4-23: Include the shared packages/tsconfig/** files in Turbo cache
hashing by adding them to globalDependencies or to the explicit inputs for the
affected build, test, and typecheck tasks. Update the task configuration around
build, test, and typecheck so changes to `@repo/tsconfig` are detected without
altering other task behavior.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: c0e13edd-4761-4fe3-b747-dff1dd0f0cac
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (73)
.drive/projects/prisma-cli-v8/assets/briefs/s1-handover.md.drive/projects/prisma-cli-v8/assets/engine/engine-interface-draft.ts.drive/projects/prisma-cli-v8/assets/engine/whoami-parity-divergences.md.drive/projects/prisma-cli-v8/assets/rollout-plan.md.drive/projects/prisma-cli-v8/design-notes.md.drive/projects/prisma-cli-v8/plan.md.drive/projects/prisma-cli-v8/plans/s1-engine-vertical.md.drive/projects/prisma-cli-v8/spec.md.drive/projects/prisma-cli-v8/specs/s1-engine-vertical.md.github/workflows/pr-quality.yml.github/workflows/preview-cli-package.yml.github/workflows/test.ymlpackage.jsonpackages/cli-engine/package.jsonpackages/cli-engine/src/args.tspackages/cli-engine/src/cli.tspackages/cli-engine/src/command-family.tspackages/cli-engine/src/commands.tspackages/cli-engine/src/config-loader.tspackages/cli-engine/src/config-section.tspackages/cli-engine/src/context.tspackages/cli-engine/src/events.tspackages/cli-engine/src/execution/command-context.tspackages/cli-engine/src/execution/command-tree.tspackages/cli-engine/src/execution/engine.tspackages/cli-engine/src/execution/needs.tspackages/cli-engine/src/execution/prompts.tspackages/cli-engine/src/execution/rendering.tspackages/cli-engine/src/execution/reporting.tspackages/cli-engine/src/execution/settlement.tspackages/cli-engine/src/execution/shared-flags.tspackages/cli-engine/src/execution/stricli-adapter.tspackages/cli-engine/src/exports/index.tspackages/cli-engine/src/exports/protocol.tspackages/cli-engine/src/exports/testing.tspackages/cli-engine/src/presentation.tspackages/cli-engine/src/protocol.tspackages/cli-engine/src/runtime.tspackages/cli-engine/src/testing.tspackages/cli-engine/tests/command-families.test.tspackages/cli-engine/tests/config.test.tspackages/cli-engine/tests/engine.test.tspackages/cli-engine/tests/engine.type-test.tspackages/cli-engine/tests/events.test.tspackages/cli-engine/tests/execution.test.tspackages/cli-engine/tests/fixtures/config/marked/nested/.gitkeeppackages/cli-engine/tests/fixtures/config/marked/prisma.config.tspackages/cli-engine/tests/fixtures/config/unmarked/prisma.config.tspackages/cli-engine/tests/fixtures/config/unreadable/prisma.config.tspackages/cli-engine/tests/fixtures/config/wrong-version/prisma.config.tspackages/cli-engine/tests/lifetimes.test.tspackages/cli-engine/tests/prompts.test.tspackages/cli-engine/tests/protocol.test.tspackages/cli-engine/tests/protocol.type-test.tspackages/cli-engine/tsconfig.jsonpackages/cli-engine/tsdown.config.tspackages/cli-engine/vitest.config.tspackages/cli/package.jsonpackages/cli/src/lib/auth/auth-ops.tspackages/cli/src/shell/command-runner.tspackages/cli/src/v8/auth/whoami.tspackages/cli/src/v8/bin.tspackages/cli/src/v8/cli.tspackages/cli/src/v8/main.tspackages/cli/src/v8/runtime.tspackages/cli/tests/v8-bin.test.tspackages/cli/tests/v8-whoami.test.tspackages/cli/tsconfig.jsonpackages/compute/package.jsonpackages/compute/tsconfig.jsonpackages/tsconfig/base.jsonpackages/tsconfig/package.jsonturbo.json
Review round 2 correctness fixes: - server-command io now carries env from Runtime.env, so server handlers never need process.env (draft interface amended to match) - a server handler's returned exit code is validated (integer, 0-255); anything else settles as CLI.INTERNAL_ERROR with exit 1 - 'version' joins RESERVED_FLAG_NAMES: --version is intercepted before parsing, so a declared version flag could never be reached - the dependency probe resolves Runtime.cwd to an absolute path so a relative cwd no longer fails every dependency check - docs URLs join base and code with a separator-safe join, and a family's docsBaseUrl must parse as a URL at construction - numeric flag values that are empty or whitespace-only are usage errors instead of parsing to 0 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
Review round 2 type fixes: - STEP_OUTCOME_SYMBOL and TONE_SYMBOL are keyed by their union types, so a new outcome or tone fails typecheck instead of rendering the text 'undefined' - Block's fields rows are readonly like every other Block member - CliStructuredError accepts an optional cause and forwards it to Error, keeping the envelope unchanged Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
Review round 2 lint fixes: - checkNeeds split into one helper per need (interaction, dependencies, credentials, configuration) with the check order unchanged, clearing the cognitive-complexity notice - the confirm-prompt hint no longer uses a nested ternary - noBarrelFile is switched off for src/exports/, the declared tsdown entrypoints, and stays on everywhere else - the stderr expectation in command-families.test.ts is one template literal Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
Review round 2 auth fixes: - makeGetCredentials now treats a set-but-blank PRISMA_SERVICE_TOKEN as invalid configuration by throwing EmptyServiceTokenError, matching readAuthState, instead of silently reading the stored OAuth token - the credentials test that claimed to exercise an aborted run signal never did (the AbortController was local and unused); it is renamed to the stored-token read it performs, and a blank-token test is added Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
Review round 2 build fixes: - build:cli runs through turbo, so a clean checkout builds @prisma/cli-engine before @prisma/cli instead of compiling against a missing engine dist - the shared @repo/tsconfig/base.json joins the build, test, and typecheck inputs via $TURBO_ROOT$, so editing it busts dependents' caches (explicit inputs replace default file discovery, and the tsconfig package has no build task to contribute a hash) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
…s, telemetry, and real prompts — the foundations for porting the platform CLI onto the engine (#130) You can now be signed in to several workspaces at once, and say which one you are working in without signing in again. ``` $ prisma auth workspace list ℹ Listing your workspace sessions on this machine. name id status Acme Inc wksp_acme current Globex wksp_globex $ prisma auth workspace use Globex ℹ Switching the current workspace session. previous: Acme Inc workspace: Globex ✔ Current workspace session updated. $ prisma auth workspace use wksp_nope ✖ [AUTH.NO_SESSION_FOR_WORKSPACE] You have no session for workspace 'wksp_nope'. → Sign in and pick 'wksp_nope' in the browser: prisma auth login ``` Today's CLI stores the same per-workspace tokens but only really models one "active" workspace: it re-fetches workspace names on every read, ends a single workspace through `auth logout --workspace`, and can leave entries behind that nothing cleans up. The runs above are the new shell against a real credential file — the second command changed which session is current without opening a browser, and the third failed at exit 2 with an error that tells you the only thing that can fix it. ## The decision We are replacing this repo's commander-based shell with `@prisma/cli-engine` (merged in #129). Every platform command will be re-mounted on the engine and the old shell will be deleted. Before that port can start, the engine has to be production-ready and the auth family has to sit on something the rest of the port can build on. **This pull request builds those foundations and proves them by porting the first command group, `auth`.** The port itself follows in sibling pull requests for resources, services, and init plus shell removal. ## The foundations **An API client on the command context.** Nearly every platform command calls the management API. Rather than each command constructing its own SDK client, the engine builds `ctx.api` once per run, lazily, for the session that process is acting as. A token refreshed mid-run is picked up on the next request, and an expired session surfaces as the standard sign-in error instead of a crash. The test harness accepts a fake client, which is the single mock seam every ported command will use. **Real interactive prompts.** On a terminal, prompts render through `@clack/prompts` — the same library today's CLI uses, so the upcoming `init` wizard keeps its current feel. In tests, pipes, and CI a plain line-based renderer runs instead, so no test depends on terminal rendering. Clack is internal to the engine and invisible in its public API. Its spinners are deliberately unused, because they install process-global signal handlers. **Telemetry, identical to the ORM CLI's.** The detached sender process, the consent configuration with a shared installation id, and the value-free command snapshots move into this repo and report through one engine hook that fires once per command with its id, exit code, and duration. `telemetry status|enable|disable` are ported. Flag values, arguments, and paths never reach the wire, which hostile-input tests hold down. **One version number and prisma/prisma's release machinery.** This repo adopts prisma/prisma's versioning model unchanged: a single lockstep version, now `8.0.0-rc.1`, committed in every manifest and advanced only by a maintainer running `pnpm bump-version` and merging the resulting pull request. `prisma --version` reports it. Merging a bump publishes that version to `latest`; ordinary merges publish `-dev.N` builds. The old publish-time arithmetic against npm dist-tags is deleted. `@prisma/compute`, an app-runtime library slated for extraction to another repo, keeps its own line. **Merge order: merge #131 first.** It pre-sets the root version on `main`, so merging this pull request is a version no-op to the publish workflow and ships only a development build. The first real `latest` release then happens through a deliberate release bump. ## The auth rework: sessions, and the thing that is not one The auth family no longer keeps a pile of credentials with a pointer at one of them. It separates three things that were previously one. A **session** is a stored logged-in-ness for one workspace — at most one per workspace. It is the only thing called a session: what `auth workspace list` lists, what `use` selects, what `logout` ends. The **selection** is a separate scalar of stored state: which session is used where a session is needed. And the **active credential** is what a given process authenticates as, which is either the selected session's or the one `PRISMA_SERVICE_TOKEN` supplies. That third thing is why this went through two revisions. The first modelled the environment token as a session, and it produced four defects that were all the same defect: a `source` field whose job was to say "this one is not really a session", a hardcoded `current: true` that gave the word two meanings, an empty-string workspace id because a non-session was forced to carry a session's key, and a guard rejecting the non-session from APIs that only take sessions. Separating the three made all four stop existing rather than each need a fix. A new component, the credential manager, owns the state. It is the only thing that touches the credential file, and it is also what the platform SDK writes through, so a token refreshed on a 401 lands under the same rules as a fresh login. The engine owns the API client; the manager never hands a token to a command, and never talks to the user or opens a browser. Four decisions are worth knowing: - **A process pins its decision once.** Which credential a command acts as is settled at its first read and does not move for the life of that process, though the material behind it is re-read every time — so another shell switching workspaces mid-run cannot redirect a running command, while a token another process rotates is still picked up. - **Refresh follows the credential, not where it came from.** A credential refreshes if it has a refresh token, full stop. There is one API client over whatever storage the manager hands out: file-backed for a stored session, memory-backed for an environment credential, which is how a 401 that could never be renewed reports that the token was rejected instead of telling a CI job to retry a permanent failure. - **Concurrent refreshes are left to the auth service.** Refresh tokens are single-use with a ten-second reuse grace, and rotation does not invalidate a pair that was already issued, so two CLI processes refreshing the same session both succeed and the file ends up holding a working pair. There is no client-side coordination beyond deduplicating within one process. A cross-process test drives two real refreshes through a scripted token endpoint that reproduces the grace. - **The state file is one file, at the same path as today's.** Writes are atomic — temp file, fsync, rename, mode 0600 — and a short advisory lock covers read-modify-write so two mutations cannot lose each other. No network call ever runs while that lock is held. Reads never write and take no lock. - **The old store migrates by being read, not rewritten.** Existing credentials are adopted as sessions on read; nothing is written until your first mutation, at which point the file is rewritten in the new shape. That is a one-way door: from then on a still-installed older CLI reads as signed out. We chose that over two auth worlds diverging silently, because `prisma auth login` fixes it and nothing else has to. The commands keep their existing names, which the session model makes honest: `auth login`, `auth logout`, `auth whoami`, and `auth workspace list|use|logout`. `auth workspace use` selects among the sessions you already hold and never creates one, because the consent screen cannot be told which workspace to grant — you pick it in the browser. The engine grew three things the auth commands needed, all of which the rest of the port will use. A repeatable global `--confirm <value>` flag carries consent for destructive work: interactively you type the value to confirm, non-interactively you pass it exactly, and `--yes` deliberately cannot grant it. `ctx.openUrl` opens a browser and degrades to printing the URL. `prompt.browserWait` waits for a browser round trip on a terminal and returns a structured "this needs interaction" error at exit 2 everywhere else. ## What changes for users The published binary still runs the old shell; the engine-based shell is an unpublished development binary until the port finishes. For the commands ported here, every behavioural difference is written down in the [divergence record](.drive/projects/prisma-cli-v8/assets/s2/parity-divergences.md) rather than left to be discovered. The ones most likely to affect someone: - `auth logout --workspace <ref>` is gone. `auth workspace logout <ref>` is the one way to end a single session, and `auth logout` now ends every session and reports how many, reaping orphaned entries the old code could leave behind. - **Workspace switching works while `PRISMA_SERVICE_TOKEN` is set**, where the old CLI refused it. The variable supplies the credential this process uses; it does not occupy a slot, so changing stored state is coherent and every mutation succeeds, each saying the environment credential stays in force until you unset it. - **Ending a session is idempotent.** A workspace you never had is still an error, raised when the reference fails to resolve. But if another `prisma` process removes the session between your command reading it and writing, you now get exit 0 rather than an exit 2 telling you something that is no longer true. - Error codes are dotted (`AUTH.NO_SESSION_FOR_WORKSPACE`), and several failures that exited 1 now exit 2, which means "could not complete" rather than "crashed". - `auth whoami` returns the active credential rather than an auth-state snapshot: workspace, user, source, and expiry. Identity comes from the credential's own claims, enriched from `/v1/me` when that answers within a short deadline. A service token whose subject names a workspace reports no user, where a naive reading would have put `workspace:<id>` in the user field. - Workspace names are no longer refreshed on every read. A name is fetched once when the session is created, so a workspace renamed in the console keeps its local name until you next log in to it. Reads are entirely offline. - Scripted consent uses `--confirm <value>`. The mock-only login flags `--provider`, `--user`, and `--workspace` do not port. ## Verification 840 CLI tests, 258 engine tests, 97 telemetry tests, typecheck, and lint all pass on Linux, macOS and Windows. Beyond the usual coverage, the credential manager is tested across real processes on a real filesystem: two processes mutating at once both land, a crashed process's lock is taken over, a running process keeps its pinned session when another switches the marker, and two processes really refresh the same session through a scripted token endpoint. A filesystem spy asserts that no read path writes, including migration adoption, and a leak scan seeds known secrets and asserts they never appear in output, debug logs, error metadata, envelopes, or a worker process's stderr. ## Alternatives considered - **Tracking identity in the stored state**, so the CLI could tell you a session belongs to a different account: rejected. The credential file has always been identity-blind and nothing today depends on it being otherwise. Identity stays a read-time decode in `whoami`. - **Letting `auth workspace use` create a session** by opening the browser: rejected, and it is not actually possible — the authorize request carries no workspace parameter, so the CLI cannot ask for a particular workspace. Creating a session belongs to `auth login` alone. - **Coordinating refreshes across processes** with an epoch or a longer-held lock: rejected once the auth service's reuse grace was confirmed. The server absorbs the race, so the machinery would only add ways to fail. - **A separate auth package** instead of a module: rejected — the boundary matters, the packaging does not, and there is no second consumer. - **A per-family context-extension mechanism** instead of putting the API client on the context: rejected. This engine serves Prisma specifically, and indirection to avoid naming our own API bought nothing. - **Arktype for the telemetry payload guard**, which the ORM uses: a hand-rolled six-field guard, proven equivalent field by field, avoids adding a published dependency. - **Sending telemetry before the command runs**, which is the ORM's timing: replaced by reporting at completion, so exit codes and durations are accurate. The privacy notice still prints first. The trade-off is that a crashed process reports nothing. The remaining open design questions are collected in [s2-overview.md](.drive/projects/prisma-cli-v8/specs/s2-overview.md). --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This is what writing a command looks like when this PR's engine owns everything between
argvand the exit code:The command declares words and a handler; the engine does the rest. The same definition produces the human card on stderr with pipeable payload lines on stdout, and — with
--json, or automatically when stdout is not a TTY — a machine stream ending in one result envelope:{"kind":"result","envelope":{"ok":true,"commandId":"auth.whoami","result":{"authenticated":false,...},"exitCode":0,"diagnostics":[],"nextActions":[{"kind":"run-command","label":"Sign in","command":"prisma-cli auth login"}]},"commandId":"auth.whoami","timestamp":"..."}The decision
Prisma ships three CLIs today (
prisma-next,prisma-composer,@prisma/cli) with three config files and three unrelated help/output/error dialects. We are consolidating them into oneprismabinary, and this PR makes the foundational move: a single library,@prisma/cli-engine, owns every CLI mechanic — parsing, preconditions, context, presentation, envelopes, exit codes, prompts, signals, config loading — so that a product package contributes only command definitions, handlers, and words. To prove the design is real rather than aspirational, the PR also ports one real command (auth whoami) end to end through an unpublished bin.How the engine models a command
A command settles like a promise. It either completes — possibly unsuccessfully: findings travel as typed diagnostics inside a completed envelope with a documented exit code — or it errors, aborting with a structured error the engine renders. This one distinction drives everything downstream: envelopes, exit codes (0 completed / 1 bug / 2 errored / 3 user cancel / 4–99 documented / 130,143 signals), and rendering.
Around that core, the engine owns:
--format human|json(auto-json on non-TTY stdout). Human mode: presentation to stderr, machine-usable payload lines to stdout — human mode is pipe-clean. Json mode: one frame per line on stdout, exactly one terminal envelope.needs). Credentials, config sections, optional dependencies, interactivity — checked before the handler loads, failing early with engine-phrased structured errors.--yes;consentstructurally undefaultable), events (a fixed vocabulary with a per-commanddataextension), log levels (--quietand--verboseare aliases on one severity scale).prisma.config.tsis discovered and evaluated once;defineConfigstamps a version marker; a Prisma 7 config fails early with a typed migration error. A broken config only fails commands that need config.exitarrive injected via aRuntime. First Ctrl-C aborts gracefully; a second force-exits. This is also what makes it fully testable.Products plug in as a
CommandFamily— the unit of contribution and ownership: commands by name, at most one config section, a docs base URL. The shell owns the tree (paths, groups); a command may only require its own family's config section, enforced at construction.Proving it
@prisma/cli-engine/testingshipscreateTestCli: argv-in/bytes-out harness testing with injected credentials, config, clock, prompt answers, and signals. The package's 145 tests byte-assert human output, json frames, exit codes, prompt semantics, signal handling, and config loading — including permanent compile-verified type-tests (a stale@ts-expect-errorfails the build).prisma-v8(unpublished, tsx-run:pnpm prisma-v8 auth whoami) mounts the ported command over the existing token storage. Behavioral differences fromprisma-cli auth whoamiare enumerated, not discovered: the divergence record.What this PR does not do
Nothing ships to users: the engine is unpublished, the bin is not wired into any package's
bin, and the published@prisma/clibuild is untouched. The other ~60 platform commands, auth-library extraction, Composer and ORM adoption, and the release pipeline are later slices (plan).Alternatives considered
handler: () => import(...)): rejected — handlers are direct function references; the indirection bought startup weight savings we didn't need at the cost of a circular typing pattern.Result-wrapped prompt returns: rejected — prompts return plain values or throw; the engine settles failures uniformly (cancel → exit 3).The full interface record, including every amendment made during implementation, is the normative draft at .drive/projects/prisma-cli-v8/assets/engine/engine-interface-draft.ts; requirements live in docs/architecture/cli-engine-requirements.md (merged via #128).
🤖 Generated with Claude Code