Imported from aditi-2827/ShipCheck (
AGENTS.md). Install upstream withnpx skills add aditi-2827/ShipCheck. Copyright stays with the author.
AGENTS.md
ShipCheck: a dark, terminal-inspired "deployment-readiness" analyzer. Frontend is a Next.js 14 App Router + React 18 + Tailwind single-page UI; there is also a real local backend (Next.js Route Handlers) for auth, feed data, scan execution, and persisted history. No external APIs/dependencies; everything (scanning, auth, storage) is implemented locally with Node built-ins.
Commands
npm run dev— start dev server (http://localhost:3000)npm run lint— ESLint (next lint)npm run build— production build- There is no
typecheckscript. Runnpx tsc --noEmitto typecheck. - No
testscript — so a scan's "Tests" check reports "not configured" until one is added. - No CI workflows directory (
.github/).
CLI (node bin/shipcheck.js / global shipcheck)
The CLI is the primary entry point (see bin/shipcheck.js):
shipcheck server— the canonical way to start the server. Generates/reuses a boot token, auto-runsnext buildif.next/BUILD_IDis missing, spawnsnext start(ornext devwith--dev), and printsDashboard: http://<host>:<port>/?token=<bootToken>. If a ShipCheck server already answers on the port and accepts our token, it reuses it instead of spawning a duplicate. Options:--port N(default 3140),--host H(default 127.0.0.1),--dev.shipcheck init— register the current directory as a project (writes.shipcheck.json). Optionally--deploy-url URLto set the deployed application URL (enables Phase 3 post-deployment/API/perf checks).shipcheck scan— real scan of the current directory against the server (slow; runs build/tests).- Commands run in the current directory; for monorepos run
init/scanonce per package root. - The CLI talks to
http://localhost:3140by default; override withSHIPCHECK_SERVER_URL. - Auth: the CLI authenticates using the boot token from
SHIPCHECK_BOOT_TOKENor~/.shipcheckrc(sent asx-shipcheck-token), else a persisted session cookie, elseSHIPCHECK_PASSWORDlogin. - A server started by the CLI (or with
SHIPCHECK_BOOT_TOKENset) publishes the token to~/.shipcheckrcvianext.config.mjs, so the CLI can discover it no matter how the server was started.
Backend architecture
Route Handlers under src/app/api/, shared logic in src/lib/. All server-side, no external services:
-
src/lib/types.ts— shared domain types (Issue, CategoryResult, ScanResult, FeedData, …). -
src/lib/data.ts— static feed reference (FEED_DATA: categories, stages, thresholds,categoryWeights). Single source of truth for weights (sum to 100) and the 80/60thresholds. BumpedschemaVersionto 4 when Phase 4 rebalanced the weights.All 16 categories, with Phase 4 weights (score contribution when healthy):
Category slug Weight Phase Security security18 1 Build build15 1 Tests tests10 1 Code Quality code_quality8 2 API Check api_check8 3 Deployment deployment6 2 Dependencies dependencies6 1 Post-Deploy post_deployment5 3 Performance performance5 3 Monitoring monitoring4 2 Database database4 1 Git git4 1 CI/CD ci_cd3 1 Docker docker2 1 Rollback rollback1 1 Environment environment1 1 Weighting rationale (Phase 4): security + build stay dominant (blockers each lose all weight); tests/code-quality/API hold meaningful shares; "easy" heuristics (docker, rollback, environment, git) get the smallest shares so they can't inflate the score on their own.
deployUrlflow: a project may carry an optionaldeployUrl(set viashipcheck init --deploy-url URL, stored on the Project record, or passed per-scan in the/api/scanbody asdeployUrl). When set, Phase 3 checks (API Check, Post-Deploy, Performance/Lighthouse) run for real. When unset, those three render as pass-status placeholders ("Set a deploy URL to enable …").runScan(targetDir, projectId?, options?)andscanBodyreadoptions.deployUrl(preferring a per-scan body override over the stored Project value). -
src/lib/checks.ts— the real scan engine (runScan). Runs node/npm versions,git status/branch,npm audit,npm run build,npm test, docker presence, and a local regex secret scan +.env.examplecoverage. Persists each result via the store. The temp build/test workspace is a full-tree copy of the target (excludingnode_modules,.next,.git,.data,dist,build,.env*;node_modulesis reused via a Windows junction), so nestedsrc/layouts (e.g.app/, monorepo sub-packages) work. Scoring is per-category weighted (categoryWeightsindata.ts, a warning keeps half its weight, a critical category contributes 0). Build/tests with nonpm run <script>configured are warnings ("No build script configured"), not criticals..env.examplechecks make no assumptions about required variable names; the secret scan walks the whole tree (nevernode_modules/.next/.env*).Phase 1 + Phase 2 checks (implemented): Git (branch, working-tree,
.gitignorepresence + critical entries), Database (detects Prisma/Drizzle/Knex/Supabase + raw pg/mysql/mongo drivers from config files orpackage.jsondeps; checks for a migration directory), CI/CD (detects GitHub Actions, GitLab CI, Jenkins, CircleCI, Bitbucket, Travis, Azure; validates YAML indentation + non-empty), Rollback (git version tags + optional rollback config), Security (secret scan +.env.example+ console.log/debugger detection), Code Quality (runs the project's own ESLint config via the programmatic ESLint API, reports errors/warnings), Deployment (detects + validates Vercel/Netlify/Fly/Railway/Procfile/Herokuapp.jsonconfigs), Monitoring (detects Sentry/LogRocket/Datadog from deps or source + React error boundaries).Phase 3 checks (implemented — all gated on
options.deployUrl, from the Project record or scan body; render as pass-status placeholders when nodeployUrlis set):- API Check (
api_check): live HTTP probes via Node built-inhttp/https(probeUrlhelper) against the deploy root,{root}/api/health, and{root}/api/status; flags any non-2xx/3xx and reports latency. - Post-Deployment (
post_deployment): smoke test — one GET to the deploy root, fails if non-2xx or > 5s latency. - Performance (
performance):bundle-sizeanalyzes the (temp-workspace) build output's.next/static/chunks(warning if no build output);lighthouseruns a full Lighthouse performance audit (perf score + LCP) viascripts/lighthouse-run.cjs, a standalone CommonJS child process spawned throughrunCommand(['node', ...]). Because puppeteer/lighthouse are heavy and break Next's webpack static analysis, they are deliberately kept out of the server bundle and isolated in that child script (loaded fromnode_modulesat runtime). Thresholds: perf ≥ 90 pass, 50–89 warning, < 50 critical. - deps:
puppeteer(bundled Chromium) +lighthouseare dev deps.@types/eslintis a devDependency for the Code Quality ESLint integration.
- API Check (
-
src/lib/store.ts— file-based JSON persistence under.data/(gitignored): scan history, auth secret, sessions. Swappable for a real DB later. -
src/lib/http.ts— response envelope{ ok:true, data } | { ok:false, error:{code,message,details?} }andApiError/readJson/errorResponsehelpers. -
src/lib/auth.ts— local session-cookie auth: password fromSHIPCHECK_PASSWORDenv, stored only as an scrypt hash (Nodecrypto), random session token in HttpOnlySameSite=Strictcookie.
API routes
| Route | Auth | Purpose |
|---|---|---|
GET /api/feed |
none | Static reference data (categories, stages, thresholds) |
POST /api/auth/login |
— | Verify password, set session cookie |
POST /api/auth/logout |
— | Destroy session, clear cookie |
GET /api/auth/me |
— | Report { authenticated } |
POST /api/scan |
required | Run runScan() (real build + tests), persist, return result |
GET /api/history |
required | Return past scan results (latest first, capped at 200) |
All API routes declare export const dynamic = 'force-dynamic' to avoid prerendering.
Run requirements
- Scanning runs
npm run build/npm testsynchronously server-side, so/api/scanis slow (tens of seconds) and side-effecting. Timeouts exist per check. SHIPCHECK_PASSWORDmust be set or login returns a 500 (INTERNAL). There is no default. Sessions expire after 12h.
Windows gotchas (this repo is developed on Windows)
- Command spawning: in
checks.ts, commands run viaexec(shell) on Windows.execFilewill fail onnpm/npxbecause they are.cmdshims not resolved through PATH. Do not switch toexecFilefor npm commands on Windows. - Child-process commands are hard-coded literals (no user input), so shelling out is safe.
- Temp-workspace
node_modulesjunction: the scan's temp build/test workspace reusesnode_modulesvia a Windows junction. Next.js Turbopack builds can fail inside such a workspace ("Symlink ... node_modules is invalid, it points out of the filesystem root") — a pre-existing Windows limitation, not a scan-engine regression. Webpack builds (the default) generally work.
Frontend (src/app/page.tsx)
'single-page client view. The "RUN SHIPCHECK" button and the status/timeline data are still simulated client-side (useState + setTimeout, e.g. runScan around line 33) and are not yet wired to the /api/* backend. So flipping page.tsx to consume runScan results is a known unfinished integration, not a regression. Hand-written CSS in src/app/globals.css (not Tailwind utilities); semantic color tokens live in tailwind.config.ts.
Gotchas
next.config.mjssetsoptimizePackageImports: ['lucide-react'], butlucide-reactis not installed. Don't import it unless you add it topackage.json.reactStrictMode: trueis on — be aware effects run twice in dev.- Never add external scanning/auth/AI dependencies; core functionality must stay local.