@natjswenson/shipflow 0.3.0 → 0.3.1

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