@natjswenson/shipflow 0.2.5 → 0.2.6
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/SKILL.md +18 -9
- package/package.json +1 -1
- package/skill-invariants.json +5 -0
package/SKILL.md
CHANGED
|
@@ -7,8 +7,17 @@ user_invocable: true
|
|
|
7
7
|
# /shipflow — branching + release-automation setup
|
|
8
8
|
|
|
9
9
|
All deterministic work is delegated to the CLI. Invoke it as
|
|
10
|
-
`npx -y @natjswenson/shipflow <command
|
|
11
|
-
|
|
10
|
+
`npx -y @natjswenson/shipflow@latest <command>` — **always with the explicit
|
|
11
|
+
`@latest` tag, never bare `@natjswenson/shipflow`.** Without a version/tag,
|
|
12
|
+
`npx` prefers an already-resolvable install on `PATH` (e.g. a stale global
|
|
13
|
+
`npm install -g @natjswenson/shipflow` from a prior manual test) over
|
|
14
|
+
fetching the current version from the registry, and does so silently with
|
|
15
|
+
no warning. This isn't hypothetical: it happened in this exact repo — the
|
|
16
|
+
same command with the `@latest` tag omitted silently ran a stale global
|
|
17
|
+
0.2.0 install (missing every fix through 0.2.5, including the Critical
|
|
18
|
+
template-injection fix), while `npx -y @natjswenson/shipflow@latest -v`
|
|
19
|
+
correctly resolved 0.2.5. Every command prints JSON to stdout — parse it,
|
|
20
|
+
don't try to re-derive what it computed.
|
|
12
21
|
|
|
13
22
|
**This skill never mutates repo state directly.** Every mutating action goes
|
|
14
23
|
through `shipflow apply`, and the computed plan is always shown to the user
|
|
@@ -30,7 +39,7 @@ user; the CLI is the only thing that *does*.
|
|
|
30
39
|
|
|
31
40
|
1. **Detect.** Run:
|
|
32
41
|
```
|
|
33
|
-
npx -y @natjswenson/shipflow detect --repo <path> --main main --dev dev
|
|
42
|
+
npx -y @natjswenson/shipflow@latest detect --repo <path> --main main --dev dev
|
|
34
43
|
```
|
|
35
44
|
(Use whatever branch names the user has, or `main`/`dev` as a starting guess — you'll confirm them next.) This prints a `RepoState` plus a `protectionOwnerClassification` of `"external"`, `"shipflow"`, or `"ambiguous"`.
|
|
36
45
|
|
|
@@ -38,7 +47,7 @@ user; the CLI is the only thing that *does*.
|
|
|
38
47
|
- **Map onto the existing default branch** — set the config's `branches.main` to the detected default branch name and continue with the rest of setup treating that as "main." No mutating calls needed; `branches.main` is fully configurable.
|
|
39
48
|
- **Switch the repo's default branch to `main`** — flag this as a bigger, more disruptive action than the rest of setup (it affects every collaborator and every open PR), get a distinct explicit confirmation for it specifically, separate from the general setup go-ahead, then run:
|
|
40
49
|
```
|
|
41
|
-
npx -y @natjswenson/shipflow rename-default-branch --repo <path> --branch <old-default> --to main
|
|
50
|
+
npx -y @natjswenson/shipflow@latest rename-default-branch --repo <path> --branch <old-default> --to main
|
|
42
51
|
```
|
|
43
52
|
GitHub natively retargets the default-branch pointer and open PRs' base ref. On success, tell the user their own local checkout still points at the old name and needs `git fetch origin && git checkout main` to follow, then re-run step 1's `detect` (repo state changed) before continuing.
|
|
44
53
|
|
|
@@ -57,18 +66,18 @@ user; the CLI is the only thing that *does*.
|
|
|
57
66
|
|
|
58
67
|
7. **Show the plan.** Run:
|
|
59
68
|
```
|
|
60
|
-
npx -y @natjswenson/shipflow plan --repo <path>
|
|
69
|
+
npx -y @natjswenson/shipflow@latest plan --repo <path>
|
|
61
70
|
```
|
|
62
71
|
This prints `{ plan, stateHash }`. Present `plan.creates`/`plan.updates`/`plan.noops` to the user in plain language — what will be created, what will change, what's already correct. **Wait for explicit confirmation before proceeding.** If any entry has `handEditDetected: true`, call it out specifically and ask whether to override (see step 9).
|
|
63
72
|
|
|
64
73
|
8. **Dry-run apply** (optional sanity check, same output shape as the real apply but nothing is mutated):
|
|
65
74
|
```
|
|
66
|
-
npx -y @natjswenson/shipflow apply --repo <path> --dry-run
|
|
75
|
+
npx -y @natjswenson/shipflow@latest apply --repo <path> --dry-run
|
|
67
76
|
```
|
|
68
77
|
|
|
69
78
|
9. **Apply for real**, passing the `stateHash` from step 7's plan output as `--expect-state-hash` — this is the TOCTOU guard: if repo state drifted between the plan you showed the user and this call, `apply` refuses to mutate anything and tells you to re-plan. **`--expect-state-hash` is mandatory for a real (non-dry-run) apply** — omitting it is a hard CLI refusal, not a silent skip of the check; the only way around it is the explicitly-named `--skip-hash-check` escape hatch, which you should never reach for as a matter of course.
|
|
70
79
|
```
|
|
71
|
-
npx -y @natjswenson/shipflow apply --repo <path> --expect-state-hash <hash-from-step-7>
|
|
80
|
+
npx -y @natjswenson/shipflow@latest apply --repo <path> --expect-state-hash <hash-from-step-7>
|
|
72
81
|
```
|
|
73
82
|
If a `handEditDetected` entry was confirmed for override in step 7, pass `--force <entry-id>` (repeatable — one flag per confirmed entry id, never a blanket override) **and** `--force-reason "<short justification>"` — the CLI refuses any `--force` without an accompanying reason, and that reason is echoed back in the apply result for auditability. Write a real justification tied to the user's actual confirmation (e.g. `--force-reason "user confirmed hand-edit override for the branch-rename migration on 2026-07-15"`), never a placeholder string.
|
|
74
83
|
|
|
@@ -84,7 +93,7 @@ This is a **separate, later invocation** from the one that ran the promotion's `
|
|
|
84
93
|
|
|
85
94
|
1. Run:
|
|
86
95
|
```
|
|
87
|
-
npx -y @natjswenson/shipflow releases --repo <path>
|
|
96
|
+
npx -y @natjswenson/shipflow@latest releases --repo <path>
|
|
88
97
|
```
|
|
89
98
|
This returns every `dev → main` PR still labeled `release-pending`, each with a `merged` flag (confirmed independently, not just inferred from the label).
|
|
90
99
|
|
|
@@ -92,7 +101,7 @@ This is a **separate, later invocation** from the one that ran the promotion's `
|
|
|
92
101
|
|
|
93
102
|
3. If yes, dispatch each changed skill's release workflow and clear the label **only after every dispatch is confirmed successful**:
|
|
94
103
|
```
|
|
95
|
-
npx -y @natjswenson/shipflow release-dispatch --repo <path> --pr <number> --workflow-file <skill1>.yml --workflow-file <skill2>.yml --ref main
|
|
104
|
+
npx -y @natjswenson/shipflow@latest release-dispatch --repo <path> --pr <number> --workflow-file <skill1>.yml --workflow-file <skill2>.yml --ref main
|
|
96
105
|
```
|
|
97
106
|
If `dispatched` shows a partial failure, the label is deliberately left in place — report this to the user and note the promotion will resurface next time `releases` is checked; a later re-dispatch is safe (each skill's release workflow is idempotent).
|
|
98
107
|
|
package/package.json
CHANGED
package/skill-invariants.json
CHANGED
|
@@ -75,6 +75,11 @@
|
|
|
75
75
|
"id": "force-requires-reason",
|
|
76
76
|
"pattern": "refuses any `--force` without an accompanying reason",
|
|
77
77
|
"rationale": "Found by a Siege security audit (2026-07-15, SIEGE-2026-07-15-002): --force had zero code-level friction beyond the flag itself, so a confused or prompt-injected agent could force through an unprotected merge or a tampered template with no audit trail. --force now requires --force-reason, echoed back in the apply result."
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"id": "npx-must-pin-latest",
|
|
81
|
+
"pattern": "always with the explicit\\s+`@latest` tag, never bare",
|
|
82
|
+
"rationale": "Self-discovered 2026-07-15 during PAT-wiring dogfood on claude-skills itself: a bare `npx -y @natjswenson/shipflow <command>` silently resolved a stale global install (0.2.0) instead of fetching the current version from the registry, with no warning — meaning every fix through 0.2.5 (including the Critical template-injection fix) was silently skipped. Every CLI invocation in this file must pin @latest."
|
|
78
83
|
}
|
|
79
84
|
],
|
|
80
85
|
"cli_commands_referenced": ["detect", "plan", "apply", "releases", "release-dispatch", "rename-default-branch"]
|