bigpowers 2.8.0 → 2.10.0

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/.pi/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bigpowers",
3
- "version": "2.8.0",
4
- "description": "63 skills — 61 agent skills for spec-driven, test-first software development by solo developers",
3
+ "version": "2.10.0",
4
+ "description": "64 skills — 61 agent skills for spec-driven, test-first software development by solo developers",
5
5
  "keywords": [
6
6
  "pi-package"
7
7
  ],
@@ -67,6 +67,66 @@ After all tests pass: extract duplication, deepen modules, apply SOLID principle
67
67
 
68
68
  After every behavior cycle, run the verify command from the active epic task. Show evidence before declaring the step done.
69
69
 
70
+ ### 6a. CI dry-run sub-step (when modifying workflows)
71
+
72
+ If this cycle modified files in `.github/workflows/`, run a CI dry-run before pushing:
73
+
74
+ ```bash
75
+ # 1. Check for workflow file changes
76
+ CHANGED_WORKFLOWS=$(git diff --name-only HEAD | grep '\.github/workflows/' || true)
77
+ if [ -n "$CHANGED_WORKFLOWS" ]; then
78
+ echo "==> CI dry-run: workflow files changed"
79
+ echo " $CHANGED_WORKFLOWS"
80
+
81
+ # 2. Validate YAML syntax
82
+ if command -v yamllint &>/dev/null; then
83
+ for f in $CHANGED_WORKFLOWS; do
84
+ yamllint "$f" && echo " OK: $f passes YAML lint" || echo " WARN: $f has YAML issues"
85
+ done
86
+ else
87
+ # Fallback: Python YAML parse
88
+ for f in $CHANGED_WORKFLOWS; do
89
+ python3 -c "import yaml; yaml.safe_load(open('$f'))" 2>/dev/null && \
90
+ echo " OK: $f YAML syntax valid" || \
91
+ echo " FAIL: $f has YAML syntax errors"
92
+ done
93
+ fi
94
+
95
+ # 3. Run actionlint if available
96
+ if command -v actionlint &>/dev/null; then
97
+ for f in $CHANGED_WORKFLOWS; do
98
+ actionlint "$f" && echo " OK: $f passes actionlint" || echo " WARN: $f has actionlint issues"
99
+ done
100
+ fi
101
+
102
+ # 4. Check common pitfalls
103
+ for f in $CHANGED_WORKFLOWS; do
104
+ # Missing permissions block
105
+ if ! grep -q 'permissions:' "$f"; then
106
+ echo " WARNING: $f missing permissions block — add one for security"
107
+ fi
108
+ # npm publish without NPM_TOKEN
109
+ if grep -q 'npm publish\|npx semantic-release' "$f" && ! grep -q 'NPM_TOKEN' "$f"; then
110
+ echo " WARNING: $f has npm publish/semantic-release but no NPM_TOKEN in secrets"
111
+ fi
112
+ # Hardcoded Node versions
113
+ if grep -q 'node-version: [0-9]' "$f"; then
114
+ echo " NOTE: $f has hardcoded Node version — consider node-version-file: .nvmrc"
115
+ fi
116
+ done
117
+
118
+ # 5. Suggest local dry-run
119
+ if command -v act &>/dev/null; then
120
+ echo " SUGGESTION: Run 'act push --dry-run' to test workflows locally"
121
+ fi
122
+ fi
123
+ ```
124
+
125
+ Checklist:
126
+ - [ ] YAML syntax validated for all changed workflow files
127
+ - [ ] No missing permissions, secrets, or hardcoded versions flagged
128
+ - [ ] Local dry-run suggested if `act` is available
129
+
70
130
  ### 7. Manual Verification Handover
71
131
 
72
132
  Once all tests pass: locate the Verification Script in the active epic capsule, present it to the user step-by-step, and wait for confirmation of behavioral correctness.
@@ -0,0 +1,260 @@
1
+ ---
2
+ description: "Package registry publishing for npm, crates.io, PyPI, and Homebrew. Verifies prerequisites, runs the publish command, confirms success, and surfaces actionable error hints on failure."
3
+ ---
4
+
5
+
6
+ # Publish Package
7
+
8
+ > **HARD GATE** — Do not attempt to publish without verifying prerequisites. Missing auth tokens, stale builds, or duplicate versions cause CI failures that are hard to debug post-push.
9
+ >
10
+ > **HARD GATE** — Always run `--dry-run` first. Package registries are append-only — a bad publish cannot be fully undone on most registries.
11
+
12
+ Publish packages to language-specific registries. Detects package type from manifest files, verifies publish prerequisites, runs the registry-specific publish command, and confirms the version appears on the registry.
13
+
14
+ ## Process
15
+
16
+ ### 1. Detect package type
17
+
18
+ Read the project root for manifest files to determine the package type:
19
+
20
+ | Manifest | Registry | Publish command |
21
+ |----------|----------|----------------|
22
+ | `package.json` | npm | `npm publish --access public` |
23
+ | `Cargo.toml` | crates.io | `cargo publish` |
24
+ | `setup.py` / `pyproject.toml` | PyPI | `twine upload dist/*` or `flit publish` |
25
+ | `Formula/<name>.rb` | Homebrew | `brew bump-formula-pr` |
26
+ | Multiple detected | Polyglot | Error: specify registry with `--registry <npm|crates.io|pypi|brew>` |
27
+
28
+ If no manifest is found, prompt the user to specify the type or pass `--type <npm|crates.io|pypi|brew>`.
29
+
30
+ ### 2. Verify prerequisites
31
+
32
+ Before attempting any publish, run all applicable checks:
33
+
34
+ **npm (`package.json`):**
35
+ ```bash
36
+ # Check auth token exists
37
+ if [ -z "${NPM_TOKEN:-}" ]; then
38
+ if [ ! -f ~/.npmrc ] || ! grep -q "_authToken" ~/.npmrc; then
39
+ echo "FAIL: NPM_TOKEN not set. Set via: export NPM_TOKEN=<token> or add //registry.npmjs.org/:_authToken=<token> to .npmrc"
40
+ exit 1
41
+ fi
42
+ fi
43
+
44
+ # Check version not already published
45
+ PACKAGE_NAME=$(node -p "require('./package.json').name")
46
+ CURRENT_VER=$(node -p "require('./package.json').version")
47
+ if npm view "$PACKAGE_NAME@$CURRENT_VER" version 2>/dev/null; then
48
+ echo "FAIL: Version $CURRENT_VER already published for $PACKAGE_NAME. Bump version first."
49
+ exit 1
50
+ fi
51
+
52
+ # Check build artifacts are fresh
53
+ if [ -d dist ] || [ -d lib ]; then
54
+ LATEST_BUILD=$(find dist lib 2>/dev/null -name "*.js" -o -name "*.cjs" -o -name "*.mjs" | xargs ls -t 2>/dev/null | head -1)
55
+ PACKAGE_MODIFIED=$(stat -f %m package.json 2>/dev/null || stat -c %Y package.json 2>/dev/null)
56
+ if [ -n "$LATEST_BUILD" ] && [ -n "$PACKAGE_MODIFIED" ]; then
57
+ BUILD_TIME=$(stat -f %m "$LATEST_BUILD" 2>/dev/null || stat -c %Y "$LATEST_BUILD" 2>/dev/null)
58
+ if [ "$BUILD_TIME" -lt "$PACKAGE_MODIFIED" ]; then
59
+ echo "WARNING: Build artifacts may be stale (package.json modified after last build). Run npm run build first."
60
+ fi
61
+ fi
62
+ fi
63
+
64
+ # Check CHANGELOG is updated
65
+ if [ -f CHANGELOG.md ]; then
66
+ if ! grep -q "$CURRENT_VER" CHANGELOG.md 2>/dev/null; then
67
+ echo "WARNING: Version $CURRENT_VER not found in CHANGELOG.md. Update changelog before publish."
68
+ fi
69
+ fi
70
+ ```
71
+
72
+ **crates.io (`Cargo.toml`):**
73
+ ```bash
74
+ # Check auth token exists
75
+ if [ -z "${CARGO_REGISTRY_TOKEN:-}" ]; then
76
+ if [ ! -f ~/.cargo/config.toml ] || ! grep -q "token" ~/.cargo/config.toml; then
77
+ echo "FAIL: CARGO_REGISTRY_TOKEN not set. Set via: export CARGO_REGISTRY_TOKEN=<token> or add to ~/.cargo/config.toml"
78
+ exit 1
79
+ fi
80
+ fi
81
+
82
+ # Check version not already published
83
+ CRATE_NAME=$(grep '^name' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')
84
+ CURRENT_VER=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')
85
+ if cargo search "$CRATE_NAME" 2>/dev/null | grep -q "^${CRATE_NAME}.*\"$CURRENT_VER\""; then
86
+ echo "FAIL: Version $CURRENT_VER already published for $CRATE_NAME. Bump version in Cargo.toml first."
87
+ exit 1
88
+ fi
89
+ ```
90
+
91
+ **PyPI (`setup.py` / `pyproject.toml`):**
92
+ ```bash
93
+ # Check auth token exists
94
+ if [ -z "${TWINE_PASSWORD:-}" ] && [ -z "${POETRY_PYPI_TOKEN_PYPI:-}" ]; then
95
+ if [ ! -f ~/.pypirc ]; then
96
+ echo "FAIL: PyPI token not configured. Set TWINE_PASSWORD or create ~/.pypirc"
97
+ exit 1
98
+ fi
99
+ fi
100
+
101
+ # Check for build artifacts
102
+ if [ ! -d dist ] || [ -z "$(ls dist/*.whl 2>/dev/null)" ]; then
103
+ echo "WARNING: No .whl files found in dist/. Run: python -m build"
104
+ fi
105
+ ```
106
+
107
+ ### 3. Run publish
108
+
109
+ After all prerequisite checks pass, run the registry-specific command:
110
+
111
+ ```bash
112
+ # npm
113
+ npm publish --access public
114
+
115
+ # crates.io
116
+ cargo publish
117
+
118
+ # PyPI
119
+ python -m twine upload dist/* # or: poetry publish
120
+
121
+ # Homebrew (opens PR, does not publish directly)
122
+ brew bump-formula-pr --url=<tarball-url> <formula-name>
123
+ ```
124
+
125
+ ### 4. Verify publish success
126
+
127
+ After publish, confirm the version appears on the registry:
128
+
129
+ ```bash
130
+ # npm
131
+ npm view "$PACKAGE_NAME" versions --json 2>/dev/null | grep -q "\"$CURRENT_VER\"" && echo "OK: npm publish confirmed"
132
+
133
+ # crates.io
134
+ cargo search "$CRATE_NAME" 2>/dev/null | grep -q "^${CRATE_NAME}.*\"$CURRENT_VER\"" && echo "OK: crates.io publish confirmed"
135
+
136
+ # PyPI
137
+ pip index versions "$PACKAGE_NAME" 2>/dev/null | grep -q "$CURRENT_VER" && echo "OK: PyPI publish confirmed"
138
+ ```
139
+
140
+ ### 5. Error handling
141
+
142
+ On failure, surface actionable hints:
143
+
144
+ ```bash
145
+ # Generic failure handler
146
+ if [ $? -ne 0 ]; then
147
+ case "$REGISTRY" in
148
+ npm)
149
+ echo "FAIL: npm publish failed."
150
+ echo " Common causes:"
151
+ echo " - NPM_TOKEN not set in secrets: add to GitHub repo secrets"
152
+ echo " - Version already published: bump version in package.json"
153
+ echo " - Two-factor auth required: use --otp=<code> flag"
154
+ echo " - Package scoped but not public: add --access public"
155
+ ;;
156
+ crates.io)
157
+ echo "FAIL: cargo publish failed."
158
+ echo " Common causes:"
159
+ echo " - CARGO_REGISTRY_TOKEN not configured: see ~/.cargo/config.toml"
160
+ echo " - Version already published: bump version in Cargo.toml"
161
+ echo " - Local changes not committed: cargo publish requires clean working tree"
162
+ ;;
163
+ pypi)
164
+ echo "FAIL: PyPI publish failed."
165
+ echo " Common causes:"
166
+ echo " - TWINE_PASSWORD not configured: set env var or ~/.pypirc"
167
+ echo " - Build artifacts missing: run python -m build first"
168
+ echo " - Version conflict: version already exists on PyPI"
169
+ ;;
170
+ esac
171
+ exit 1
172
+ fi
173
+ ```
174
+
175
+ ### 6. Dry-run mode (`--dry-run`)
176
+
177
+ Run `--dry-run` to verify all prerequisites without actually publishing:
178
+
179
+ ```bash
180
+ # Example output
181
+ $ publish-package --dry-run
182
+
183
+ [DRY-RUN] Detected package type: npm
184
+ [DRY-RUN] Package: my-package v0.4.0
185
+ [DRY-RUN] Checking NPM_TOKEN... OK
186
+ [DRY-RUN] Checking version 0.4.0 not already published... OK
187
+ [DRY-RUN] Checking build artifacts... WARNING: package.json modified after build
188
+ [DRY-RUN] Checking CHANGELOG... OK
189
+ [DRY-RUN] Would run: npm publish --access public
190
+ [DRY-RUN] Exiting without publishing.
191
+ ```
192
+
193
+ ### 7. Dry-run mode per registry
194
+
195
+ ```bash
196
+ # npm dry-run
197
+ npm publish --access public --dry-run
198
+
199
+ # crates.io dry-run (cargo does not have a publish dry-run; use --dry-run flag for validation only)
200
+ cargo package --list 2>/dev/null
201
+
202
+ # PyPI dry-run
203
+ python -m twine upload --repository testpypi dist/* # test.pypi.org
204
+ ```
205
+
206
+ ## Options
207
+
208
+ | Flag | Description |
209
+ |------|-------------|
210
+ | `--dry-run` | Verify prerequisites and show publish command without executing |
211
+ | `--registry <type>` | Force registry type (skip auto-detection) |
212
+ | `--otp <code>` | One-time password for npm 2FA |
213
+ | `--no-verify` | Skip prerequisite checks (use with caution) |
214
+
215
+ ## Examples
216
+
217
+ ### Publish an npm package
218
+
219
+ ```bash
220
+ # Verify first
221
+ publish-package --dry-run
222
+
223
+ # Publish
224
+ publish-package
225
+
226
+ # Output:
227
+ # [npm] Publishing my-package v0.4.0...
228
+ # OK: npm publish confirmed (my-package@0.4.0 on registry)
229
+ ```
230
+
231
+ ### Publish a Rust crate
232
+
233
+ ```bash
234
+ export CARGO_REGISTRY_TOKEN=<token>
235
+ publish-package --dry-run
236
+ publish-package
237
+ ```
238
+
239
+ ### Missing token scenario
240
+
241
+ ```bash
242
+ $ publish-package
243
+ FAIL: NPM_TOKEN not set. Set via: export NPM_TOKEN=<token> or add to .npmrc
244
+ ```
245
+
246
+ ## Integration with release-branch
247
+
248
+ When wired into `release-branch`, add a step after git push:
249
+
250
+ ```
251
+ 6a. Run publish-package to publish to package registries
252
+ → verify: publish-package --dry-run && publish-package
253
+ ```
254
+
255
+ ## Verify
256
+
257
+ → verify: `test -f publish-package/SKILL.md && echo "OK: skill file exists" || echo "FAIL: no skill file"`
258
+ → verify: `grep -q "name: publish-package" publish-package/SKILL.md && echo "OK: frontmatter" || echo "FAIL: frontmatter"`
259
+ → verify: `grep -ci "npm\|crates.io\|pypi\|publish\|registry" publish-package/SKILL.md | awk '{if($1>=4) print "OK: semantics"; else print "FAIL: missing"}'`
260
+ → verify: `grep -q "publish-package" SKILL-INDEX.md && echo "OK: in SKILL-INDEX" || echo "FAIL: not indexed"`
@@ -107,6 +107,52 @@ gh pr merge --squash --delete-branch
107
107
  mv specs/epics/eNN-slug specs/epics/archive/
108
108
  ```
109
109
 
110
+ ### 7b. CI verification (solo-local and team-pr)
111
+
112
+ > **HARD GATE** — Do NOT declare success until CI completes. A push that fails CI is a regression, not a release.
113
+
114
+ After push (solo-local step 5 or team-pr step 7), verify the CI workflow completes successfully:
115
+
116
+ ```bash
117
+ echo "==> Polling CI for main branch..."
118
+ TIMEOUT=600 # 10 minutes
119
+ INTERVAL=30 # poll every 30 seconds
120
+ ELAPSED=0
121
+
122
+ while [ $ELAPSED -lt $TIMEOUT ]; do
123
+ CI_JSON=$(gh run list --limit 1 --branch main --workflow CI --json status,conclusion,headSha,databaseId 2>/dev/null)
124
+ CI_STATUS=$(echo "$CI_JSON" | jq -r '.[0].status // "unknown"')
125
+ CI_CONCLUSION=$(echo "$CI_JSON" | jq -r '.[0].conclusion // ""')
126
+ CI_SHA=$(echo "$CI_JSON" | jq -r '.[0].headSha // ""')
127
+ CI_ID=$(echo "$CI_JSON" | jq -r '.[0].databaseId // ""')
128
+
129
+ if [ "$CI_STATUS" = "completed" ] && [ "$CI_CONCLUSION" = "success" ]; then
130
+ echo "OK: CI passed for $(git rev-parse --short HEAD)"
131
+ bp-yaml-set.sh specs/state.yaml release.ci_verified true 2>/dev/null || \
132
+ echo " (bp-yaml-set not available — manually set release.ci_verified: true in state.yaml)"
133
+ break
134
+ fi
135
+
136
+ if [ "$CI_STATUS" = "completed" ] && [ "$CI_CONCLUSION" = "failure" ]; then
137
+ echo "FAIL: CI failed for $(git rev-parse --short HEAD)"
138
+ echo " Run URL: https://github.com/$(gh repo view --json nameWithOwner -q .nameWithOwner)/actions/runs/$CI_ID"
139
+ echo " Handoff to fix-bug with the failure URL above."
140
+ return 1
141
+ fi
142
+
143
+ sleep $INTERVAL
144
+ ELAPSED=$((ELAPSED + INTERVAL))
145
+ echo " Waiting... (${ELAPSED}s / ${TIMEOUT}s)"
146
+ done
147
+
148
+ echo "FAIL: CI did not complete within ${TIMEOUT}s timeout"
149
+ return 1
150
+ ```
151
+
152
+ - [ ] CI workflow passes after push
153
+ - [ ] `release.ci_verified: true` documented in state.yaml
154
+ - On failure: `handoff.next_skill = fix-bug` with the CI failure URL
155
+
110
156
  ### 8. Clean up worktree
111
157
 
112
158
  ```bash
@@ -69,6 +69,66 @@ After all tests pass: extract duplication, deepen modules, apply SOLID principle
69
69
 
70
70
  After every behavior cycle, run the verify command from the active epic task. Show evidence before declaring the step done.
71
71
 
72
+ ### 6a. CI dry-run sub-step (when modifying workflows)
73
+
74
+ If this cycle modified files in `.github/workflows/`, run a CI dry-run before pushing:
75
+
76
+ ```bash
77
+ # 1. Check for workflow file changes
78
+ CHANGED_WORKFLOWS=$(git diff --name-only HEAD | grep '\.github/workflows/' || true)
79
+ if [ -n "$CHANGED_WORKFLOWS" ]; then
80
+ echo "==> CI dry-run: workflow files changed"
81
+ echo " $CHANGED_WORKFLOWS"
82
+
83
+ # 2. Validate YAML syntax
84
+ if command -v yamllint &>/dev/null; then
85
+ for f in $CHANGED_WORKFLOWS; do
86
+ yamllint "$f" && echo " OK: $f passes YAML lint" || echo " WARN: $f has YAML issues"
87
+ done
88
+ else
89
+ # Fallback: Python YAML parse
90
+ for f in $CHANGED_WORKFLOWS; do
91
+ python3 -c "import yaml; yaml.safe_load(open('$f'))" 2>/dev/null && \
92
+ echo " OK: $f YAML syntax valid" || \
93
+ echo " FAIL: $f has YAML syntax errors"
94
+ done
95
+ fi
96
+
97
+ # 3. Run actionlint if available
98
+ if command -v actionlint &>/dev/null; then
99
+ for f in $CHANGED_WORKFLOWS; do
100
+ actionlint "$f" && echo " OK: $f passes actionlint" || echo " WARN: $f has actionlint issues"
101
+ done
102
+ fi
103
+
104
+ # 4. Check common pitfalls
105
+ for f in $CHANGED_WORKFLOWS; do
106
+ # Missing permissions block
107
+ if ! grep -q 'permissions:' "$f"; then
108
+ echo " WARNING: $f missing permissions block — add one for security"
109
+ fi
110
+ # npm publish without NPM_TOKEN
111
+ if grep -q 'npm publish\|npx semantic-release' "$f" && ! grep -q 'NPM_TOKEN' "$f"; then
112
+ echo " WARNING: $f has npm publish/semantic-release but no NPM_TOKEN in secrets"
113
+ fi
114
+ # Hardcoded Node versions
115
+ if grep -q 'node-version: [0-9]' "$f"; then
116
+ echo " NOTE: $f has hardcoded Node version — consider node-version-file: .nvmrc"
117
+ fi
118
+ done
119
+
120
+ # 5. Suggest local dry-run
121
+ if command -v act &>/dev/null; then
122
+ echo " SUGGESTION: Run 'act push --dry-run' to test workflows locally"
123
+ fi
124
+ fi
125
+ ```
126
+
127
+ Checklist:
128
+ - [ ] YAML syntax validated for all changed workflow files
129
+ - [ ] No missing permissions, secrets, or hardcoded versions flagged
130
+ - [ ] Local dry-run suggested if `act` is available
131
+
72
132
  ### 7. Manual Verification Handover
73
133
 
74
134
  Once all tests pass: locate the Verification Script in the active epic capsule, present it to the user step-by-step, and wait for confirmation of behavioral correctness.
@@ -0,0 +1,262 @@
1
+ ---
2
+ name: publish-package
3
+ description: "\"Package registry publishing for npm, crates.io, PyPI, and Homebrew. Verifies prerequisites, runs the publish command, confirms success, and surfaces actionable error hints on failure.\""
4
+ model: sonnet
5
+ ---
6
+
7
+
8
+ # Publish Package
9
+
10
+ > **HARD GATE** — Do not attempt to publish without verifying prerequisites. Missing auth tokens, stale builds, or duplicate versions cause CI failures that are hard to debug post-push.
11
+ >
12
+ > **HARD GATE** — Always run `--dry-run` first. Package registries are append-only — a bad publish cannot be fully undone on most registries.
13
+
14
+ Publish packages to language-specific registries. Detects package type from manifest files, verifies publish prerequisites, runs the registry-specific publish command, and confirms the version appears on the registry.
15
+
16
+ ## Process
17
+
18
+ ### 1. Detect package type
19
+
20
+ Read the project root for manifest files to determine the package type:
21
+
22
+ | Manifest | Registry | Publish command |
23
+ |----------|----------|----------------|
24
+ | `package.json` | npm | `npm publish --access public` |
25
+ | `Cargo.toml` | crates.io | `cargo publish` |
26
+ | `setup.py` / `pyproject.toml` | PyPI | `twine upload dist/*` or `flit publish` |
27
+ | `Formula/<name>.rb` | Homebrew | `brew bump-formula-pr` |
28
+ | Multiple detected | Polyglot | Error: specify registry with `--registry <npm|crates.io|pypi|brew>` |
29
+
30
+ If no manifest is found, prompt the user to specify the type or pass `--type <npm|crates.io|pypi|brew>`.
31
+
32
+ ### 2. Verify prerequisites
33
+
34
+ Before attempting any publish, run all applicable checks:
35
+
36
+ **npm (`package.json`):**
37
+ ```bash
38
+ # Check auth token exists
39
+ if [ -z "${NPM_TOKEN:-}" ]; then
40
+ if [ ! -f ~/.npmrc ] || ! grep -q "_authToken" ~/.npmrc; then
41
+ echo "FAIL: NPM_TOKEN not set. Set via: export NPM_TOKEN=<token> or add //registry.npmjs.org/:_authToken=<token> to .npmrc"
42
+ exit 1
43
+ fi
44
+ fi
45
+
46
+ # Check version not already published
47
+ PACKAGE_NAME=$(node -p "require('./package.json').name")
48
+ CURRENT_VER=$(node -p "require('./package.json').version")
49
+ if npm view "$PACKAGE_NAME@$CURRENT_VER" version 2>/dev/null; then
50
+ echo "FAIL: Version $CURRENT_VER already published for $PACKAGE_NAME. Bump version first."
51
+ exit 1
52
+ fi
53
+
54
+ # Check build artifacts are fresh
55
+ if [ -d dist ] || [ -d lib ]; then
56
+ LATEST_BUILD=$(find dist lib 2>/dev/null -name "*.js" -o -name "*.cjs" -o -name "*.mjs" | xargs ls -t 2>/dev/null | head -1)
57
+ PACKAGE_MODIFIED=$(stat -f %m package.json 2>/dev/null || stat -c %Y package.json 2>/dev/null)
58
+ if [ -n "$LATEST_BUILD" ] && [ -n "$PACKAGE_MODIFIED" ]; then
59
+ BUILD_TIME=$(stat -f %m "$LATEST_BUILD" 2>/dev/null || stat -c %Y "$LATEST_BUILD" 2>/dev/null)
60
+ if [ "$BUILD_TIME" -lt "$PACKAGE_MODIFIED" ]; then
61
+ echo "WARNING: Build artifacts may be stale (package.json modified after last build). Run npm run build first."
62
+ fi
63
+ fi
64
+ fi
65
+
66
+ # Check CHANGELOG is updated
67
+ if [ -f CHANGELOG.md ]; then
68
+ if ! grep -q "$CURRENT_VER" CHANGELOG.md 2>/dev/null; then
69
+ echo "WARNING: Version $CURRENT_VER not found in CHANGELOG.md. Update changelog before publish."
70
+ fi
71
+ fi
72
+ ```
73
+
74
+ **crates.io (`Cargo.toml`):**
75
+ ```bash
76
+ # Check auth token exists
77
+ if [ -z "${CARGO_REGISTRY_TOKEN:-}" ]; then
78
+ if [ ! -f ~/.cargo/config.toml ] || ! grep -q "token" ~/.cargo/config.toml; then
79
+ echo "FAIL: CARGO_REGISTRY_TOKEN not set. Set via: export CARGO_REGISTRY_TOKEN=<token> or add to ~/.cargo/config.toml"
80
+ exit 1
81
+ fi
82
+ fi
83
+
84
+ # Check version not already published
85
+ CRATE_NAME=$(grep '^name' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')
86
+ CURRENT_VER=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')
87
+ if cargo search "$CRATE_NAME" 2>/dev/null | grep -q "^${CRATE_NAME}.*\"$CURRENT_VER\""; then
88
+ echo "FAIL: Version $CURRENT_VER already published for $CRATE_NAME. Bump version in Cargo.toml first."
89
+ exit 1
90
+ fi
91
+ ```
92
+
93
+ **PyPI (`setup.py` / `pyproject.toml`):**
94
+ ```bash
95
+ # Check auth token exists
96
+ if [ -z "${TWINE_PASSWORD:-}" ] && [ -z "${POETRY_PYPI_TOKEN_PYPI:-}" ]; then
97
+ if [ ! -f ~/.pypirc ]; then
98
+ echo "FAIL: PyPI token not configured. Set TWINE_PASSWORD or create ~/.pypirc"
99
+ exit 1
100
+ fi
101
+ fi
102
+
103
+ # Check for build artifacts
104
+ if [ ! -d dist ] || [ -z "$(ls dist/*.whl 2>/dev/null)" ]; then
105
+ echo "WARNING: No .whl files found in dist/. Run: python -m build"
106
+ fi
107
+ ```
108
+
109
+ ### 3. Run publish
110
+
111
+ After all prerequisite checks pass, run the registry-specific command:
112
+
113
+ ```bash
114
+ # npm
115
+ npm publish --access public
116
+
117
+ # crates.io
118
+ cargo publish
119
+
120
+ # PyPI
121
+ python -m twine upload dist/* # or: poetry publish
122
+
123
+ # Homebrew (opens PR, does not publish directly)
124
+ brew bump-formula-pr --url=<tarball-url> <formula-name>
125
+ ```
126
+
127
+ ### 4. Verify publish success
128
+
129
+ After publish, confirm the version appears on the registry:
130
+
131
+ ```bash
132
+ # npm
133
+ npm view "$PACKAGE_NAME" versions --json 2>/dev/null | grep -q "\"$CURRENT_VER\"" && echo "OK: npm publish confirmed"
134
+
135
+ # crates.io
136
+ cargo search "$CRATE_NAME" 2>/dev/null | grep -q "^${CRATE_NAME}.*\"$CURRENT_VER\"" && echo "OK: crates.io publish confirmed"
137
+
138
+ # PyPI
139
+ pip index versions "$PACKAGE_NAME" 2>/dev/null | grep -q "$CURRENT_VER" && echo "OK: PyPI publish confirmed"
140
+ ```
141
+
142
+ ### 5. Error handling
143
+
144
+ On failure, surface actionable hints:
145
+
146
+ ```bash
147
+ # Generic failure handler
148
+ if [ $? -ne 0 ]; then
149
+ case "$REGISTRY" in
150
+ npm)
151
+ echo "FAIL: npm publish failed."
152
+ echo " Common causes:"
153
+ echo " - NPM_TOKEN not set in secrets: add to GitHub repo secrets"
154
+ echo " - Version already published: bump version in package.json"
155
+ echo " - Two-factor auth required: use --otp=<code> flag"
156
+ echo " - Package scoped but not public: add --access public"
157
+ ;;
158
+ crates.io)
159
+ echo "FAIL: cargo publish failed."
160
+ echo " Common causes:"
161
+ echo " - CARGO_REGISTRY_TOKEN not configured: see ~/.cargo/config.toml"
162
+ echo " - Version already published: bump version in Cargo.toml"
163
+ echo " - Local changes not committed: cargo publish requires clean working tree"
164
+ ;;
165
+ pypi)
166
+ echo "FAIL: PyPI publish failed."
167
+ echo " Common causes:"
168
+ echo " - TWINE_PASSWORD not configured: set env var or ~/.pypirc"
169
+ echo " - Build artifacts missing: run python -m build first"
170
+ echo " - Version conflict: version already exists on PyPI"
171
+ ;;
172
+ esac
173
+ exit 1
174
+ fi
175
+ ```
176
+
177
+ ### 6. Dry-run mode (`--dry-run`)
178
+
179
+ Run `--dry-run` to verify all prerequisites without actually publishing:
180
+
181
+ ```bash
182
+ # Example output
183
+ $ publish-package --dry-run
184
+
185
+ [DRY-RUN] Detected package type: npm
186
+ [DRY-RUN] Package: my-package v0.4.0
187
+ [DRY-RUN] Checking NPM_TOKEN... OK
188
+ [DRY-RUN] Checking version 0.4.0 not already published... OK
189
+ [DRY-RUN] Checking build artifacts... WARNING: package.json modified after build
190
+ [DRY-RUN] Checking CHANGELOG... OK
191
+ [DRY-RUN] Would run: npm publish --access public
192
+ [DRY-RUN] Exiting without publishing.
193
+ ```
194
+
195
+ ### 7. Dry-run mode per registry
196
+
197
+ ```bash
198
+ # npm dry-run
199
+ npm publish --access public --dry-run
200
+
201
+ # crates.io dry-run (cargo does not have a publish dry-run; use --dry-run flag for validation only)
202
+ cargo package --list 2>/dev/null
203
+
204
+ # PyPI dry-run
205
+ python -m twine upload --repository testpypi dist/* # test.pypi.org
206
+ ```
207
+
208
+ ## Options
209
+
210
+ | Flag | Description |
211
+ |------|-------------|
212
+ | `--dry-run` | Verify prerequisites and show publish command without executing |
213
+ | `--registry <type>` | Force registry type (skip auto-detection) |
214
+ | `--otp <code>` | One-time password for npm 2FA |
215
+ | `--no-verify` | Skip prerequisite checks (use with caution) |
216
+
217
+ ## Examples
218
+
219
+ ### Publish an npm package
220
+
221
+ ```bash
222
+ # Verify first
223
+ publish-package --dry-run
224
+
225
+ # Publish
226
+ publish-package
227
+
228
+ # Output:
229
+ # [npm] Publishing my-package v0.4.0...
230
+ # OK: npm publish confirmed (my-package@0.4.0 on registry)
231
+ ```
232
+
233
+ ### Publish a Rust crate
234
+
235
+ ```bash
236
+ export CARGO_REGISTRY_TOKEN=<token>
237
+ publish-package --dry-run
238
+ publish-package
239
+ ```
240
+
241
+ ### Missing token scenario
242
+
243
+ ```bash
244
+ $ publish-package
245
+ FAIL: NPM_TOKEN not set. Set via: export NPM_TOKEN=<token> or add to .npmrc
246
+ ```
247
+
248
+ ## Integration with release-branch
249
+
250
+ When wired into `release-branch`, add a step after git push:
251
+
252
+ ```
253
+ 6a. Run publish-package to publish to package registries
254
+ → verify: publish-package --dry-run && publish-package
255
+ ```
256
+
257
+ ## Verify
258
+
259
+ → verify: `test -f publish-package/SKILL.md && echo "OK: skill file exists" || echo "FAIL: no skill file"`
260
+ → verify: `grep -q "name: publish-package" publish-package/SKILL.md && echo "OK: frontmatter" || echo "FAIL: frontmatter"`
261
+ → verify: `grep -ci "npm\|crates.io\|pypi\|publish\|registry" publish-package/SKILL.md | awk '{if($1>=4) print "OK: semantics"; else print "FAIL: missing"}'`
262
+ → verify: `grep -q "publish-package" SKILL-INDEX.md && echo "OK: in SKILL-INDEX" || echo "FAIL: not indexed"`
@@ -109,6 +109,52 @@ gh pr merge --squash --delete-branch
109
109
  mv specs/epics/eNN-slug specs/epics/archive/
110
110
  ```
111
111
 
112
+ ### 7b. CI verification (solo-local and team-pr)
113
+
114
+ > **HARD GATE** — Do NOT declare success until CI completes. A push that fails CI is a regression, not a release.
115
+
116
+ After push (solo-local step 5 or team-pr step 7), verify the CI workflow completes successfully:
117
+
118
+ ```bash
119
+ echo "==> Polling CI for main branch..."
120
+ TIMEOUT=600 # 10 minutes
121
+ INTERVAL=30 # poll every 30 seconds
122
+ ELAPSED=0
123
+
124
+ while [ $ELAPSED -lt $TIMEOUT ]; do
125
+ CI_JSON=$(gh run list --limit 1 --branch main --workflow CI --json status,conclusion,headSha,databaseId 2>/dev/null)
126
+ CI_STATUS=$(echo "$CI_JSON" | jq -r '.[0].status // "unknown"')
127
+ CI_CONCLUSION=$(echo "$CI_JSON" | jq -r '.[0].conclusion // ""')
128
+ CI_SHA=$(echo "$CI_JSON" | jq -r '.[0].headSha // ""')
129
+ CI_ID=$(echo "$CI_JSON" | jq -r '.[0].databaseId // ""')
130
+
131
+ if [ "$CI_STATUS" = "completed" ] && [ "$CI_CONCLUSION" = "success" ]; then
132
+ echo "OK: CI passed for $(git rev-parse --short HEAD)"
133
+ bp-yaml-set.sh specs/state.yaml release.ci_verified true 2>/dev/null || \
134
+ echo " (bp-yaml-set not available — manually set release.ci_verified: true in state.yaml)"
135
+ break
136
+ fi
137
+
138
+ if [ "$CI_STATUS" = "completed" ] && [ "$CI_CONCLUSION" = "failure" ]; then
139
+ echo "FAIL: CI failed for $(git rev-parse --short HEAD)"
140
+ echo " Run URL: https://github.com/$(gh repo view --json nameWithOwner -q .nameWithOwner)/actions/runs/$CI_ID"
141
+ echo " Handoff to fix-bug with the failure URL above."
142
+ return 1
143
+ fi
144
+
145
+ sleep $INTERVAL
146
+ ELAPSED=$((ELAPSED + INTERVAL))
147
+ echo " Waiting... (${ELAPSED}s / ${TIMEOUT}s)"
148
+ done
149
+
150
+ echo "FAIL: CI did not complete within ${TIMEOUT}s timeout"
151
+ return 1
152
+ ```
153
+
154
+ - [ ] CI workflow passes after push
155
+ - [ ] `release.ci_verified: true` documented in state.yaml
156
+ - On failure: `handoff.next_skill = fix-bug` with the CI failure URL
157
+
112
158
  ### 8. Clean up worktree
113
159
 
114
160
  ```bash
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [2.10.0](https://github.com/danielvm-git/bigpowers/compare/v2.9.0...v2.10.0) (2026-06-20)
2
+
3
+
4
+ ### Features
5
+
6
+ * **skills:** add CI verify and dry-run to skills ([e751564](https://github.com/danielvm-git/bigpowers/commit/e75156478b7c23f4e32ed78eec644916f14dd3c4))
7
+
8
+ # [2.9.0](https://github.com/danielvm-git/bigpowers/compare/v2.8.0...v2.9.0) (2026-06-20)
9
+
10
+
11
+ ### Features
12
+
13
+ * **skills:** add publish-package skill for multi-registry publishing ([945a481](https://github.com/danielvm-git/bigpowers/commit/945a481cef07335c59277c82137e4a6bec78655a))
14
+
1
15
  # [2.8.0](https://github.com/danielvm-git/bigpowers/compare/v2.7.5...v2.8.0) (2026-06-20)
2
16
 
3
17
 
package/SKILL-INDEX.md CHANGED
@@ -3,8 +3,8 @@
3
3
  > **DO NOT EDIT** — This file is auto-generated by `scripts/generate-skill-index.sh`.
4
4
  > Edit `SKILL.md` source files or `skills-lock.json` instead. Run `bash scripts/sync-skills.sh` to regenerate.
5
5
 
6
- **Generated:** 2026-06-20T21:22:23Z
7
- **Skills:** 63
6
+ **Generated:** 2026-06-20T21:31:11Z
7
+ **Skills:** 64
8
8
 
9
9
  ---
10
10
 
@@ -15,11 +15,11 @@
15
15
  | Discover | 6 | `elaborate-spec, map-codebase, research-first, search-skills, survey-context, using-bigpowers` |
16
16
  | Design | 7 | `deepen-architecture, define-language, define-success, design-interface, grill-me, grill-with-docs, model-domain` |
17
17
  | Plan | 9 | `assess-impact, change-request, plan-refactor, plan-release, plan-work, run-planning, scope-work, seed-conventions, slice-tasks` |
18
- | Build | 13 | `align-grid, build-epic, craft-skill, develop-tdd, execute-plan, guard-git, hook-commits, kickoff-branch, orchestrate-project, setup-environment, spike-prototype, wire-ci, wire-observability` |
18
+ | Build | 14 | `align-grid, build-epic, craft-skill, develop-tdd, execute-plan, guard-git, hook-commits, kickoff-branch, orchestrate-project, publish-package, setup-environment, spike-prototype, wire-ci, wire-observability` |
19
19
  | Verify | 12 | `audit-code, diagnose-root, enforce-first, fix-bug, inspect-quality, investigate-bug, request-review, respond-review, run-evals, trace-requirement, validate-fix, verify-work` |
20
20
  | Release | 2 | `commit-message, release-branch` |
21
21
  | Sustain | 13 | `compose-workflow, delegate-task, dispatch-agents, edit-document, evolve-skill, migrate-spec, organize-workspace, reset-baseline, session-state, simulate-agents, stocktake-skills, terse-mode, write-document` |
22
- | **TOTAL** | **62** | |
22
+ | **TOTAL** | **63** | |
23
23
 
24
24
  ---
25
25
 
@@ -58,39 +58,40 @@
58
58
  | 29 | Build | `hook-commits` | Set up pre-commit hooks with lint-staged (Prettier), type checking, and tests in | ✅ Active |
59
59
  | 30 | Build | `kickoff-branch` | Create a git worktree and feature branch, then verify a clean test baseline befo | ✅ Active |
60
60
  | 31 | Build | `orchestrate-project` | Meta-skill that enforces the 6-phase core loop (discover → elaborate → plan | ✅ Active |
61
- | 32 | Build | `setup-environment` | Pre-install dependencies and configure tools before development work begins. Use | ✅ Active |
62
- | 33 | Build | `spike-prototype` | Throw-away prototype for unknown problem spaces. Output is learning notes in spe | ✅ Active |
63
- | 34 | Build | `wire-ci` | "CI pipeline setup with pre-built templates and local validation. Generates GitH | ✅ Active |
64
- | 35 | Build | `wire-observability` | Add structured JSON logging, observability commands, and idempotent setup script | ✅ Active |
65
- | 36 | Verify | `audit-code` | Self-review checklist for the coding agent to run before dispatching a reviewer. | ✅ Active |
66
- | 37 | Verify | `diagnose-root` | Run 4-phase root cause analysis reproduce, isolate, hypothesize, verify. Use | ✅ Active |
67
- | 38 | Verify | `enforce-first` | Apply the F.I.R.S.T test quality rubric (Fast, Independent, Repeatable, Self-Val | ✅ Active |
68
- | 39 | Verify | `fix-bug` | Bug fix orchestrator active_flow fix_bug; reads specs/bugs/BUG-*.md; chains | ✅ Active |
69
- | 40 | Verify | `inspect-quality` | Interactive QA session where user reports bugs or issues conversationally, and t | ✅ Active |
70
- | 41 | Verify | `investigate-bug` | Investigate a bug or issue by exploring the codebase to find root cause, then wr | ✅ Active |
71
- | 42 | Verify | `request-review` | Dispatch a fresh reviewer agent with a clean context to critique the code after | ✅ Active |
72
- | 43 | Verify | `respond-review` | Act on a reviewer agent's feedback systematically categorize findings, apply | ✅ Active |
73
- | 44 | Verify | `run-evals` | Eval-Driven Development define capability and regression evals before buildi | ✅ Active |
74
- | 45 | Verify | `trace-requirement` | Link story IDs from specs/release-plan.yaml + epic capsule directories to the im | ✅ Active |
75
- | 46 | Verify | `validate-fix` | Prove a fix works before declaring done re-run the failing test, run the ful | ✅ Active |
76
- | 47 | Verify | `verify-work` | Multi-phase UAT gatecold-start smoke, build, typecheck, lint, tests, step-b | ✅ Active |
77
- | 48 | Release | `commit-message` | Reviews working-tree changes, then drafts a Conventional Commits title/body and | ✅ Active |
78
- | 49 | Release | `release-branch` | Make the merge/PR/keep/discard decision for a feature branch, verify coverage ga | ✅ Active |
79
- | 50 | Sustain | `compose-workflow` | Chain multiple bigpowers skills into a custom workflow recipe saved in specs/. U | ✅ Active |
80
- | 51 | Sustain | `delegate-task` | Delegate one complex task to a single subagent, review its work in two stages be | ✅ Active |
81
- | 52 | Sustain | `dispatch-agents` | Dispatch multiple subagents in parallel on independent tasks. No waiting between | ✅ Active |
82
- | 53 | Sustain | `edit-document` | Edit and improve documents by restructuring sections, improving clarity, and tig | ✅ Active |
83
- | 54 | Sustain | `evolve-skill` | Benchmark-gated skill evolution consume bigpowers-benchmark report, propose | ✅ Active |
84
- | 55 | Sustain | `migrate-spec` | Detect GSD, spec-kit, or BMAD spec artifacts and transform them into bigpowers Y | ✅ Active |
85
- | 56 | Sustain | `organize-workspace` | Scans the active workspace for disposable artifacts—logs, caches, stale build | ✅ Active |
86
- | 57 | Sustain | `reset-baseline` | Restore the project to a known clean state between agent runs or experiments. Us | ✅ Active |
87
- | 58 | Sustain | `session-state` | Track implementation decisions and progress in specs/state.yaml to prevent conte | ✅ Active |
88
- | 59 | Sustain | `simulate-agents` | Run Mock User and Auditor agents against a feature in fresh contexts before huma | ✅ Active |
89
- | 60 | Sustain | `stocktake-skills` | Sequential subagent batch audit of the bigpowers skill catalog Quick Scan (c | ✅ Active |
90
- | 61 | Sustain | `terse-mode` | Fallback ultra-compressed communication mode. Cuts token usage ~75% by dropping | ✅ Active |
91
- | 62 | Sustain | `write-document` | Write, organize, and sync high-integrity technical documents using the BMAD meth | ✅ Active |
92
-
93
- **Total: 62 active skills.**
61
+ | 32 | Build | `publish-package` | "Package registry publishing for npm, crates.io, PyPI, and Homebrew. Verifies pr | ✅ Active |
62
+ | 33 | Build | `setup-environment` | Pre-install dependencies and configure tools before development work begins. Use | ✅ Active |
63
+ | 34 | Build | `spike-prototype` | Throw-away prototype for unknown problem spaces. Output is learning notes in spe | ✅ Active |
64
+ | 35 | Build | `wire-ci` | "CI pipeline setup with pre-built templates and local validation. Generates GitH | ✅ Active |
65
+ | 36 | Build | `wire-observability` | Add structured JSON logging, observability commands, and idempotent setup script | ✅ Active |
66
+ | 37 | Verify | `audit-code` | Self-review checklist for the coding agent to run before dispatching a reviewer. | ✅ Active |
67
+ | 38 | Verify | `diagnose-root` | Run 4-phase root cause analysis reproduce, isolate, hypothesize, verify. Use | ✅ Active |
68
+ | 39 | Verify | `enforce-first` | Apply the F.I.R.S.T test quality rubric (Fast, Independent, Repeatable, Self-Val | ✅ Active |
69
+ | 40 | Verify | `fix-bug` | Bug fix orchestrator active_flow fix_bug; reads specs/bugs/BUG-*.md; chains | ✅ Active |
70
+ | 41 | Verify | `inspect-quality` | Interactive QA session where user reports bugs or issues conversationally, and t | ✅ Active |
71
+ | 42 | Verify | `investigate-bug` | Investigate a bug or issue by exploring the codebase to find root cause, then wr | ✅ Active |
72
+ | 43 | Verify | `request-review` | Dispatch a fresh reviewer agent with a clean context to critique the code after | ✅ Active |
73
+ | 44 | Verify | `respond-review` | Act on a reviewer agent's feedback systematically categorize findings, apply | ✅ Active |
74
+ | 45 | Verify | `run-evals` | Eval-Driven Development define capability and regression evals before buildi | ✅ Active |
75
+ | 46 | Verify | `trace-requirement` | Link story IDs from specs/release-plan.yaml + epic capsule directories to the im | ✅ Active |
76
+ | 47 | Verify | `validate-fix` | Prove a fix works before declaring done re-run the failing test, run the ful | ✅ Active |
77
+ | 48 | Verify | `verify-work` | Multi-phase UAT gate — cold-start smoke, build, typecheck, lint, tests, step-b | ✅ Active |
78
+ | 49 | Release | `commit-message` | Reviews working-tree changes, then drafts a Conventional Commits title/body and | ✅ Active |
79
+ | 50 | Release | `release-branch` | Make the merge/PR/keep/discard decision for a feature branch, verify coverage ga | ✅ Active |
80
+ | 51 | Sustain | `compose-workflow` | Chain multiple bigpowers skills into a custom workflow recipe saved in specs/. U | ✅ Active |
81
+ | 52 | Sustain | `delegate-task` | Delegate one complex task to a single subagent, review its work in two stages be | ✅ Active |
82
+ | 53 | Sustain | `dispatch-agents` | Dispatch multiple subagents in parallel on independent tasks. No waiting between | ✅ Active |
83
+ | 54 | Sustain | `edit-document` | Edit and improve documents by restructuring sections, improving clarity, and tig | ✅ Active |
84
+ | 55 | Sustain | `evolve-skill` | Benchmark-gated skill evolution consume bigpowers-benchmark report, propose | ✅ Active |
85
+ | 56 | Sustain | `migrate-spec` | Detect GSD, spec-kit, or BMAD spec artifacts and transform them into bigpowers Y | ✅ Active |
86
+ | 57 | Sustain | `organize-workspace` | Scans the active workspace for disposable artifacts—logs, caches, stale build | ✅ Active |
87
+ | 58 | Sustain | `reset-baseline` | Restore the project to a known clean state between agent runs or experiments. Us | ✅ Active |
88
+ | 59 | Sustain | `session-state` | Track implementation decisions and progress in specs/state.yaml to prevent conte | ✅ Active |
89
+ | 60 | Sustain | `simulate-agents` | Run Mock User and Auditor agents against a feature in fresh contexts before huma | ✅ Active |
90
+ | 61 | Sustain | `stocktake-skills` | Sequential subagent batch audit of the bigpowers skill catalog — Quick Scan (c | ✅ Active |
91
+ | 62 | Sustain | `terse-mode` | Fallback ultra-compressed communication mode. Cuts token usage ~75% by dropping | ✅ Active |
92
+ | 63 | Sustain | `write-document` | Write, organize, and sync high-integrity technical documents using the BMAD meth | ✅ Active |
93
+
94
+ **Total: 63 active skills.**
94
95
 
95
96
  ---
96
97
 
@@ -68,6 +68,66 @@ After all tests pass: extract duplication, deepen modules, apply SOLID principle
68
68
 
69
69
  After every behavior cycle, run the verify command from the active epic task. Show evidence before declaring the step done.
70
70
 
71
+ ### 6a. CI dry-run sub-step (when modifying workflows)
72
+
73
+ If this cycle modified files in `.github/workflows/`, run a CI dry-run before pushing:
74
+
75
+ ```bash
76
+ # 1. Check for workflow file changes
77
+ CHANGED_WORKFLOWS=$(git diff --name-only HEAD | grep '\.github/workflows/' || true)
78
+ if [ -n "$CHANGED_WORKFLOWS" ]; then
79
+ echo "==> CI dry-run: workflow files changed"
80
+ echo " $CHANGED_WORKFLOWS"
81
+
82
+ # 2. Validate YAML syntax
83
+ if command -v yamllint &>/dev/null; then
84
+ for f in $CHANGED_WORKFLOWS; do
85
+ yamllint "$f" && echo " OK: $f passes YAML lint" || echo " WARN: $f has YAML issues"
86
+ done
87
+ else
88
+ # Fallback: Python YAML parse
89
+ for f in $CHANGED_WORKFLOWS; do
90
+ python3 -c "import yaml; yaml.safe_load(open('$f'))" 2>/dev/null && \
91
+ echo " OK: $f YAML syntax valid" || \
92
+ echo " FAIL: $f has YAML syntax errors"
93
+ done
94
+ fi
95
+
96
+ # 3. Run actionlint if available
97
+ if command -v actionlint &>/dev/null; then
98
+ for f in $CHANGED_WORKFLOWS; do
99
+ actionlint "$f" && echo " OK: $f passes actionlint" || echo " WARN: $f has actionlint issues"
100
+ done
101
+ fi
102
+
103
+ # 4. Check common pitfalls
104
+ for f in $CHANGED_WORKFLOWS; do
105
+ # Missing permissions block
106
+ if ! grep -q 'permissions:' "$f"; then
107
+ echo " WARNING: $f missing permissions block — add one for security"
108
+ fi
109
+ # npm publish without NPM_TOKEN
110
+ if grep -q 'npm publish\|npx semantic-release' "$f" && ! grep -q 'NPM_TOKEN' "$f"; then
111
+ echo " WARNING: $f has npm publish/semantic-release but no NPM_TOKEN in secrets"
112
+ fi
113
+ # Hardcoded Node versions
114
+ if grep -q 'node-version: [0-9]' "$f"; then
115
+ echo " NOTE: $f has hardcoded Node version — consider node-version-file: .nvmrc"
116
+ fi
117
+ done
118
+
119
+ # 5. Suggest local dry-run
120
+ if command -v act &>/dev/null; then
121
+ echo " SUGGESTION: Run 'act push --dry-run' to test workflows locally"
122
+ fi
123
+ fi
124
+ ```
125
+
126
+ Checklist:
127
+ - [ ] YAML syntax validated for all changed workflow files
128
+ - [ ] No missing permissions, secrets, or hardcoded versions flagged
129
+ - [ ] Local dry-run suggested if `act` is available
130
+
71
131
  ### 7. Manual Verification Handover
72
132
 
73
133
  Once all tests pass: locate the Verification Script in the active epic capsule, present it to the user step-by-step, and wait for confirmation of behavioral correctness.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bigpowers",
3
- "version": "2.8.0",
3
+ "version": "2.10.0",
4
4
  "description": "61 agent skills for spec-driven, test-first software development by solo developers",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -0,0 +1,261 @@
1
+ ---
2
+ name: publish-package
3
+ description: "Package registry publishing for npm, crates.io, PyPI, and Homebrew. Verifies prerequisites, runs the publish command, confirms success, and surfaces actionable error hints on failure."
4
+ model: sonnet
5
+ ---
6
+
7
+ # Publish Package
8
+
9
+ > **HARD GATE** — Do not attempt to publish without verifying prerequisites. Missing auth tokens, stale builds, or duplicate versions cause CI failures that are hard to debug post-push.
10
+ >
11
+ > **HARD GATE** — Always run `--dry-run` first. Package registries are append-only — a bad publish cannot be fully undone on most registries.
12
+
13
+ Publish packages to language-specific registries. Detects package type from manifest files, verifies publish prerequisites, runs the registry-specific publish command, and confirms the version appears on the registry.
14
+
15
+ ## Process
16
+
17
+ ### 1. Detect package type
18
+
19
+ Read the project root for manifest files to determine the package type:
20
+
21
+ | Manifest | Registry | Publish command |
22
+ |----------|----------|----------------|
23
+ | `package.json` | npm | `npm publish --access public` |
24
+ | `Cargo.toml` | crates.io | `cargo publish` |
25
+ | `setup.py` / `pyproject.toml` | PyPI | `twine upload dist/*` or `flit publish` |
26
+ | `Formula/<name>.rb` | Homebrew | `brew bump-formula-pr` |
27
+ | Multiple detected | Polyglot | Error: specify registry with `--registry <npm|crates.io|pypi|brew>` |
28
+
29
+ If no manifest is found, prompt the user to specify the type or pass `--type <npm|crates.io|pypi|brew>`.
30
+
31
+ ### 2. Verify prerequisites
32
+
33
+ Before attempting any publish, run all applicable checks:
34
+
35
+ **npm (`package.json`):**
36
+ ```bash
37
+ # Check auth token exists
38
+ if [ -z "${NPM_TOKEN:-}" ]; then
39
+ if [ ! -f ~/.npmrc ] || ! grep -q "_authToken" ~/.npmrc; then
40
+ echo "FAIL: NPM_TOKEN not set. Set via: export NPM_TOKEN=<token> or add //registry.npmjs.org/:_authToken=<token> to .npmrc"
41
+ exit 1
42
+ fi
43
+ fi
44
+
45
+ # Check version not already published
46
+ PACKAGE_NAME=$(node -p "require('./package.json').name")
47
+ CURRENT_VER=$(node -p "require('./package.json').version")
48
+ if npm view "$PACKAGE_NAME@$CURRENT_VER" version 2>/dev/null; then
49
+ echo "FAIL: Version $CURRENT_VER already published for $PACKAGE_NAME. Bump version first."
50
+ exit 1
51
+ fi
52
+
53
+ # Check build artifacts are fresh
54
+ if [ -d dist ] || [ -d lib ]; then
55
+ LATEST_BUILD=$(find dist lib 2>/dev/null -name "*.js" -o -name "*.cjs" -o -name "*.mjs" | xargs ls -t 2>/dev/null | head -1)
56
+ PACKAGE_MODIFIED=$(stat -f %m package.json 2>/dev/null || stat -c %Y package.json 2>/dev/null)
57
+ if [ -n "$LATEST_BUILD" ] && [ -n "$PACKAGE_MODIFIED" ]; then
58
+ BUILD_TIME=$(stat -f %m "$LATEST_BUILD" 2>/dev/null || stat -c %Y "$LATEST_BUILD" 2>/dev/null)
59
+ if [ "$BUILD_TIME" -lt "$PACKAGE_MODIFIED" ]; then
60
+ echo "WARNING: Build artifacts may be stale (package.json modified after last build). Run npm run build first."
61
+ fi
62
+ fi
63
+ fi
64
+
65
+ # Check CHANGELOG is updated
66
+ if [ -f CHANGELOG.md ]; then
67
+ if ! grep -q "$CURRENT_VER" CHANGELOG.md 2>/dev/null; then
68
+ echo "WARNING: Version $CURRENT_VER not found in CHANGELOG.md. Update changelog before publish."
69
+ fi
70
+ fi
71
+ ```
72
+
73
+ **crates.io (`Cargo.toml`):**
74
+ ```bash
75
+ # Check auth token exists
76
+ if [ -z "${CARGO_REGISTRY_TOKEN:-}" ]; then
77
+ if [ ! -f ~/.cargo/config.toml ] || ! grep -q "token" ~/.cargo/config.toml; then
78
+ echo "FAIL: CARGO_REGISTRY_TOKEN not set. Set via: export CARGO_REGISTRY_TOKEN=<token> or add to ~/.cargo/config.toml"
79
+ exit 1
80
+ fi
81
+ fi
82
+
83
+ # Check version not already published
84
+ CRATE_NAME=$(grep '^name' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')
85
+ CURRENT_VER=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')
86
+ if cargo search "$CRATE_NAME" 2>/dev/null | grep -q "^${CRATE_NAME}.*\"$CURRENT_VER\""; then
87
+ echo "FAIL: Version $CURRENT_VER already published for $CRATE_NAME. Bump version in Cargo.toml first."
88
+ exit 1
89
+ fi
90
+ ```
91
+
92
+ **PyPI (`setup.py` / `pyproject.toml`):**
93
+ ```bash
94
+ # Check auth token exists
95
+ if [ -z "${TWINE_PASSWORD:-}" ] && [ -z "${POETRY_PYPI_TOKEN_PYPI:-}" ]; then
96
+ if [ ! -f ~/.pypirc ]; then
97
+ echo "FAIL: PyPI token not configured. Set TWINE_PASSWORD or create ~/.pypirc"
98
+ exit 1
99
+ fi
100
+ fi
101
+
102
+ # Check for build artifacts
103
+ if [ ! -d dist ] || [ -z "$(ls dist/*.whl 2>/dev/null)" ]; then
104
+ echo "WARNING: No .whl files found in dist/. Run: python -m build"
105
+ fi
106
+ ```
107
+
108
+ ### 3. Run publish
109
+
110
+ After all prerequisite checks pass, run the registry-specific command:
111
+
112
+ ```bash
113
+ # npm
114
+ npm publish --access public
115
+
116
+ # crates.io
117
+ cargo publish
118
+
119
+ # PyPI
120
+ python -m twine upload dist/* # or: poetry publish
121
+
122
+ # Homebrew (opens PR, does not publish directly)
123
+ brew bump-formula-pr --url=<tarball-url> <formula-name>
124
+ ```
125
+
126
+ ### 4. Verify publish success
127
+
128
+ After publish, confirm the version appears on the registry:
129
+
130
+ ```bash
131
+ # npm
132
+ npm view "$PACKAGE_NAME" versions --json 2>/dev/null | grep -q "\"$CURRENT_VER\"" && echo "OK: npm publish confirmed"
133
+
134
+ # crates.io
135
+ cargo search "$CRATE_NAME" 2>/dev/null | grep -q "^${CRATE_NAME}.*\"$CURRENT_VER\"" && echo "OK: crates.io publish confirmed"
136
+
137
+ # PyPI
138
+ pip index versions "$PACKAGE_NAME" 2>/dev/null | grep -q "$CURRENT_VER" && echo "OK: PyPI publish confirmed"
139
+ ```
140
+
141
+ ### 5. Error handling
142
+
143
+ On failure, surface actionable hints:
144
+
145
+ ```bash
146
+ # Generic failure handler
147
+ if [ $? -ne 0 ]; then
148
+ case "$REGISTRY" in
149
+ npm)
150
+ echo "FAIL: npm publish failed."
151
+ echo " Common causes:"
152
+ echo " - NPM_TOKEN not set in secrets: add to GitHub repo secrets"
153
+ echo " - Version already published: bump version in package.json"
154
+ echo " - Two-factor auth required: use --otp=<code> flag"
155
+ echo " - Package scoped but not public: add --access public"
156
+ ;;
157
+ crates.io)
158
+ echo "FAIL: cargo publish failed."
159
+ echo " Common causes:"
160
+ echo " - CARGO_REGISTRY_TOKEN not configured: see ~/.cargo/config.toml"
161
+ echo " - Version already published: bump version in Cargo.toml"
162
+ echo " - Local changes not committed: cargo publish requires clean working tree"
163
+ ;;
164
+ pypi)
165
+ echo "FAIL: PyPI publish failed."
166
+ echo " Common causes:"
167
+ echo " - TWINE_PASSWORD not configured: set env var or ~/.pypirc"
168
+ echo " - Build artifacts missing: run python -m build first"
169
+ echo " - Version conflict: version already exists on PyPI"
170
+ ;;
171
+ esac
172
+ exit 1
173
+ fi
174
+ ```
175
+
176
+ ### 6. Dry-run mode (`--dry-run`)
177
+
178
+ Run `--dry-run` to verify all prerequisites without actually publishing:
179
+
180
+ ```bash
181
+ # Example output
182
+ $ publish-package --dry-run
183
+
184
+ [DRY-RUN] Detected package type: npm
185
+ [DRY-RUN] Package: my-package v0.4.0
186
+ [DRY-RUN] Checking NPM_TOKEN... OK
187
+ [DRY-RUN] Checking version 0.4.0 not already published... OK
188
+ [DRY-RUN] Checking build artifacts... WARNING: package.json modified after build
189
+ [DRY-RUN] Checking CHANGELOG... OK
190
+ [DRY-RUN] Would run: npm publish --access public
191
+ [DRY-RUN] Exiting without publishing.
192
+ ```
193
+
194
+ ### 7. Dry-run mode per registry
195
+
196
+ ```bash
197
+ # npm dry-run
198
+ npm publish --access public --dry-run
199
+
200
+ # crates.io dry-run (cargo does not have a publish dry-run; use --dry-run flag for validation only)
201
+ cargo package --list 2>/dev/null
202
+
203
+ # PyPI dry-run
204
+ python -m twine upload --repository testpypi dist/* # test.pypi.org
205
+ ```
206
+
207
+ ## Options
208
+
209
+ | Flag | Description |
210
+ |------|-------------|
211
+ | `--dry-run` | Verify prerequisites and show publish command without executing |
212
+ | `--registry <type>` | Force registry type (skip auto-detection) |
213
+ | `--otp <code>` | One-time password for npm 2FA |
214
+ | `--no-verify` | Skip prerequisite checks (use with caution) |
215
+
216
+ ## Examples
217
+
218
+ ### Publish an npm package
219
+
220
+ ```bash
221
+ # Verify first
222
+ publish-package --dry-run
223
+
224
+ # Publish
225
+ publish-package
226
+
227
+ # Output:
228
+ # [npm] Publishing my-package v0.4.0...
229
+ # OK: npm publish confirmed (my-package@0.4.0 on registry)
230
+ ```
231
+
232
+ ### Publish a Rust crate
233
+
234
+ ```bash
235
+ export CARGO_REGISTRY_TOKEN=<token>
236
+ publish-package --dry-run
237
+ publish-package
238
+ ```
239
+
240
+ ### Missing token scenario
241
+
242
+ ```bash
243
+ $ publish-package
244
+ FAIL: NPM_TOKEN not set. Set via: export NPM_TOKEN=<token> or add to .npmrc
245
+ ```
246
+
247
+ ## Integration with release-branch
248
+
249
+ When wired into `release-branch`, add a step after git push:
250
+
251
+ ```
252
+ 6a. Run publish-package to publish to package registries
253
+ → verify: publish-package --dry-run && publish-package
254
+ ```
255
+
256
+ ## Verify
257
+
258
+ → verify: `test -f publish-package/SKILL.md && echo "OK: skill file exists" || echo "FAIL: no skill file"`
259
+ → verify: `grep -q "name: publish-package" publish-package/SKILL.md && echo "OK: frontmatter" || echo "FAIL: frontmatter"`
260
+ → verify: `grep -ci "npm\|crates.io\|pypi\|publish\|registry" publish-package/SKILL.md | awk '{if($1>=4) print "OK: semantics"; else print "FAIL: missing"}'`
261
+ → verify: `grep -q "publish-package" SKILL-INDEX.md && echo "OK: in SKILL-INDEX" || echo "FAIL: not indexed"`
@@ -108,6 +108,52 @@ gh pr merge --squash --delete-branch
108
108
  mv specs/epics/eNN-slug specs/epics/archive/
109
109
  ```
110
110
 
111
+ ### 7b. CI verification (solo-local and team-pr)
112
+
113
+ > **HARD GATE** — Do NOT declare success until CI completes. A push that fails CI is a regression, not a release.
114
+
115
+ After push (solo-local step 5 or team-pr step 7), verify the CI workflow completes successfully:
116
+
117
+ ```bash
118
+ echo "==> Polling CI for main branch..."
119
+ TIMEOUT=600 # 10 minutes
120
+ INTERVAL=30 # poll every 30 seconds
121
+ ELAPSED=0
122
+
123
+ while [ $ELAPSED -lt $TIMEOUT ]; do
124
+ CI_JSON=$(gh run list --limit 1 --branch main --workflow CI --json status,conclusion,headSha,databaseId 2>/dev/null)
125
+ CI_STATUS=$(echo "$CI_JSON" | jq -r '.[0].status // "unknown"')
126
+ CI_CONCLUSION=$(echo "$CI_JSON" | jq -r '.[0].conclusion // ""')
127
+ CI_SHA=$(echo "$CI_JSON" | jq -r '.[0].headSha // ""')
128
+ CI_ID=$(echo "$CI_JSON" | jq -r '.[0].databaseId // ""')
129
+
130
+ if [ "$CI_STATUS" = "completed" ] && [ "$CI_CONCLUSION" = "success" ]; then
131
+ echo "OK: CI passed for $(git rev-parse --short HEAD)"
132
+ bp-yaml-set.sh specs/state.yaml release.ci_verified true 2>/dev/null || \
133
+ echo " (bp-yaml-set not available — manually set release.ci_verified: true in state.yaml)"
134
+ break
135
+ fi
136
+
137
+ if [ "$CI_STATUS" = "completed" ] && [ "$CI_CONCLUSION" = "failure" ]; then
138
+ echo "FAIL: CI failed for $(git rev-parse --short HEAD)"
139
+ echo " Run URL: https://github.com/$(gh repo view --json nameWithOwner -q .nameWithOwner)/actions/runs/$CI_ID"
140
+ echo " Handoff to fix-bug with the failure URL above."
141
+ return 1
142
+ fi
143
+
144
+ sleep $INTERVAL
145
+ ELAPSED=$((ELAPSED + INTERVAL))
146
+ echo " Waiting... (${ELAPSED}s / ${TIMEOUT}s)"
147
+ done
148
+
149
+ echo "FAIL: CI did not complete within ${TIMEOUT}s timeout"
150
+ return 1
151
+ ```
152
+
153
+ - [ ] CI workflow passes after push
154
+ - [ ] `release.ci_verified: true` documented in state.yaml
155
+ - On failure: `handoff.next_skill = fix-bug` with the CI failure URL
156
+
111
157
  ### 8. Clean up worktree
112
158
 
113
159
  ```bash
@@ -51,6 +51,7 @@ PHASE_MAP=(
51
51
  [setup-environment]="Build"
52
52
  [wire-observability]="Build"
53
53
  [wire-ci]="Build"
54
+ [publish-package]="Build"
54
55
  [align-grid]="Build"
55
56
  [orchestrate-project]="Build"
56
57
  [guard-git]="Build"
package/skills-lock.json CHANGED
@@ -68,7 +68,7 @@
68
68
  },
69
69
  "develop-tdd": {
70
70
  "description": "Test-driven development with red-green-refactor loop using vertical slices. Use for features (epic tasks) or bugs (specs/bugs/BUG-*.md).",
71
- "sha256": "af45529ecb20d449",
71
+ "sha256": "4002d960b18436cd",
72
72
  "path": "develop-tdd/SKILL.md"
73
73
  },
74
74
  "diagnose-root": {
@@ -186,9 +186,14 @@
186
186
  "sha256": "12d45efb07a36e94",
187
187
  "path": "plan-work/SKILL.md"
188
188
  },
189
+ "publish-package": {
190
+ "description": "\"Package registry publishing for npm, crates.io, PyPI, and Homebrew. Verifies prerequisites, runs the publish command, confirms success, and surfaces actionable error hints on failure.\"",
191
+ "sha256": "25ead1dd4d174d54",
192
+ "path": "publish-package/SKILL.md"
193
+ },
189
194
  "release-branch": {
190
195
  "description": "Make the merge/PR/keep/discard decision for a feature branch, verify coverage gates, create the PR with gh, and clean up the worktree. Use when a feature is done and ready to ship, or when user says \"release\", \"merge\", or \"open a PR\".",
191
- "sha256": "70fc37ac4e22143d",
196
+ "sha256": "6b2df2c92230d098",
192
197
  "path": "release-branch/SKILL.md"
193
198
  },
194
199
  "request-review": {