tsup inlines every @airship/* package into apps/cli/dist/index.js
(noExternal), so an edit anywhere under packages/ is invisible until the
CLI is rebuilt. The Makefile could not catch that: $(CLI) was a file
prerequisite with no source prerequisites, so `@pnpm build` ran only when
dist was absent. Once it existed, `make run` launched the stale bundle
silently.
./airship is a pure passthrough — argv untouched, cwd never changed, so
--cwd, the upward config search and the bare-invocation wizard behave
exactly as the published binary does — that mtime-scans apps/cli and
packages/* and shells out to turbo only when something moved.
Three things the obvious implementation gets wrong:
- Turbo hashes contents, so a cache hit leaves dist/ untouched and a
plain mtime comparison never converges. A successful build stamps the
entry, but only when turbo did not rewrite it, since stamping backwards
reopens the window tsup's `clean` creates on apps/cli's own mtime.
- Scanning */src misses real build inputs. `turbo --dry=json` reaches
package.json, tsconfig.json, scripts/ and editor-icons' 507 SVGs under
assets/, so the scan covers package roots minus a skip set.
- SIGINT arrives from the tty at the whole process group, so forwarding
it would trip the CLI's own second-Ctrl-C escape hatch; SIGTERM arrives
at one pid, so swallowing it would hang. Swallow the first, forward the
second.
The logic lives in scripts/airship-run.mjs so airship.cmd gives Windows
the same behaviour rather than a degraded passthrough. The Makefile's
run:* and doctor targets now go through the wrapper, so they inherit the
build check; a test reserves the wrapper's two flag names, since the CLI
generates --no-<name> for every boolean and would collide silently.