@natjswenson/shipflow 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,336 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@natjswenson/shipflow` are documented here.
4
+
5
+ ## 0.3.2 (2026-07-28) — actually render the README on npmjs.com
6
+
7
+ - **Fixed: the npm page still showed "No README data found" after 0.3.1.** That
8
+ release put README.md, LICENSE and CHANGELOG.md into the tarball (verified by
9
+ downloading it), which fixed what `npm install` delivers — but not the website.
10
+ npm reads README.md to populate the registry manifest's `readme` field when it
11
+ builds the publish manifest, which happens BEFORE the package's own `prepack`
12
+ hook runs. So the tarball had the file and the registry record did not.
13
+ Worse than merely empty: the stored value was the literal string
14
+ `ERROR: No README data found!`, and npmjs.com falls back to reading the
15
+ tarball only when that field is *absent* (compare `zod`, which renders fine
16
+ with no packument readme at all), so the error string kept winning.
17
+ The release workflow now stages the three files into the package directory
18
+ before `npm publish` is invoked at all, rather than relying on `prepack`.
19
+ Code unchanged.
20
+
21
+ ## 0.3.1 (2026-07-28) — publish the README, LICENSE and CHANGELOG to npm
22
+
23
+ - **Fixed: the npm package shipped with no README, LICENSE or CHANGELOG.**
24
+ `package.json`'s `files` listed all three, but they live at the plugin root
25
+ (beside `.claude-plugin/`), one level above the package directory — and npm only
26
+ includes files from inside the package directory, so the entries silently matched
27
+ nothing. Every release through 0.3.0 published a tarball without them, and
28
+ `npm view @natjswenson/shipflow readme` returned *"No README data found"*, leaving
29
+ the npm page blank for anyone evaluating the CLI. A `prepack` script now stages the
30
+ three files into the package directory before the tarball is built, and `postpack`
31
+ removes them again so the working tree stays clean; they are gitignored at that
32
+ path so a interrupted pack cannot leave committable strays. Code unchanged.
33
+
34
+ ## 0.3.0 (2026-07-28) — multi-pattern workflow templates; `undefined` param fix
35
+
36
+ Minor, not patch: this release carries the multi-pattern feature work that had been
37
+ sitting unreleased on `main` since 0.2.6, plus one bug fix found by the new baseline
38
+ eval. Existing `dev-main-promotion` repos are unaffected — see the compatibility note
39
+ below.
40
+
41
+ - **Fixed: a missing config field rendered the literal string `"undefined"` into the
42
+ workflow.** `renderTemplate` tested param presence with `key in params`, which is
43
+ true for a key whose value is `undefined`, so `String(undefined)` flowed through as
44
+ a real value and passed the safety regexes. A config with no `branches.main`
45
+ rendered `name: auto-merge dev to undefined` and `branches: [undefined]` — a
46
+ syntactically valid workflow that installs cleanly and can never fire, with no
47
+ error at `apply` time. Present-but-`undefined` and `null` now count as missing and
48
+ raise the same "missing param(s)" error as an absent key. Found by
49
+ `tests/baseline.test.mjs` on its first run.
50
+
51
+ Generalizes shipflow from one hardcoded branching pattern to a registry of three
52
+ selectable patterns, with deterministic autodetection. Backward compatible: a repo's
53
+ existing `.github/shipflow.json` with no `workflowPattern` field keeps resolving to
54
+ `dev-main-promotion` with identical behavior — confirmed against this repo's own live
55
+ config (a `noops`-only plan, byte-identical to before this change).
56
+
57
+ - **Added: `github-flow` pattern** — a single long-lived `main`; every PR merges (and
58
+ auto-merges) directly to it, no separate promotion branch. New
59
+ `main-automerge.yml.tmpl` template.
60
+ - **Added: `gitflow` pattern** — `develop` + `main` + transient `release/*`/`hotfix/*`
61
+ branches, for software maintaining multiple released versions concurrently. New
62
+ `release-automerge.yml.tmpl`/`hotfix-automerge.yml.tmpl` templates (prefix-matched
63
+ `head.ref` guards) and `hotfix-merge-back.yml.tmpl`/`release-merge-back.yml.tmpl`
64
+ (GitFlow's defining dual-merge-back semantic: a hotfix/release merges into both
65
+ `main` and `develop`; a merge-back conflict or push failure opens a PR for manual
66
+ resolution instead of force-pushing or silently dropping the merge).
67
+ - **Added: deterministic autodetection.** `shipflow detect` now returns a
68
+ `rankedPatterns` array (every pattern's score + evidence). Classification is
69
+ `confident` (top score `>= 0.7` and a `> 0.3` gap over second place), `greenfield`
70
+ (top score `< 0.4`), or `ambiguous` (the residual case) — the first-run interview
71
+ confirms a confident detection's evidence with the user rather than silently
72
+ applying it, and presents all 3 patterns for an explicit choice otherwise.
73
+ - **Added: `workflowPattern` + `patternConfig` config fields.** `patternConfig.gitflow`
74
+ holds `releaseBranchPrefix`/`hotfixBranchPrefix` (default `release/`/`hotfix/`).
75
+ `branches.dev` remains the sole source of truth for gitflow's develop-branch name —
76
+ no separate `developBranch` field.
77
+ - **Architecture:** new `lib/pattern-registry.mjs` (`listPatterns`/`resolvePattern`/
78
+ `scoreAll`) and one `lib/patterns/<id>/index.mjs` module per pattern. `detect.mjs`,
79
+ `plan.mjs`, and `apply.mjs` are now thin dispatchers over whatever the registry
80
+ returns, instead of hardcoding `dev-main-promotion`'s logic inline — adding a 4th
81
+ pattern in the future needs no changes to any of the three.
82
+ - Existing single-pattern behavior (branch protection, auto-merge, branch cleanup,
83
+ release tagging) is unchanged for repos already on `dev-main-promotion`.
84
+
85
+ ## 0.2.6 (2026-07-15) — pin `@latest` on every invocation; docs pass
86
+
87
+ Self-discovered during the PAT-wiring dogfood step that immediately followed
88
+ 0.2.5's release — not a Siege audit finding, but adjacent to the same
89
+ class of risk the audit was meant to close.
90
+
91
+ - **Fixed: silent global-install shadowing.** `npx -y @natjswenson/shipflow
92
+ <command>` (no version/tag) can resolve an already-installed copy on
93
+ `PATH` — e.g. a stale `npm install -g @natjswenson/shipflow` left over
94
+ from manual testing — instead of fetching the current version from the
95
+ registry, with **no warning that this happened**. Confirmed concretely on
96
+ `claude-skills` itself: a bare invocation silently ran a stale global
97
+ 0.2.0 install, missing every fix through 0.2.5, including the Critical
98
+ template-injection fix (0.2.3). `npx -y @natjswenson/shipflow@latest
99
+ <command>` correctly resolved 0.2.5. This meant a repo could run
100
+ `shipflow` believing it was getting current, audited behavior while
101
+ silently getting pre-audit, vulnerable behavior instead.
102
+ - **Fix: every invocation in `SKILL.md` now pins `@latest`.** New
103
+ regression tests (`tests/skill_contract.test.mjs`) assert every `npx`
104
+ invocation of shipflow in `SKILL.md` is pinned, and fail if a future edit
105
+ reintroduces a bare invocation. New skill-invariant entry
106
+ (`npx-must-pin-latest`).
107
+ - **Docs pass**, per user request before this release: root `README.md`
108
+ gained a `shipflow` row in the skills table, marketplace/manual-install
109
+ instructions, and a rewritten Branch & release flow section describing
110
+ the actual shipflow-managed automation this repo runs (replacing stale
111
+ prose describing the pre-dogfood bespoke flow); `skills/shipflow/README.md`
112
+ gained current usage instructions (with the `@latest` pin and its
113
+ rationale), an updated Status section reflecting 0.2.5's live validation
114
+ and completed security audit, and a pointer to remove a shadowing global
115
+ install if one exists (`npm uninstall -g @natjswenson/shipflow`).
116
+ - No code changes to `lib/`/`bin/` in this release — SKILL.md, tests,
117
+ README, and version metadata only.
118
+
119
+ ## 0.2.5 (2026-07-15) — mandatory TOCTOU guard, forced-override auditability, subprocess timeouts, YAML-validity CI check
120
+
121
+ The four remaining findings from the same Siege audit as 0.2.3/0.2.4, all
122
+ presented to the user for a fix-vs-accept decision and fixed on explicit
123
+ go-ahead.
124
+
125
+ - **High, fixed (SIEGE-2026-07-15-002):** `--force allow-no-checks` /
126
+ `--force <template-id>` had zero code-level friction beyond the flag
127
+ itself — "get explicit user confirmation before forcing" lived entirely in
128
+ SKILL.md prose, not in the CLI. `apply` now refuses any `--force` unless
129
+ accompanied by `--force-reason "<text>"`; the reason is echoed back on
130
+ each forced entry in the apply result (`{ forced: true, forceReason }`)
131
+ for auditability. This doesn't stop a determined bypass, but it raises
132
+ the bar from a single flag to an explicit, logged justification.
133
+ - **Medium, fixed (SIEGE-2026-07-15-003):** `--expect-state-hash` (the
134
+ documented TOCTOU guard) was optional — omitting it silently proceeded
135
+ with zero drift protection. A real (non-dry-run) `apply` now hard-refuses
136
+ without it, unless the caller explicitly passes the new, named
137
+ `--skip-hash-check` escape hatch.
138
+ - **Medium, fixed (SIEGE-2026-07-15-004):** no subprocess timeout was set
139
+ anywhere in `gh.mjs`'s `spawnSync` calls — a hung/rate-limited `gh api` or
140
+ stuck `git` call could hang the whole process indefinitely. All
141
+ `spawnArgs` calls now default to a 30s timeout, with the resulting
142
+ `ETIMEDOUT` surfaced in the returned `stderr`.
143
+ - **Low, fixed (SIEGE-2026-07-15-005):** no test rendered the template and
144
+ validated the output as syntactically valid YAML — the exact bug class
145
+ that bit 0.2.1/0.2.2 (a silently-broken generated workflow) was caught
146
+ only by live production testing, not the unit suite. New
147
+ `tests/template-validity.test.mjs` parses the rendered workflow with the
148
+ `yaml` package (dev-only dependency, not shipped to consumers) across a
149
+ range of legal inputs.
150
+ - New CLI integration test file (`tests/cli-apply-guards.test.mjs`) spawns
151
+ the real `bin/shipflow.js` to exercise both new refusals end-to-end.
152
+ - This closes out the Siege security audit run before rolling shipflow out
153
+ to repos beyond `claude-skills` — zero Critical/High findings remain open.
154
+
155
+ ## 0.2.4 (2026-07-15) — REST-path encoding, resolveOwnerRepo hardening, file-size cap
156
+
157
+ Three more findings from the same Siege audit as 0.2.3, surfaced by an
158
+ independently-dispatched Boundary Attacker pass that (eventually) returned
159
+ its report and cross-confirmed the 0.2.3 fix while adding new findings:
160
+
161
+ - **Medium, fixed:** `fetchBranchProtection` and `checkSecretPresent`
162
+ interpolated `branch`/`secretName` unencoded into `gh api` REST path
163
+ segments — inconsistent with `checkLabelExists`, which already used
164
+ `encodeURIComponent` for the same class of input. Both now encode.
165
+ - **Low, fixed:** `resolveOwnerRepo`'s regex capture (`[\w.-]+`) admitted
166
+ all-dots segments (`.`, `..`) since `.` is in the character class with no
167
+ further constraint — a crafted remote like `github.com/../claude-skills`
168
+ could yield an `ownerRepo` that normalizes away the intended
169
+ `repos/<owner>/<repo>` prefix once interpolated downstream. Now rejects
170
+ any owner/repo segment matching `^\.+$`.
171
+ - **Medium, fixed:** no file shipflow reads from a target repo
172
+ (`.github/shipflow.json`, candidate settings-as-code artifacts, workflow
173
+ YAML, the rendered template) had a size guard — all of these are
174
+ repo-write-controlled, not admin-only, so a maliciously huge or
175
+ pathologically nested file could exhaust memory on an unbounded
176
+ `readFileSync`/`JSON.parse`. New `readFileCapped` helper in `gh.mjs`
177
+ (1 MB cap) used at every such read site.
178
+ - 8 new regression tests.
179
+
180
+ ## 0.2.3 (2026-07-15) — Critical: unescaped template substitution allowed workflow injection
181
+
182
+ Found by a Siege security audit run before rolling shipflow out to other
183
+ repos, immediately after 0.2.1/0.2.2 landed the previous two fixes.
184
+
185
+ - **Critical, fixed:** `render.mjs`'s `renderTemplate` did pure string
186
+ substitution with zero escaping. `config.branches.dev`/`main` and
187
+ `config.release.releaseCredential` — all sourced from
188
+ `.github/shipflow.json`, a file anyone with repo **write** access can
189
+ edit, not just the admin who ran shipflow's setup — were substituted
190
+ directly into single-quoted YAML string comparisons and a
191
+ `${{ secrets.X }}` GitHub Actions expression with no validation.
192
+ Concretely: a `branches.dev` value of `dev' || 'x'=='x` rendered the
193
+ auto-merge job's `if:` condition to `... == 'dev' || 'x'=='x'` —
194
+ unconditionally true, enabling auto-merge on **any** pull request into
195
+ `main`, not just genuine `dev`-branch promotions. A `releaseCredential`
196
+ value containing a newline could inject arbitrary new YAML keys/steps
197
+ into the committed, then-executed workflow file. Both are a privilege
198
+ escalation: a repo-write-level actor reaching an admin-scoped mutation
199
+ through the credential the rendered workflow runs with.
200
+ - **Fix:** `renderTemplate` now validates each substituted value against a
201
+ per-token safety rule before rendering — `DEV_BRANCH`/`MAIN_BRANCH` reject
202
+ any single quote or newline; `RELEASE_CREDENTIAL_SECRET` must match
203
+ GitHub's own secret-naming rule (`^[A-Za-z_][A-Za-z0-9_]*$`). A rejected
204
+ value throws rather than silently rendering unsafe YAML.
205
+ - **Also fixed:** `bin/shipflow.js`'s `cmdPlan`/`cmdApply` never wrapped
206
+ `computePlan` in a try/catch, so this (and the pre-existing "missing
207
+ param") error would have crashed with a raw stack trace instead of the
208
+ clean `{"error": ...}` JSON contract every other failure mode uses —
209
+ breaking the "every command prints JSON to stdout" guarantee agents rely
210
+ on to parse output.
211
+ - New regression tests assert the exploit renders are rejected, and that
212
+ ordinary branch/secret names still render normally.
213
+
214
+ ## 0.2.2 (2026-07-15) — `label-release-pending` never fires under `GITHUB_TOKEN`
215
+
216
+ Found by the same dogfood run as 0.2.1, one merge later — a second, more
217
+ serious bug than the missing `--repo`: the manual-gate release-ask flow's
218
+ whole premise (a durable label survives the async gap between auto-merge
219
+ enabling and completing) silently didn't work at all.
220
+
221
+ - **Root cause: GitHub's loop-prevention rule.** A PR auto-merged via `gh pr
222
+ merge --auto` run under the default `secrets.GITHUB_TOKEN` completes
223
+ (later, once checks pass) attributed to the `github-actions[bot]`
224
+ identity. A `pull_request: closed` event from that bot-attributed merge
225
+ does **not** trigger this or any other workflow's `on: pull_request`
226
+ handlers. Confirmed empirically, not just from docs: an otherwise-identical
227
+ promotion PR merged by a real, PAT-authenticated actor fired the
228
+ closed-event trigger within 2 seconds; one completed by
229
+ `GITHUB_TOKEN`-enabled auto-merge fired **no run at all**, even after
230
+ 100+ seconds of polling. This means `label-release-pending` never ran for
231
+ any normally-auto-merged promotion — only for a promotion a human merged
232
+ by hand — which is the opposite of the common case the feature exists for.
233
+ - **Fix: both `gh` calls now use `config.release.releaseCredential`** instead
234
+ of a hardcoded `secrets.GITHUB_TOKEN`. Wired a new `RELEASE_CREDENTIAL_SECRET`
235
+ template token through `render.mjs` and `plan.mjs`'s
236
+ `computeTemplatePlanEntry` (previously `releaseCredential` was read by
237
+ `detect.mjs` only to check whether a named secret *existed* — it was never
238
+ actually substituted into the rendered workflow).
239
+ - **First-run setup (SKILL.md) now has an explicit step** requiring the user
240
+ to create a real PAT/App-installation-token secret and record its name in
241
+ `release.releaseCredential` — defaulting to `GITHUB_TOKEN` is called out as
242
+ a silent-failure trap, not a safe default. `config.example.json`'s
243
+ placeholder changed from `"GITHUB_TOKEN"` to `"SHIPFLOW_AUTOMERGE_PAT"` so
244
+ copying the example doesn't propagate the trap.
245
+ - New regression test asserts both `GH_TOKEN` lines use the configured
246
+ secret name and never fall back to a hardcoded `GITHUB_TOKEN`.
247
+
248
+ ## 0.2.1 (2026-07-14) — rendered workflow was missing `--repo`
249
+
250
+ Found by dogfooding shipflow on its own home repo (`claude-skills`) — the
251
+ very first live promotion PR after switching over would have silently
252
+ broken auto-merge and release labeling.
253
+
254
+ - **Fix: both `gh` calls in the rendered `dev-to-main-automerge.yml` now pass
255
+ `--repo "${{ github.repository }}"` explicitly.** Neither the `auto-merge`
256
+ job's `gh pr merge` nor the `label-release-pending` job's `gh pr edit` had
257
+ it, and the workflow has no `actions/checkout` step for `gh` to infer the
258
+ repo from — every run failed with `fatal: not a git repository (or any of
259
+ the parent directories): .git`. This masked itself in the first dogfood
260
+ migration only because the hand-built workflow it was replacing (which did
261
+ pass `--repo`) happened to still be present on `main` and fired on the same
262
+ transitional PR.
263
+ - **New regression tests** (`tests/render.test.mjs`) read the actual
264
+ `.tmpl` file's rendered output and assert `--repo` is present on both `gh`
265
+ invocations — no prior test read the template's real command lines, only a
266
+ synthetic placeholder string, so this shipped with zero coverage of the
267
+ actual `gh` calls.
268
+
269
+ ## 0.2.0 (2026-07-14) — first live-repo fixes
270
+
271
+ Fixes found by running shipflow end-to-end against a real repo
272
+ (`natejswenson/1.00s`) for the first time, beyond the read-only smoke test
273
+ against `claude-skills` itself:
274
+
275
+ - **First-run setup is now an explicit, unskippable interview.** SKILL.md's
276
+ setup steps must present detected branch names, `requiredChecks`, and the
277
+ resolved `protectionOwner` and wait for confirmation before writing
278
+ `.github/shipflow.json` — even when the detected values already look
279
+ correct. Previously nothing stopped an orchestrating agent from silently
280
+ narrating findings and proceeding straight to the config write.
281
+ - **Default-branch mismatch detection.** `detect` now reports the repo's
282
+ actual GitHub default branch (`repoSettings.defaultBranch`). First-run
283
+ setup surfaces a mismatch against the assumed `main` name and asks the
284
+ user to either map shipflow's `main` role onto the existing default branch
285
+ name, or rename the repo's default branch via the new
286
+ `rename-default-branch` command.
287
+ - **New `rename-default-branch` command**, wrapping GitHub's native
288
+ branch-rename endpoint (which retargets the default-branch pointer and
289
+ open PRs automatically when the renamed branch is the current default).
290
+ - **Honest classification of the tier-gated ruleset failure.** Creating the
291
+ deletion-protection ruleset 403s on private repos without GitHub
292
+ Pro/Team/Enterprise (rulesets are free for public repos only). This now
293
+ surfaces as a `skipped` entry with a clear reason instead of an `errors`
294
+ entry — it's an expected environment limitation, not a shipflow bug. No
295
+ fallback to classic branch protection was added (declined — out of scope
296
+ for this fix).
297
+ - **`requiredChecks` candidates are now filtered to actually PR-triggered
298
+ jobs.** `detect`'s `workflows.jobNames` previously listed every job name
299
+ from every workflow file regardless of its `on:` trigger — a
300
+ `schedule`/`workflow_dispatch`-only job (like `1.00s`'s `weekly-archive.yml`)
301
+ could be picked as a required check that would never run on a PR and
302
+ would block every future merge forever. Now only jobs from
303
+ `pull_request`/`pull_request_target`-triggered workflows are offered as
304
+ candidates.
305
+ - **Agent-driven CI scaffolding when no PR check exists.** Rather than
306
+ teaching shipflow's deterministic CLI about every language/build-tool
307
+ ecosystem, first-run setup now has the orchestrating agent investigate the
308
+ repo and draft a starter `pull_request`-triggered build+test workflow when
309
+ the (now-accurate) required-checks candidate list is empty, with the same
310
+ confirm-before-write discipline as every other step — never a silent
311
+ overwrite, always shown to the user first.
312
+
313
+ ## 0.1.0 (2026-07-14) — Phase A: manual-gate core
314
+
315
+ Initial release. Implements the fully-specified, reference-repo-validated slice
316
+ of the [shipflow design](../../docs/plans/2026-07-14-shipflow-skill-design.md):
317
+
318
+ - Long-lived `dev`/`main` branches with configurable names.
319
+ - `dev → main` promotion PRs that auto-merge once configured required checks pass.
320
+ - Automatic branch cleanup (`delete_branch_on_merge` + a deletion ruleset) for
321
+ every branch except `dev`/`main` — zero custom mutation logic, a native
322
+ GitHub setting.
323
+ - `protectionOwner` detection: defers to an existing settings-as-code
324
+ mechanism (e.g. `repo-settings.sh`, Terraform) rather than installing a
325
+ competing ruleset, with an explicit user prompt when protection exists
326
+ with no artifact behind it.
327
+ - `release.mode: "manual-gate"` — the deliberate, ask-before-tagging release
328
+ flow (a durable `release-pending` label survives the async gap between a
329
+ promotion merging and the next interactive `shipflow` run).
330
+
331
+ `release.mode: "auto"` (release-please-driven automatic tagging) is accepted
332
+ in the config schema but **not yet implemented** — `apply.mjs` refuses to run
333
+ against an `"auto"` config with a clear "not yet implemented" error rather
334
+ than silently no-oping. Tracked as Phase B; needs a live GitHub sandbox to
335
+ build and verify the two-hop `RELEASE_PAT` credential wiring and the
336
+ release-please byte-equality pre-flight safely.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nate Swenson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,66 @@
1
+ # shipflow
2
+
3
+ [![npm](https://img.shields.io/npm/v/@natjswenson/shipflow?color=blue)](https://www.npmjs.com/package/@natjswenson/shipflow)
4
+ [![license](https://img.shields.io/npm/l/@natjswenson/shipflow)](./LICENSE)
5
+
6
+ A Claude Code skill that scaffolds a configurable branching, auto-merge, branch-cleanup, and release-tagging workflow into any repo — across three selectable patterns, not just one.
7
+
8
+ Run it in a target repo and it detects existing branch protection, CI checks, release conventions, and which branching pattern the repo already uses, shows you a plan, and only mutates anything after you confirm. The skill package is identical everywhere — the actual policy (workflow pattern, branch names, required checks, release mode, ...) lives in the target repo's own `.github/shipflow.json`, committed and auditable.
9
+
10
+ ## Patterns
11
+
12
+ | Pattern | Shape |
13
+ |---|---|
14
+ | `dev-main-promotion` | Long-lived `dev` + `main`; a promotion PR auto-merges `dev` into `main` |
15
+ | `github-flow` | Single long-lived `main`; every PR merges (and auto-merges) directly to it |
16
+ | `gitflow` | `develop` + `main` + transient `release/*`/`hotfix/*` branches, for software maintaining multiple released versions concurrently |
17
+
18
+ `shipflow detect` scores all three against the repo's existing shape (branches, tags, workflow files) and either confirms a confident match with you or asks you to pick when detection is ambiguous or the repo is greenfield — it never silently picks one.
19
+
20
+ ## How it works
21
+
22
+ 1. **`/shipflow` in Claude Code** runs an interactive setup interview — detects the workflow pattern, branch protection, CI, and the default branch, confirms them (plus `requiredChecks` and `protectionOwner`) with you, and writes `.github/shipflow.json`.
23
+ 2. **`shipflow plan`** diffs that config against live repo state and shows exactly what would change, before anything is touched.
24
+ 3. **`shipflow apply`** — only after you confirm — renders the resolved pattern's workflow file(s) and makes the confirmed mutations. Nothing happens outside what the plan showed.
25
+ 4. Ongoing: promotions/merges auto-merge once required checks pass; a durable `release-pending` label survives the async gap until a later `shipflow releases` check asks whether to cut a release.
26
+
27
+ ## Quick start
28
+
29
+ All deterministic work runs through the published CLI:
30
+
31
+ ```sh
32
+ npx -y @natjswenson/shipflow@latest detect --repo . --main main --dev dev
33
+ ```
34
+
35
+ > **Always pin `@latest`.** Without an explicit version/tag, `npx` can silently resolve a stale install already on your `PATH` instead of fetching the current version from the registry — with no warning. If you've ever run `npm install -g @natjswenson/shipflow` for manual testing, remove it: `npm uninstall -g @natjswenson/shipflow`.
36
+
37
+ Full interactive setup flow: [`skills/shipflow/SKILL.md`](skills/shipflow/SKILL.md).
38
+
39
+ ## Commands
40
+
41
+ | Command | What it does |
42
+ |---|---|
43
+ | `detect --repo <path> [--main <name>] [--dev <name>]` | Inspect live repo state: branch protection, CI checks, release conventions |
44
+ | `plan --repo <path>` | Diff `.github/shipflow.json` against live state; prints what would change + a state hash |
45
+ | `apply --repo <path> --expect-state-hash <hash> [--dry-run] [--force <id> --force-reason <text>]` | Apply a confirmed plan |
46
+ | `releases --repo <path>` | List `dev → main` promotions still labeled `release-pending` |
47
+ | `release-dispatch --repo <path> --pr <n> --workflow-file <f>... --ref <ref>` | Dispatch each changed skill's release workflow; clear the label on success |
48
+ | `rename-default-branch --repo <path> --branch <old> --to <new>` | One-time bootstrap: rename a repo's default branch |
49
+
50
+ Every command prints JSON to stdout.
51
+
52
+ ## Status
53
+
54
+ **`release.mode: "manual-gate"`** (the only implemented mode) is live-validated end-to-end — dogfooded on this repo (`claude-skills`) and an external repo (`natejswenson/1.00s`). A full Siege security audit found and fixed 9 findings (1 Critical, 1 High, the rest Medium/Low) before wider rollout; zero Critical/High findings remain open. See [`CHANGELOG.md`](./CHANGELOG.md) for the fix-by-fix history.
55
+
56
+ **`release.mode: "auto"`** (fully automatic tagging via `release-please`) is accepted in the config schema but not yet implemented — `apply` refuses with a clear error until it ships.
57
+
58
+ ## Design
59
+
60
+ [`docs/plans/2026-07-14-shipflow-skill-design.md`](../../docs/plans/2026-07-14-shipflow-skill-design.md) — the original single-pattern design (7 rounds of adversarial review, score 12 → 0).
61
+
62
+ [`docs/plans/2026-07-16-shipflow-multi-pattern-design.md`](../../docs/plans/2026-07-16-shipflow-multi-pattern-design.md) — the multi-pattern registry design (10 rounds of adversarial review).
63
+
64
+ ## License
65
+
66
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@natjswenson/shipflow",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Scaffold a configurable dev/main branching, auto-merge, branch-cleanup, and release-tagging workflow into any repo",
5
5
  "license": "MIT",
6
6
  "author": "Nate Swenson",
@@ -41,7 +41,9 @@
41
41
  },
42
42
  "scripts": {
43
43
  "test": "node --test \"tests/**/*.test.mjs\"",
44
- "audit": "npm audit --audit-level=moderate"
44
+ "audit": "npm audit --audit-level=moderate",
45
+ "prepack": "cp ../../README.md ../../LICENSE ../../CHANGELOG.md .",
46
+ "postpack": "rm -f README.md LICENSE CHANGELOG.md"
45
47
  },
46
48
  "devDependencies": {
47
49
  "yaml": "^2.9.0"