Skip to content

Commit 5c10b66

Browse files
author
catlog22
committed
feat(ship): add platform publish and GitHub release phases
Extend ship skill from 5 to 7 phases: adds Phase 6 (npm/PyPI/crates publish) and Phase 7 (git tag + gh release create with structured notes). Phase 5 (PR creation) is now skippable for trunk-based workflows. Captures the v7.3.7 release workflow (npm publish + tag + GitHub release) so future releases follow the same protocol.
1 parent 3a0d6d7 commit 5c10b66

4 files changed

Lines changed: 299 additions & 32 deletions

File tree

.claude/skills/ship/SKILL.md

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
---
22
name: ship
3-
description: Structured release pipeline with pre-flight checks, AI code review, version bump, changelog, and PR creation. Triggers on "ship", "release", "publish".
3+
description: Structured release pipeline with pre-flight checks, AI code review, version bump, changelog, PR creation, platform publish, and GitHub release. Triggers on "ship", "release", "publish".
44
allowed-tools: Read, Write, Bash, Glob, Grep
55
---
66

77
# Ship
88

9-
Structured release pipeline that guides code from working branch to pull request through 5 gated phases: pre-flight checks, automated code review, version bump, changelog generation, and PR creation.
9+
Structured release pipeline that guides code from working branch to a published release through 7 gated phases: pre-flight checks, automated code review, version bump, changelog generation, PR creation, platform publish (npm/PyPI/etc.), and GitHub release.
10+
11+
Phases 5 (PR) and 6 (Publish) are conditionally skippable — trunk-based workflows that commit directly to `main` skip Phase 5; private packages skip Phase 6. Phase 7 (GitHub release) is the terminal phase and always runs unless the publish step failed.
1012

1113
## Key Design Principles
1214

@@ -49,11 +51,24 @@ User: "ship" / "release" / "publish"
4951
│ → Output: commit SHA │
5052
│ → Gate: Push successful │
5153
├──────────────────────────────────────────────────────────┤
52-
│ Phase 5: PR Creation
54+
│ Phase 5: PR Creation (skippable for trunk-based)
5355
│ → gh pr create with structured body │
5456
│ → auto-link issues from commits │
5557
│ → Output: PR URL │
56-
│ → Gate: PR created │
58+
│ → Gate: PR created OR skipped (direct-to-main) │
59+
├──────────────────────────────────────────────────────────┤
60+
│ Phase 6: Platform Publish (skippable if private) │
61+
│ → detect registry (npm / PyPI / crates.io) │
62+
│ → npm publish / twine upload / cargo publish │
63+
│ → verify new version is live on registry │
64+
│ → Output: published artifact metadata │
65+
│ → Gate: registry confirms version OR skipped (private) │
66+
├──────────────────────────────────────────────────────────┤
67+
│ Phase 7: GitHub Release │
68+
│ → git tag -a vX.Y.Z + push tag │
69+
│ → gh release create with structured notes │
70+
│ → Output: release URL │
71+
│ → Gate: release URL returned │
5772
└──────────────────────────────────────────────────────────┘
5873
```
5974

@@ -65,7 +80,9 @@ Execute phases sequentially. Each phase has a gate condition — if the gate fai
6580
2. **Phase 2**: [Code Review](phases/02-code-review.md) -- AI-powered diff review with risk assessment
6681
3. **Phase 3**: [Version Bump](phases/03-version-bump.md) -- Detect and update version across project types
6782
4. **Phase 4**: [Changelog & Commit](phases/04-changelog-commit.md) -- Generate changelog, create release commit, push
68-
5. **Phase 5**: [PR Creation](phases/05-pr-creation.md) -- Create PR with structured body and issue links
83+
5. **Phase 5**: [PR Creation](phases/05-pr-creation.md) -- Create PR with structured body and issue links *(skip for trunk-based)*
84+
6. **Phase 6**: [Platform Publish](phases/06-platform-publish.md) -- Publish to npm / PyPI / crates.io *(skip for private packages)*
85+
7. **Phase 7**: [GitHub Release](phases/07-github-release.md) -- Tag release commit and publish GitHub release notes
6986

7087
## Pre-Flight Checklist (Quick Reference)
7188

@@ -84,10 +101,10 @@ Every execution terminates with one of:
84101

85102
| Status | When |
86103
|--------|------|
87-
| **DONE** | All 5 phases completed, PR created |
88-
| **DONE_WITH_CONCERNS** | PR created but with review warnings or non-critical issues |
89-
| **BLOCKED** | A gate failed (dirty git, tests fail, push rejected) |
90-
| **NEEDS_CONTEXT** | Cannot determine bump type, ambiguous branch target |
104+
| **DONE** | All applicable phases completed, GitHub release published |
105+
| **DONE_WITH_CONCERNS** | Release published but with review warnings, skipped publish (private pkg), or non-critical issues |
106+
| **BLOCKED** | A gate failed (dirty git, tests fail, push rejected, publish failed, tag conflict) |
107+
| **NEEDS_CONTEXT** | Cannot determine bump type, ambiguous branch target, unclear registry target |
91108

92109
### Escalation
93110

@@ -101,5 +118,7 @@ Follows the Three-Strike Rule (SKILL-DESIGN-SPEC section 14). On 3 consecutive f
101118
| [phases/02-code-review.md](phases/02-code-review.md) | AI-powered diff review |
102119
| [phases/03-version-bump.md](phases/03-version-bump.md) | Version detection and bump |
103120
| [phases/04-changelog-commit.md](phases/04-changelog-commit.md) | Changelog generation and release commit |
104-
| [phases/05-pr-creation.md](phases/05-pr-creation.md) | PR creation with issue linking |
121+
| [phases/05-pr-creation.md](phases/05-pr-creation.md) | PR creation with issue linking (skippable) |
122+
| [phases/06-platform-publish.md](phases/06-platform-publish.md) | npm / PyPI / crates.io publish (skippable) |
123+
| [phases/07-github-release.md](phases/07-github-release.md) | Tag and GitHub release notes |
105124
| [../_shared/SKILL-DESIGN-SPEC.md](../_shared/SKILL-DESIGN-SPEC.md) | Skill design spec (completion protocol, escalation) |

.claude/skills/ship/phases/05-pr-creation.md

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
Create a pull request via GitHub CLI with a structured body, linked issues, and release metadata.
44

5+
## Skip Conditions
6+
7+
Skip this phase (proceed directly to Phase 6) when:
8+
9+
- Current branch is `main` / `master` and the project uses trunk-based development (release commit was pushed directly to the default branch)
10+
- User explicitly passes `--no-pr`
11+
- No remote write access to create PRs (rare — usually a BLOCKED signal instead)
12+
13+
When skipped, record `phase: "pr-creation", overall: "skip", reason: "..."` in the skill output and move on.
14+
515
## Objective
616

717
- Create a PR using `gh pr create` with structured body
@@ -11,7 +21,7 @@ Create a pull request via GitHub CLI with a structured body, linked issues, and
1121

1222
## Gate Condition
1323

14-
PR created successfully and URL returned.
24+
PR created successfully and URL returned — OR skip condition matched.
1525

1626
## Execution Steps
1727

@@ -138,26 +148,8 @@ echo "PR created: $pr_url"
138148
}
139149
```
140150
141-
## Completion
142-
143-
After PR creation, output the final Completion Status:
144-
145-
```
146-
## STATUS: DONE
147-
148-
**Summary**: Released vX.Y.Z — PR created at {pr_url}
151+
## Next Phase
149152
150-
### Details
151-
- Phases completed: 5/5
152-
- Version: {previous} -> {new} ({bump_type})
153-
- PR: {pr_url}
154-
- Key outputs: CHANGELOG.md updated, release commit pushed, PR created
155-
156-
### Outputs
157-
- CHANGELOG.md (updated)
158-
- {version_file} (version bumped)
159-
- Release commit: {sha}
160-
- PR: {pr_url}
161-
```
153+
After PR creation (or skip), proceed to [Phase 6: Platform Publish](06-platform-publish.md).
162154
163-
If there were review warnings, use `DONE_WITH_CONCERNS` and list the warnings in the Details section.
155+
**Note**: Final completion status is emitted by Phase 7 (the terminal phase), not here. PR URL flows through to the final STATUS output as part of the release record.
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Phase 6: Platform Publish
2+
3+
Publish the released version to its package registry (npm, PyPI, etc.). Runs after the release commit is merged to the default branch (or immediately after push for trunk-based workflows).
4+
5+
## Objective
6+
7+
- Detect the target registry from the version file type
8+
- Verify the local working tree is on the release commit
9+
- Publish to the registry
10+
- Capture the published artifact metadata (name, version, tarball URL)
11+
12+
## Gate Condition
13+
14+
Publish command exits 0 and registry confirms the new version is live.
15+
16+
## When to Skip
17+
18+
- Project is marked `"private": true` in `package.json` (or equivalent private flag) — **skip and record as N/A**
19+
- User explicitly passes `--no-publish`
20+
- Registry is unreachable — report BLOCKED with diagnostic, do not retry blindly
21+
22+
## Execution Steps
23+
24+
### Step 1: Detect Registry
25+
26+
| Version File | Registry | Publish Command |
27+
|--------------|----------|-----------------|
28+
| `package.json` (not private) | npm | `npm publish` |
29+
| `pyproject.toml` | PyPI | `python -m build && twine upload dist/*` |
30+
| `Cargo.toml` | crates.io | `cargo publish` |
31+
| `VERSION` / other || SKIP (no registry) |
32+
33+
```bash
34+
if [ -f "package.json" ]; then
35+
is_private=$(node -p "require('./package.json').private === true" 2>/dev/null || echo "false")
36+
if [ "$is_private" = "true" ]; then
37+
echo "SKIP: package.json marked private"
38+
exit 0
39+
fi
40+
pkg_name=$(node -p "require('./package.json').name")
41+
pkg_version=$(node -p "require('./package.json').version")
42+
fi
43+
```
44+
45+
### Step 2: Verify Working Tree
46+
47+
Before publishing, confirm the local tree matches what was committed:
48+
49+
```bash
50+
git status --porcelain # must be empty
51+
git rev-parse HEAD # record the commit SHA being published
52+
```
53+
54+
If the tree is dirty, abort — partial/uncommitted changes must never land in a published artifact.
55+
56+
### Step 3: Publish
57+
58+
**npm**:
59+
```bash
60+
# Default — runs the package's own prepublish hooks (build, clean, etc.)
61+
npm publish 2>&1 | tee /tmp/publish.log
62+
63+
# For scoped packages that need public access:
64+
# npm publish --access public
65+
```
66+
67+
Background execution is acceptable for long publish operations (large packages, slow network). Use the Bash tool's `run_in_background: true` and monitor the output file.
68+
69+
**PyPI**:
70+
```bash
71+
rm -rf dist/ build/
72+
python -m build
73+
twine upload dist/* 2>&1 | tee /tmp/publish.log
74+
```
75+
76+
### Step 4: Verify Publish
77+
78+
```bash
79+
# npm — query the registry for the new version
80+
npm view "$pkg_name@$pkg_version" version
81+
82+
# PyPI — query the simple index
83+
curl -sfI "https://pypi.org/pypi/$pkg_name/$pkg_version/json" | head -1
84+
```
85+
86+
If verification fails, check the publish log — a common cause is an `OTP required` prompt for 2FA npm accounts. Surface the prompt to the user.
87+
88+
## Output
89+
90+
```json
91+
{
92+
"phase": "platform-publish",
93+
"registry": "npm|pypi|crates|none",
94+
"package_name": "claude-code-workflow",
95+
"published_version": "7.3.7",
96+
"tarball_size": "8.2 MB",
97+
"total_files": 2280,
98+
"published_sha": "3a0d6d71",
99+
"overall": "pass|skip|fail"
100+
}
101+
```
102+
103+
## Next Phase
104+
105+
Proceed to [Phase 7: GitHub Release](07-github-release.md).
106+
107+
If publish fails, report BLOCKED — **do not** proceed to GitHub release, since the release notes would point to a version that doesn't exist on the registry.
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# Phase 7: GitHub Release
2+
3+
Tag the release commit and publish a GitHub release with auto-generated release notes.
4+
5+
## Objective
6+
7+
- Create an annotated git tag `vX.Y.Z` pointing at the release commit
8+
- Push the tag to origin
9+
- Create a GitHub release with structured release notes (summary, commit list, migration notes, compare link)
10+
11+
## Gate Condition
12+
13+
`gh release create` returns a release URL.
14+
15+
## Prerequisite
16+
17+
- Phase 6 (Platform Publish) completed with status `pass` or `skip`. **Do not create a release for a version that failed to publish.**
18+
- `gh` CLI authenticated (`gh auth status`).
19+
20+
## Execution Steps
21+
22+
### Step 1: Create and Push Annotated Tag
23+
24+
```bash
25+
# Tag the release commit
26+
git tag -a "v${new_version}" -m "v${new_version}"
27+
28+
# Push to origin
29+
git push origin "v${new_version}"
30+
```
31+
32+
**Do NOT** use lightweight tags — annotated tags carry the release metadata and are what `gh release create` expects.
33+
34+
If the tag already exists (e.g. a prior attempt pushed the tag), decide:
35+
- Same commit → skip tag creation, proceed to release
36+
- Different commit → BLOCKED, tag must be deleted and recreated deliberately (destructive, require user confirmation)
37+
38+
### Step 2: Compose Release Notes
39+
40+
Release notes have four sections — keep them focused and scannable:
41+
42+
```markdown
43+
## Summary
44+
45+
- **{highlight 1}**: one-line description of the most impactful change
46+
- **{highlight 2}**: another key change (feature/fix/perf)
47+
- **Scope**: rough scale indicator (e.g. "74 files across X, Y, Z")
48+
49+
## What's Changed
50+
51+
- `<commit subject>` (<short SHA>)
52+
- `<commit subject>` (<short SHA>)
53+
54+
## Migration
55+
56+
{Migration notes if any, or "No action required."}
57+
58+
**Full Changelog**: https://github.com/{owner}/{repo}/compare/v{prev}...v{new}
59+
```
60+
61+
**Sourcing the content**:
62+
63+
- **Summary bullets**: synthesize 2-4 highlights from the commits since last release. Prefer `feat:` / `refactor:` / breaking changes. Call out compatibility posture explicitly if it's non-trivial.
64+
- **What's Changed**: one line per commit since `git describe --tags --abbrev=0` (exclude merge commits and the `chore: bump version` commit itself).
65+
- **Migration**: only include if users need to take action. Default to "No action required."
66+
- **Compare link**: always include — it's the authoritative diff.
67+
68+
```bash
69+
prev_tag=$(git describe --tags --abbrev=0 HEAD^)
70+
commits=$(git log "$prev_tag..v${new_version}^" --pretty=format:"- \`%s\` (%h)" --no-merges)
71+
compare_url="https://github.com/${owner}/${repo}/compare/${prev_tag}...v${new_version}"
72+
```
73+
74+
### Step 3: Create GitHub Release
75+
76+
```bash
77+
gh release create "v${new_version}" \
78+
--title "v${new_version} — {short descriptive suffix}" \
79+
--notes "$(cat <<'EOF'
80+
## Summary
81+
82+
- ...
83+
84+
## What's Changed
85+
86+
- ...
87+
88+
## Migration
89+
90+
...
91+
92+
**Full Changelog**: ...
93+
EOF
94+
)"
95+
```
96+
97+
**Title convention**: `v{X.Y.Z} — {Short Theme}` (e.g. `v7.3.7 — Session ID Chronological Sort`). The suffix summarizes the release theme in 2-5 words.
98+
99+
**Flags**:
100+
- Omit `--draft` unless the user wants to review notes first
101+
- Omit `--prerelease` for standard releases; use it for `-beta` / `-rc` tags
102+
- `--latest` is automatic when the tag is the highest semver
103+
104+
### Step 4: Capture Release URL
105+
106+
`gh release create` prints the release URL on stdout — capture and report it:
107+
108+
```bash
109+
release_url=$(gh release create ... 2>&1 | tail -1)
110+
echo "Release: $release_url"
111+
```
112+
113+
## Output
114+
115+
```json
116+
{
117+
"phase": "github-release",
118+
"tag": "v7.3.7",
119+
"tagged_sha": "3a0d6d71",
120+
"release_url": "https://github.com/owner/repo/releases/tag/v7.3.7",
121+
"previous_tag": "v7.3.6",
122+
"commits_included": 4,
123+
"overall": "pass|fail"
124+
}
125+
```
126+
127+
## Completion
128+
129+
After the GitHub release is created, emit the final Completion Status:
130+
131+
```
132+
## STATUS: DONE
133+
134+
**Summary**: Released vX.Y.Z — published to {registry} and GitHub
135+
136+
### Details
137+
- Version: {previous} -> {new} ({bump_type})
138+
- npm: https://www.npmjs.com/package/{pkg_name}/v/{new}
139+
- Release: {release_url}
140+
- Tag: v{new} -> {commit_sha}
141+
142+
### Outputs
143+
- CHANGELOG.md (updated)
144+
- Release commit: {sha}
145+
- npm tarball: {size}, {files} files
146+
- GitHub release: {release_url}
147+
```
148+
149+
Use `DONE_WITH_CONCERNS` if the publish step was skipped (private package) or if the release notes were auto-generated without human review.

0 commit comments

Comments
 (0)