Ship init
Skill aksheyw/claude-code-ship-gate/plugins/ship-gate/skills/ship-init
Pre-push quality gate for Claude Code that runs above git, so git push --no-verify can't skip it. Tests, code review, security, and a secret scan must pass before any push to your protected branch. Say 'ship it' to run it.
npx -y skills add aksheyw/claude-code-ship-gate --skill ship-initAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Scaffold a .shipgate.json for this project by auto-detecting its test/lint/typecheck/build commands and deploy target.
SKILL.md
8.8 KB, ~2.3k tokens by cl100k_base, as published. Nobody here has run it
/ship-init
Runtime check (do this FIRST): this skill requires the Ship Gate plugin runtime. If
${CLAUDE_PLUGIN_ROOT}is empty or the file"${CLAUDE_PLUGIN_ROOT}/scripts/ship-gate.sh"does not exist, STOP and tell the user: this skill was installed as a bare skill (e.g. via skills.sh), which does not ship the detection runner or the push-block hook: install the full plugin via/plugin marketplace add aksheyw/claude-code-ship-gate+/plugin install ship-gate@ship-gate, or clone the repo and runinstall-local.sh.
You are the ship-gate setup helper. You run once in a new project to produce a .shipgate.json
scaffold that the user can tune before running /ship.
Step 1: Run detection
Run the following command from the project root (the directory containing the user's code, NOT the plugin root):
bash "${CLAUDE_PLUGIN_ROOT}/scripts/ship-gate.sh" detect
This prints a JSON object:
{ "tests": "...", "lint": "...", "typecheck": "...", "build": "...", "deploy": "...", "ci": "..." }
An empty string "" for a key means no command was detected for that gate. deploy and ci name the
detected deploy target and CI system (or "" if none): they are informational, not gate commands.
Step 1.5: Scan for uncovered suites (doctor)
Run the advisory scan from the project root:
bash "${CLAUDE_PLUGIN_ROOT}/scripts/ship-gate.sh" doctor
It prints [WARN] uncovered suite: … for every suite a single tests.command would silently skip, a
nested package with its own test script, an extra/named test-runner config, an e2e suite, or a *.rules
security-rules file, and a line for CI: [PASS] CI detected: … or [WARN] no CI detected …. (It always
exits 0; it never blocks. You can re-run it any time.)
If it reports no CI ([WARN] no CI detected), tell the user directly: with no CI, this gate is the only
automated check before production, so keeping every suite wired (above) and the gates enabled matters more.
If it reports uncovered suites, tell the user plainly: the default single tests command runs ONE suite,
so those other suites would not run on /ship and the gate would still report "tests PASS": the exact blind
spot this catches. Offer to wire each one as a when-conditional suite in gates.tests.command using the
array form (this replaces the single "command": null|"…" string). Example for a repo with a functions/
package and Firestore rules:
"tests": {
"enabled": true,
"command": [
{ "command": "npm test" },
{ "command": "npm --prefix functions test", "when": ["functions"] },
{ "command": "npm run test:rules", "when": ["*.rules"] }
]
}
Each entry is an object with a non-empty command string and an optional when array of git pathspecs;
the suite runs iff a changed file matches one (omitted/empty when = always run). Keep these shapes exact:
the runner fail-closes on a malformed gate (a bare string in the array, an empty command, a non-array when).
Pathspec globbing: * spans / (so *.rules matches at any depth, functions matches everything under it);
brace globs like {a,b} are NOT supported: list entries separately ("*.tsx", "*.jsx"). Let the user
confirm the exact command for each suite; only scaffold what they approve.
Step 2: No-overwrite check
Before writing anything, check whether .shipgate.json already exists at the project root.
If it does: do NOT overwrite it. Instead, show the user the detected scaffold (filled per Step 3)
and ask: "A .shipgate.json already exists. Do you want to replace it with this scaffold?"
Proceed only on explicit confirmation.
Step 3: Write .shipgate.json
Write the following scaffold to .shipgate.json at the project root. Fill in each
gates.<g>.command from the detect output where the detected value is non-empty; leave
null where detection found nothing: null means "auto-detect at runtime".
Special rules:
gates.build: by defaultenabledstaysfalse(build is opt-in; the user flips it when ready). Exception: deploy-connected: if Step 1'sdetectreported a non-emptydeploytarget (e.g. Vercel, Netlify, Firebase, Fly.io, Render), scaffoldgates.build.enabled: truewith the detectedcommand, and tell the user why: a push to a deploy-connected branch builds in the cloud anyway, so a ~3-second local build pre-flight catches a broken build before it breaks the deploy mid-flight. This only changes what/ship-initscaffolds: the runtime default for configs that don't setbuild.enabledis unchanged (false), so existing configs behave exactly as before. Either way, still fillgates.build.commandwhenever a build command was detected.mainBranch: use the repo's actual default branch if it isn'tmain(check withgit symbolic-ref refs/remotes/origin/HEADorgit remote show origin).- Do NOT add a
$schemakey. The schema lives at the plugin'sschema/shipgate.schema.jsonbut has no stable path from a user's project, so omitting the pointer avoids a dangling reference.
{
"mainBranch": "main",
"gates": {
"tests": { "enabled": true, "command": null },
"lint": { "enabled": true, "command": null },
"typecheck": { "enabled": true, "command": null },
"build": { "enabled": false, "command": null },
"secretScan": { "enabled": true },
"codeReview": { "enabled": true, "skill": "ship-review", "upgrade": null },
"security": { "enabled": true, "skill": "ship-security", "upgrade": null },
"uat": { "enabled": true, "skill": "verify", "mode": "confidence" }
},
"heavyGates": {
"audit": { "skill": "deep-review", "upgrade": "audit", "suggestWhen": { "filesChanged": 15, "areasChanged": 3, "riskyPaths": true } },
"deep": { "skill": "deep-review", "upgrade": "deep-review", "suggestWhen": { "diffLines": 400, "riskyPaths": true, "release": true } }
},
"regression": { "enabled": false, "skill": "ai-regression-testing", "runWhen": "test-affecting" },
"scoping": {
"docs": ["**/*.md", "docs/**"],
"ui": ["src/components/**", "src/pages/**", "**/*.{tsx,jsx,vue,svelte}"],
"security": ["**/auth/**", "**/*payment*", "**/*.rules", "**/migrations/**", "**/.env*", "api/**", "package.json", "requirements*.txt", "go.mod", "Cargo.toml", "pubspec.yaml"]
},
"deploy": { "autoDetect": true, "warnOnPush": true },
"hotfix": { "skipGates": ["uat", "regression", "audit", "deep"] },
"markerTtlSeconds": 900
}
When writing the file, replace each null command value with the detected string if non-empty.
For example, if detection returned "tests": "pnpm test", write "command": "pnpm test" for
gates.tests.command. If detection returned "", keep "command": null.
Step 4: Explain configuration
After writing the file, give a brief explanation (roughly 8–12 lines):
- Gate tiers: The judgment gates run in a tiered order, first an external upgrade skill
if configured (e.g.
vibe-security,code-reviewer), then the plugin's bundled skill where one exists (codeReview and security have one; UAT's shipped fallback is a manual confirmation), then a manual prompt. Enabled deterministic gates (tests, lint, typecheck, build, secretScan) are non-negotiable; a gate disabled in config is skipped and reported as such. - Changed-file awareness:
/shiponly fires gates relevant to the diff. Run/ship-gate:ship --dry-runto preview which gates will fire for your current changes. - CI backstop: if
doctorreported no CI, this gate is the only automated check before production, prefer enabling more gates and wiring every suite over leaving coverage gaps. - Tuning handles:
- Set
gates.*.upgradeto plug in external skills (e.g."upgrade": "vibe-security"for the security gate,"upgrade": "code-reviewer"for code review). - Set
heavyGates.audit/heavyGates.deepthresholds to control when--audit/--deepare suggested. - Set
regression.enabled: trueto surface theai-regression-testingstrategy skill on test-affecting changes. This is advisory guidance: it runs no command and never blocks the ship. For regression suites you want actually executed, wire them intogates.tests.commandaswhen-conditional entries (see Step 1.5) rather than relying on this pointer. - Flip
gates.build.enabled: truewhen you want the build gate to run on every ship. (If a deploy target was detected,/ship-initalready turned it on: a deploy-connected push builds anyway, so a local pre-flight beats a mid-deploy break.) deploy.autoDetect: truemakes the ship gate warn before pushing if it detects a deploy target (e.g.vercel.json,netlify.toml) that would auto-deploy on push.
- Set
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.