Port init, delete the Commander shell: the CLI runs entirely on the engine - #139
Merged
Conversation
A self-contained handover for an agent with no context on this project: port `init` and `version` onto the engine, delete the commander shell and the fixture machinery, cut the shipped binary over, and close the platform port. Written to be actionable rather than a summary. It names the two pull requests that must land first and why starting early means rebasing a very large deletion across moving branches; separates the one thing that is blocked on an operator ruling (how the shipped binary reads a TypeScript config file, since the loader currently relies on tsx) from everything that proceeds without it; sizes the deletion; and calls out the knot where the auth module still constructs the old shell's error class, so the shell it is meant to outlive cannot simply be deleted underneath it. It also flags that the slice contract contradicts itself mid-sentence on the binary cutover — a previous author catching their own error — and points at the question ledger as authoritative there. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
…tories
Operator ruling, 2026-08-11. The question was how the shipped binary
reads `prisma.config.ts` when it runs on ordinary Node and our loader
does a plain dynamic import that only works under tsx. It was recorded
as an open design choice between jiti, esbuild-register, and requiring
a TypeScript-capable runtime.
It is not a design choice. prisma/prisma and prisma/composer both
solved it already and identically, with `c12`: a dependency, imported
dynamically at the call site, called as
`loadConfig({ name, cwd, configFile? })`, with `typescript` as a peer.
`c12` evaluates TypeScript through `jiti`, which is the part that makes
it work on plain Node, so the dependency declarations are as much of
the answer as the call.
This unblocks the binary cutover in the last platform slice, whose
contract said not to start that work until the question was ruled. The
contract also contradicted itself mid-sentence on this point — a
previous author catching their own error and leaving both halves in —
and that paragraph is replaced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
…s asking it The brief told the next agent to request a ruling on how the shipped binary reads a TypeScript config file, and to work around the block meanwhile. That question is answered: copy prisma/prisma and prisma/composer, which both use `c12`. The section now says what to copy — including the dependency declarations, since `c12` evaluates TypeScript through `jiti` and that is the part that makes it work on plain Node — and the two discovery behaviours worth carrying over. Nothing in the slice is blocked now, so the ordering advice that worked around it is gone too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
…nary can read it at all The loader did a plain dynamic import of the user's prisma.config.ts. That works under tsx and nowhere else, so the binary we ship — ordinary Node — could not read the config file the CLI is built around. On a Node with type stripping off it fails with `Unknown file extension ".ts"`; on a current Node it fails on the first piece of syntax stripping cannot handle, such as an enum. prisma/prisma and prisma/composer both solved this already and identically, so this copies them rather than choosing: c12, imported dynamically at the call site so a run with no config file never pays for it. jiti, which c12 pulls in and which does the transpiling, is what makes plain Node work. Only the evaluation step changes. Discovery is still cwd-only, absence is still an empty config rather than an error, the $prismaConfig marker check is unchanged, and failures are still diagnostics rather than exceptions. Every c12 feature beyond "evaluate this one file" is switched off explicitly, most importantly `extend: false` — c12 treats a top-level `extends` key as an instruction to merge another config layer, where our model says every top-level key is a section name, so a user with a section called `extends` would have had it silently eaten. `omit$Keys: false` for the same reason: the default is already correct but a change to it would strip $prismaConfig and turn every valid config into a missing-marker error. The test that matters spawns plain node, with no tsx, against a real TypeScript config, and asserts both that a direct import fails and that the config comes back. Two cases, because a current Node can strip simple annotations: one with stripping disabled, one with an enum. Reverting the loader fails both. No existing test changed. One was added to pin `extend: false` and to record the one observable difference: c12 merges through defu, so the object we receive is a fresh merge rather than the frozen export. Arrays, Dates and class instances keep their prototypes; nested plain objects are new objects. Nothing reads identity, but it is now written down. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
|
Important Review skippedToo many files! This PR contains 320 files, which is 220 over the limit of 100. To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch. Upgrade to a paid plan to raise the limit. Usage-priced reviews support at most 300 files. ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (320)
You can disable this status message by setting the 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 |
… not an equivalent one Compared line by line against prisma/composer's `loadAppConfig` and prisma/prisma's `config-loader`. Ours had drifted in three ways. It passed five options neither reference passes — dotenv, envName, giget, omit$Keys and extend — reasoned out from c12's documentation rather than copied. Four are now gone. The call is composer's: explicit configFile, cwd at that file's directory, and the three lookups it turns off, with prisma/prisma's dynamic import so a run without a config file never loads c12 or jiti. It passed the bare cwd where both references pass the config file's own directory. Now the same. And it was missing a check both references have: c12 is asked for one exact path and must not answer with another. prisma/prisma compares the resolved path, composer compares realpaths. Added. One deviation remains and it is forced. Both references describe a config with a fixed set of keys; ours says every top-level key is a section name. c12 reads `extends` as an instruction to merge another config layer, so with their options exactly, a user's section called `extends` is silently consumed. That is demonstrated rather than argued: with `extend` left at its default the existing test fails, expecting `['extends','values']` and receiving `['values']`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
The second of the two commands still on the old shell. It reports the same three things it always did — the CLI's own version, the Node version, and the platform — and now does it through the engine, so it gets the shared flag family, the json envelope and the exit-code contract for free. Two changes a user can see. The invocation kind is gone. The old command reported whether it had been run via npx, bunx, a global install or a dev checkout, and it worked that out largely from process.argv[1]. Handlers cannot read argv on this engine — the runtime owns it and does not pass it through — so the field has no honest successor. Deriving it from environment variables alone would still answer for npx and bunx but would quietly answer "unknown" where it used to say "global", and a field that is sometimes wrong is worse than one that is absent. The slice contract and the handover brief both name only version, node and platform, so this matches what was asked for; it is in the divergence list for sign-off. Human mode now also writes the fields to stdout, where the old command wrote only to stderr. That is the engine-wide pattern the ported commands already follow, and it is what makes the output pipeable. The version read is split so the new command does not depend on the shell that is about to be deleted: readCliVersion returns the version or undefined, getCliVersion keeps throwing the old CliError for the old shell, and the engine command raises a structured VERSION.UNAVAILABLE instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
# Conflicts: # packages/cli/src/v8/cli.ts
wmadden-electric
force-pushed
the
s2d-init-and-retirement
branch
from
August 11, 2026 09:23
dda94d8 to
b74253c
Compare
commit: |
`init` has two flags that need three states: `--link`, `--no-link`, and neither, which means "ask me". The same goes for `--install`. A command could not express that. `flag.boolean` produces a stricli boolean with a default of false, so `--no-link` and saying nothing arrive identically, and the wizard would silently link a directory for someone who asked it not to. The engine already uses the right spelling — `optional` with `withNegated` — for its own `--interactive` and `--color`. It just was not reachable from a command. `flag.optionalBoolean` exposes it, and follows the shape `flag.optionalString` already set for strings. The alternatives both changed a documented flag. Spelling it `--link <yes|no>` breaks every script and doc line that says `--no-link`, and adding a separate `--skip-link` leaves `--no-link` parsing as stricli's negation and quietly meaning "ask me" — a flag that works and does the wrong thing. Pinned at both levels: the type test proves all three states reach the handler and that a plain boolean flag still cannot be undefined, and the runtime test proves `--flag`, `--no-flag` and absence produce true, false and undefined. Collapsing the adapter case back to a plain boolean fails both. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
The last command on the old shell, and the hardest one in the product: a wizard that picks a framework, writes a compute config, optionally installs types, optionally links the directory to a Project, and optionally installs the agent skill. It now runs on the engine's prompt surface and works interactively, under --yes, non-interactively, and when cancelled. Three decisions worth reading. **init's link step is `project link`.** The legacy wizard called `runProjectLink`, which is precisely the controller that became the v8 `project link` command. Rather than grow a second project picker, the handler body of `project link` is extracted as `linkDirectoryToProject` and both call it. So the picker, the "create a new Project" and "cancel" choices, the duplicate-name labels, the pin write and the .gitignore update are one implementation, not two that drift. Making that function ignore its explicit ref fails tests in both commands, which is what proves the sharing is real. **--format becomes --config-format.** Legacy `init --format <ts|json>` chooses the format of the config file it writes. The engine reserves --format globally for the output format. The engine's shared flags win, so the flag is renamed; values and behaviour, including the JSON to TypeScript conversion path, are unchanged. **The three optional steps default to no.** The engine has one knob where the legacy CLI had two: a preselected answer when it asks, and a separate behaviour when nobody can be asked. `prompt.confirm`'s default serves both, because --yes and non-interactive take the same branch and a handler cannot read TTY state. Defaulting them to yes would make `init --yes` in CI run a package install, call the Management API and write agent-skill files into the repo, none of which it does today. Defaulting to no keeps unattended runs behaving exactly as they do now. The cost is that an interactive user presses y rather than Enter, and the prompts read (y/N) where they read (Y/n) — a one-line flip per prompt if the other trade is preferred. init mounts as shell-owned, beside version and telemetry: it writes a local compute config and needs neither a config section nor a platform docs base. When a compute family arrives it should move into it. Eighteen user-visible differences are recorded for sign-off, including that a cancelled prompt now ends the run at exit 3 where the legacy command recorded a decline and exited 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
…for users Twenty differences between the shipping commands and their ports, in the shape S2b's list uses, so the closing dispatch can fold the four lists into one document. Two need a decision rather than a nod, and are marked: the version command loses its invocation field because the engine does not hand a handler raw argv, and init's three optional steps now default to no because the engine has one knob where the legacy CLI had two. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
wmadden-electric
force-pushed
the
s2d-init-and-retirement
branch
from
August 11, 2026 10:04
b74253c to
cc4d2d9
Compare
The contract asked for `version` to be ported. It should not be, and porting it was my mistake. `--version` is already an engine surface — a pre-parse fast path that prints the version in human mode and emits a version result frame in json. The command inventory's own note says the port should unify the two rather than carry both forward. I read that note and built the second surface anyway. Nothing the command added survives scrutiny. The invocation kind (dev/npx/bunx/global/unknown) had exactly one consumer in the codebase: the presenter that printed it. It came from process.argv[1] — the process inspecting how it was launched — which is the thing the engine withholds argv from handlers to prevent. Keeping it would have meant piping an environment fact through the runtime into a command, and commands do not read the environment that way. Node version, platform and arch are already gathered by the command that needs them: feedback builds its own context for bug reports. So `prisma-cli version` goes and `--version` answers instead. This makes `version` a ruled removal for the grammar completeness check, alongside `service build`, `service deploy`, `service run` and the mock-only login flags — the check must exclude it or it will report the command missing against the inventory. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
Three commands across two branches independently reached straight through to `process` for the same reason: nothing else told them what they were running on. v8/init/agent-setup.ts process.platform === "win32" v8/agent/skills-cli.ts process.platform === "win32" (on #132) v8/feedback.ts process.version/platform/arch (on #132) Three agents making the same move is a missing surface, not three oversights. Handlers already take the working directory and the environment from the context precisely so they never touch process globals, and there was no equivalent for the machine. `Runtime.host` carries it and `ctx.host` hands it to commands. It is deliberately not node-shaped: the field is `runtime: { name, version }`, so bun and deno describe themselves rather than being flattened into something called nodeVersion. That naming is what R4 asks for, and it is what a bug-report payload should be carrying. This is not a way around the engine's constraints. What a command may not do is infer its situation from the environment — sniff argv to guess how it was launched, read process.env behind ctx.env's back. What it legitimately needs is the odd real platform difference (symlinks want a privilege on Windows) and the facts a bug report is made of. Those are supplied, once, by the bin. The two call sites on #132 cannot be converted from here without touching that branch. They are recorded for the deletion pass, which already has to sweep the tree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
…it still owes Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
… onto the new engine Main moved substantially while this branch was in flight: S2c's service and agent families, S3's composer commands and ctx.spawn, S8's service resource surface, engine-owned CI detection, engine-owned package manager operations, an on-demand config loader, and the fixture machinery already deleted by #159. What this merge decided, beyond taking both sides: - init's link step keeps calling the shared linkDirectoryToProject, updated to main's listWorkspaceProjects signature. Main had inlined the handler body this branch extracted; the extraction survives because init depends on it. - The bin-side detectPackageManager this branch carried is deleted in favour of main's engine-owned detection — Runtime.packageManager is now an optional override, exactly the shape the operator asked for. - ctx.host joins main's grown context beside ctx.isCI; every Runtime literal in the engine tests gains the fixed test host. - init and its tests adopt main's summary-block field (status, not tone) and the mount-coverage test's FAMILYLESS set. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
R-S2d-6: the per-slice lists (s2b, s2c, s2d) fold into parity-divergences.md verbatim, headings demoted, behind a new preamble that states the shared class rules once and puts everything needing an actual decision — the init prompt defaults, S2c's four escalated engine gaps, and the open ledger questions — at the top, where signing off is meaningful. S3's and S8's lists belong to those slices and stay separate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
Every command runs on @prisma/cli-engine, so the shell that used to run them — the commander program, its registration tree, its renderers, its prompt and help machinery, and the 27 test files that drove them — is deleted: 88 files, ~42,000 lines. Nothing a user could reach through the v8 tree is lost; the four commands that do disappear were each ruled (app build, app deploy, app run — superseded by Composer; version — the engine's --version answers) or explicitly shelved (app logs, waiting on the engine transport service logs needs). What survives is the operation layer the v8 handlers still call, and it is enumerated, not assumed: .drive/projects/prisma-cli-v8/assets/s2/shell-deletion-survivors.md is the transitive import closure of src/v8/** — after this commit nothing outside that closure remains in src/. Two survivors moved: the operation-layer context types to src/legacy/runtime.ts, narrowed to the three fields anything still reads, and detectDeployFramework out of the 5,099-line app controller into its own module, since v8 init was its only caller. The CliError knot is resolved the way the contract preferred: the auth module throws CliStructuredError directly and the auth mapping layer is gone. Elsewhere CliError remains as the deliberate contract between the surviving operation layer and v8's five per-domain error mappers, all of which rewrite the dead --trace flag to --log-level verbose before any fix text reaches a user. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
The last two commands reading process directly move onto the host
surface. The agent skill installer takes the Windows --copy decision
from ctx.host.platform, and the platform test now sets the engine test
host instead of redefining process.platform — which was itself the
pattern this closes off.
One user-visible change, recorded in the divergence document: the
feedback payload's nodeVersion string becomes a runtime object
({ name, version }), so a bun or deno binary reports itself truthfully
instead of masquerading as a node version. The human summary line reads
the same.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
packages/cli's bin points at dist/v8/cli.js — the same value PR #164 writes on its branch, byte for byte, so whichever lands second resolves a trivial conflict. The tsdown build drops the legacy entry, the root prisma and prisma-cli scripts point at the v8 bin, the prisma-v8 working name is gone, and six dependencies with no importer left (commander among them) leave the manifest. Proven from a packed tarball on plain Node: the declared bin resolves, --version answers with the lockstep version at exit 0, and auth whoami --format json settles signed-out with a sign-in next action. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
…cument Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
wmadden-electric
marked this pull request as ready for review
August 12, 2026 14:17
…2d-init-and-retirement
The S7 merge (#164) mounted the ORM family, whose grammar includes a top-level init — colliding with the platform's compute-config wizard this branch ports to the same path. Resolution ruled by the operator (2026-08-12): the ORM's project initializer mounts at `orm init`, and top-level `init` is the platform wizard, as the S2d contract wrote it. The merge also closes a coverage hole it exposed. The e2e coverage manifest's scan matched only quoted mount keys, so bare identifier mounts (init, feedback — and after this merge format, lsp, migrate) were invisible to the every-command-needs-a-happy-path rule. The scan now sees bare keys; init gains a real e2e happy path against the built binary (run against the live API before committing); the ORM paths take the family's existing exclusion reason; feedback is excluded because a per-CI-run post to the real feedback service is spam, not a test. Co-Authored-By: Claude Opus 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 Opus 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
wmadden
approved these changes
Aug 12, 2026
The divergence document, its surfaced decisions, and ledger Q1/Q3/Q5-Q8 are ratified at their stated defaults (2026-08-12). The escalated engine gaps move to deferred.md as ratified-as-shipped follow-ups. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
The engine-based CLI was developed as "v8" beside the shell it replaced. The shell is gone, so the marker means nothing and goes — from paths, identifiers, strings, comments, docs and workflows. src/v8/ dissolves into src/: the command groups land at src/commands/ (the path the old registration tree freed, which also keeps the auth COMMANDS distinct from the src/auth module), and cli.ts, main.ts, bin.ts and runtime.ts take the root names the deletion vacated. The bundle moves from dist/v8/cli.js to dist/cli.js — the path the package originally declared — and the forty v8-*.test.ts files drop the prefix. Two things about the move that are not mechanical: The version read broke and is fixed. lib/version.ts resolved ../../package.json, which only worked because dist/v8/cli.js happened to sit at the same depth as src/lib/. The flattened bundle is one level shallower, so the freshly built binary could not find its own version. The read now tries ../package.json first and ../../package.json second, and the built binary and the declared-bin end-to-end test prove both layouts. One test file was nearly lost. tests/update-check.test.ts (the update check module's own tests — the module survived the shell deletion) and tests/v8-update-check.test.ts (the bin's wiring of it) collide under prefix-stripping, and the rename clobbered the former. Restored; the wiring tests live at update-check-wiring.test.ts, and the suite is back to its full count. Where "v8" meant the Prisma 8 product version rather than the working name (the release-line docs, the workspace-id format contrast), the text now says "Prisma 8" — same meaning, no marker. .drive keeps its historical vocabulary; falsifying records is worse than an old name. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
Two expectations in agent.test.ts still conditioned on the machine's own process.platform, from before the installer read the platform off ctx.host. The harness pins the host to linux on every machine, so on a Windows runner the expectation added --copy while the command rightly did not, and the suite failed only there. The expectations are now unconditional, and the Windows rule keeps its own dedicated test, which sets the harness host explicitly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
This was referenced Aug 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The last platform slice:
initmoves onto the engine, and the Commander shell is deleted. When this merges,prisma-cliruns entirely on@prisma/cli-engine.What is in it
initportedversioncommand--versionanswersprisma-cli→dist/cli.js, proven from a tarball on plain Nodesrc/v8/dissolves intosrc/; no "v8" remains in code, tests or docsctx.hostprocess;flag.optionalBooleanadded forinitinitThe hardest command in the product: pick a framework, write a compute config, optionally install types, optionally link the directory to a Project, optionally install the agent skill. Green interactively, under
--yes, non-interactively, and when cancelled. The files it writes are asserted byte for byte against what the shipping command produced.Its link step is literally
project link. The legacy wizard calledrunProjectLink— the same controller that became the v8project linkcommand. Rather than grow a second picker,project link's handler body is extracted aslinkDirectoryToProjectand both commands call it. Making that function ignore its explicit ref fails tests in both commands, which is what shows the sharing is real.--formatis renamed--config-format(the engine reserves--formatfor output). The three optional steps default to no: the engine has one knob where legacy had two — a prompt default under--yesand non-interactive take the same branch — and defaulting to yes would makeinit --yesin CI install packages, call the API and write skill files. Interactive users pressywhere they pressed Enter. Flagged for a decision in the divergence document; one line per prompt to flip.The deletion
Every command already ran on the engine, so the shell that used to run them goes: the commander program, registration tree, renderers, prompt and help machinery, and 27 test files. Four commands disappear with it, each ruled beforehand (
app build,app deploy,app run— Composer's;version—--versionanswers). One is a shelve, not a drop:app logshad no v8 successor because the engine lacks the streaming transportservice logsneeds, and until that lands, streaming service logs is unavailable in any form. That was recorded in the S2c review and is now true.Survivors are enumerated, not assumed.
.drive/projects/prisma-cli-v8/assets/s2/shell-deletion-survivors.mdis the transitive import closure ofsrc/v8/**— after this branch nothing outside that closure remains insrc/. Two survivors moved: the operation-layer context types (narrowed to the three fields anything still reads), anddetectDeployFrameworkout of the 5,099-line app controller, sinceinitwas its only caller.The CliError knot resolved as the contract preferred: the auth module throws
CliStructuredErrordirectly and the auth mapping layer is deleted. ElsewhereCliErrorstays as the deliberate contract between the surviving operation layer and v8's five per-domain error mappers, all of which rewrite the dead--traceflag to--log-level verbosebefore any fix text reaches a user.The
initcollision, ruled#164 merged first and mounted the ORM family, whose grammar includes a top-level
init— colliding with the platform wizard this branch ports to the same path. Ruled (operator, 2026-08-12): the ORM's project initializer mounts atorm init; top-levelinitis the platform's compute-config wizard. Users of the old ORM CLI who typeprisma initexpecting a schema scaffold now get the compute wizard; the divergence document records it.The merge also exposed a coverage hole: the e2e manifest's scan matched only quoted mount keys, so bare mounts (
init,feedback,format,lsp,migrate) were invisible to the every-command-needs-a-happy-path rule. The scan now sees them;initgained a real e2e happy path, run against the live API before committing; the others took reasoned exclusions.Verification
@prisma/cli-engine@prisma/clipnpm check:grammar)@repo/cli-telemetrypnpm typecheck,pnpm lint--versionexit 0;auth whoami --format jsonsettles signed-out with a sign-in actionClaims were checked by breaking code and watching the right test fail: renaming an auth error code (caught by the envelope test with no mapper in between), disabling a survivor's API-error check, orphaning the extracted framework detector, hard-coding the feedback runtime, inverting the Windows
--copyrule, flipping a prompt default, and unmountinginit.Signed off
.drive/projects/prisma-cli-v8/assets/s2/parity-divergences.mdis the one cumulative S2 record, ratified by the operator on 2026-08-12: theinitprompt defaults, S2c's four escalated engine gaps (now tracked indeferred.mdas ratified-as-shipped follow-ups), and ledger Q1/Q3/Q5–Q8 at their built defaults.The working name retires
Developed as "v8" beside the shell it replaced; with the shell gone the marker means nothing.
src/v8/dissolves — command groups tosrc/commands/, the entry files tosrc/— the bundle returns to the originally declareddist/cli.js, and forty test files drop their prefix. Two non-mechanical finds: the version read only worked because of the old bundle depth (fixed, nearest-first, proven on the built binary), and the rename would have silently clobbered the update-check module's tests with the bin-wiring tests of the same stripped name (caught by the before/after test count, both files kept). Where "v8" meant Prisma 8 the product, the text now says that..drivekeeps its historical vocabulary.