Closes #236.
What was wrong
Three defects, each hiding the next.
The SDK spawn was unmarked. #26 added CRAFT_CLI_SPAWN_MARKER so that a CLI spawned in place of the native binary recognises itself and explains it. Only runCraftBinary() ever set it. CraftApp.show() spawns the same resolved binary and did not, so every SDK caller on a machine where craft on PATH is this package's CLI still got the raw parser error the marker exists to replace.
quiet destroyed the explanation. It mapped to stdio: 'ignore', so the child's stderr was discarded — and the rejection then told the caller to "check the console output above", which the SDK had just thrown away. A dashboard passing quiet: !verbose saw Craft process exited with code 1 and had no way to reach the cause.
The version warning misattributed it. parseCraftVersionOutput strips craft version or a leading v, but the CLI prints craft/0.0.92 darwin-arm64 bun-v1.4.1. The platform suffix survived, the comparison never matched, and two halves of one checkout were announced as drift. That warning was the only signal a user got, and it pointed away from the real fault.
Before and after, on the machine from the report
craft on PATH is ~/.bun/bin/craft → a checkout linked under the package's former name ts-craft. No native binary anywhere.
Before, without quiet:
⚠️ Craft binary/SDK version mismatch: SDK=0.0.92, native=craft/0.0.92 darwin-arm64 bun-v1.4.1.
ClappError: Unknown option `--url`
Run `craft [url] --help` to see available options.Before, with quiet — the dashboard's path:
⚠️ Craft binary/SDK version mismatch: SDK=0.0.92, native=craft/0.0.92 darwin-arm64 bun-v1.4.1.
RESULT:
❌ Craft process exited with code 1
...
Check the console output above for more details.After, both ways, and with no warning because the two versions now compare equal:
❌ Craft process exited with code 1
"craft" on PATH is the Craft CLI, not the native binary.
Craft spawned it expecting the native build and reached this CLI instead —
which is why an option you never typed (--url) came back as unknown.
Fix it in one of two ways:
1. Install the native binary and make sure it comes first on PATH:
pantry install craft
2. Point CRAFT_BIN at a build directly:
CRAFT_BIN=/path/to/craft craft <url>Two choices worth naming
Settling on close rather than exit. stderr can still hold buffered data when the process ends, and the child's last words are the whole of the diagnosis. With no pipe — the inherit path — the two events fire together, so nothing changes there.
The shim message no longer says "The CLI spawned it". Both spawners set the marker now, and for an SDK caller that sentence was simply untrue. It reads "Craft spawned it expecting the native build and reached this CLI instead", which is accurate either way and keeps the marker a single env var rather than two.
The generic "check the console output above" line stays for the inherit path, where it is correct, and a quiet child that said nothing is told so explicitly instead.
Tests that fail without the fix
Four mutations, each against the restored tree:
| mutation | fails |
|---|---|
| the SDK spawn unmarked again | 1 |
quiet back to stdio: 'ignore' | 1 |
| the version parser back to the old strip | 2 |
| the failure message drops what the child said | 1 |
parseCraftVersionOutput gains the CLI's craft/<version> <platform> form, the bare craft 0.0.92 and 0.0.92 forms, and empty output. craftProcessFailedMessage is exported and covered directly: it leads with the child's words when there are any, does not point a quiet caller at output that was never printed, and still points an inheriting one at its own console.
Not done here
The two Stacks-side findings from the same session — the dashboard's quiet flag hiding child errors, and its home page assuming the commerce bundle — belong in that repository, not this one.
Verification
bun run test, bun run typecheck, bun run verify:packages, Pickier (0 errors), and the typescript package tests (559). The before/after above was captured by stashing this branch and re-running the same script.