
Run Full Release
- 89 installs
- 62 repo stars
- Updated August 3, 2026
- terrylica/cc-skills
Helps with ai & agent building tasks.
About
run-full-release is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- run-full-release
- AI & Agent Building
- AI-coding skill
Run Full Release by the numbers
- 89 all-time installs (skills.sh)
- Ranked #4,891 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/terrylica/cc-skills --skill run-full-releaseAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 89 |
|---|---|
| repo stars | ★ 62 |
| Last updated | August 3, 2026 |
| Repository | terrylica/cc-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
/mise:run-full-release
Run the current repo's mise release pipeline — or bootstrap one if it doesn't exist yet.
Self-Evolving Skill: This skill improves through use. If instructions are wrong, parameters drifted, or a workaround was needed — fix this file immediately, don't defer. Only update for real, reproducible issues.
Step 1: Detect Existing Release Tasks
mise tasks ls 2>/dev/null | grep -i releaseIf tasks exist → skip to Step 3: Execute. If tasks NOT found → continue to Step 1b.
Step 1b: Classify the Repo Before Scaffolding
Not every repo is a publishable package. Before bootstrapping a versioning/publish pipeline, decide what kind of repo this is:
ls pyproject.toml Cargo.toml package.json setup.py go.mod 2>/dev/null || echo "NO_PACKAGE_MANIFEST"- Has a package manifest (Python/Rust/Node/Go) → publishable repo. Continue to Step 2 (scaffold a version+publish pipeline).
- NO package manifest AND no existing release infra → this is a docs / config / sync repo (personal dotfiles, notes, or a multi-machine
git-synced config repo). There is nothing to version or publish. Do NOT scaffold `semantic-release`. For these repos, "release" = sync to origin:
git fetch origin
git rev-list --left-right --count origin/<branch>...HEAD # behind ahead
[ -z "$(git status --porcelain)" ] || echo "DIRTY — commit or stash first"
# Only when the user has asked to release/ship (push only on request):
git push origin <branch>
git fetch origin && [ "$(git rev-parse HEAD)" = "$(git rev-parse origin/<branch>)" ] && echo "✅ synced — release complete"If behind > 0, reconcile first (git pull --rebase or merge) before pushing. Optionally scaffold a minimal release:full = preflight (clean tree + correct branch + up-to-date) → push → verify (origin == HEAD), with NO version/publish phases — but only if the user wants repeatable sync; otherwise the one-shot push above IS the whole release.
_Evidence (2026-06-04): invoked on claude-sys, a multi-machine git-synced config/docs repo — no package manifest, no .mise.toml, no semantic-release. Correct action was a fast-forward git push origin main + HEAD==origin verify, NOT a pipeline scaffold. Audit-first (Step 2a's ecosystem check) is what surfaced this; promoting it to an explicit Step 1b gate prevents the wrong reflex of scaffolding a publish pipeline on a repo with nothing to publish._
Step 2: Bootstrap Release Workflow
This step scaffolds an individualized release pipeline for THIS repo. Every repo is different — do not copy templates verbatim. Audit first, then scaffold what fits.
2a. Audit the Repository
Run all of these to understand what this repo needs:
# Ecosystem detection
ls pyproject.toml Cargo.toml package.json setup.py 2>/dev/null
# Existing mise config
cat .mise.toml 2>/dev/null || cat mise.toml 2>/dev/null
# Existing release infra (semantic-release, Makefile, GitHub Actions)
ls .releaserc* release.config.* 2>/dev/null
ls .github/workflows/*release* 2>/dev/null
grep -i release Makefile 2>/dev/null
# Credentials already configured
grep -E 'GH_TOKEN|GITHUB_TOKEN|UV_PUBLISH_TOKEN|CARGO_REGISTRY_TOKEN|NPM_TOKEN' .mise.toml mise.toml 2>/dev/null2b. Read the Reference Implementation
Read cc-skills' own release tasks as a working example — adapt, don't copy:
ls $HOME/.claude/plugins/marketplaces/cc-skills/.mise/tasks/release/Also read: $HOME/.claude/plugins/marketplaces/cc-skills/docs/RELEASE.md
2c. Scaffold .mise/tasks/release/
Create only the tasks this repo actually needs. The 5-phase pattern is:
| Phase | Task | Purpose | Required? |
|---|---|---|---|
| 1. Preflight | preflight | Clean dir, auth, branch check | Always |
| 2. Version | version | semantic-release (or repo-specific versioning) | Always |
| 3. Publish | pypi | uv publish or custom script | If Python |
crates | cargo publish --workspace (Rust 1.90+) | If Rust | |
npm | npm publish | If Node | |
| 4. Verify | verify | Tag exists, release exists, artifacts published | Always |
| 5. Postflight | postflight | Clean git state, no unpushed, lockfile reset | Always |
Orchestrator: full task chains the phases with depends = [...].
Key rules:
- Credentials in
.mise.toml[env], not hardcoded in scripts - Tool versions in
.mise.toml[tools] - Lockfile drift reset in both
preflightandpostflight(build artifacts, not intentional changes) --dryand--statusconvenience tasks
2d. Known Issues
Stale cc-skills marketplace-path hook entries leak into `~/.claude/settings.json`: cc-skills' release:preflight hook-validation gate fails with ✗ N cc-skills marketplace-path entries found in settings.json. Cause: hooks loaded from a previous Claude Code session can persist their resolved marketplace paths into settings.json, but plugin hooks are auto-loaded from each plugin's hooks/hooks.json — settings.json should NOT contain duplicates. The fix is built in: run ./scripts/sync-hooks-to-settings.sh (idempotent — prunes only the leaked marketplace-path entries, leaves user-authored hooks alone), then re-run mise run release:full. Recurs across releases — not a one-time issue. Verified 2026-05-09 mid-release: 4 entries pruned, re-run succeeded.
`@semantic-release/git` untracked file explosion: v10.x runs git ls-files -m -o without --exclude-standard. Patch after install:
find $(npm root -g 2>/dev/null) node_modules \
-path "*/@semantic-release/git/lib/git.js" 2>/dev/null | while read f; do
grep -q 'exclude-standard' "$f" || \
sed -i '' "s/\['ls-files', '-m', '-o'\]/['ls-files', '-m', '-o', '--exclude-standard']/" "$f"
donePartial semantic-release failure (version bumped, no tag): Do NOT re-run semantic-release. Manually create tag + GitHub release, then continue with publish tasks.
Cargo workspace lockfile cascade (Rust workspaces with version.workspace = true): the perl-based version bump in prepareCmd only touches Cargo.toml, but the workspace version cascades into every member crate's entry in Cargo.lock. The @semantic-release/git plugin only stages files listed in assets, so without an explicit cargo invocation + Cargo.lock in assets, the lockfile stays at the old version. Symptoms: release:preflight fails with M Cargo.lock after a successful release:version, blocking cargo publish. Fix in .releaserc.yml:
- - "@semantic-release/exec"
- prepareCmd: |
perl -i -pe 's/^version = ".*"/version = "${nextRelease.version}"/' Cargo.toml
cargo update --workspace --offline 2>/dev/null \
|| cargo metadata --format-version=1 --offline >/dev/null 2>&1 \
|| true
- - "@semantic-release/git"
- assets:
- Cargo.toml
- Cargo.lock # ← critical: capture the cascading version bump
message: "chore(release): ${nextRelease.version} [skip ci]"--offline keeps the sync local (no registry hit). The || true fallback prevents prepareCmd from blocking the release if neither cargo command can run; if the lockfile actually needed sync, the next preflight will catch it.
mise `depends` runs in parallel (release:full deps [preflight, version]): preflight and version race. If version dirties the working tree (e.g. lockfile cascade above), preflight may report failure mid-stream while version still completes, leaving an irreversible tag + GitHub release without release:crates/verify/postflight having run. Recovery: fix the dirty state, then run the remaining phases manually (release:crates → release:verify → release:postflight) — do NOT re-run release:full, which would attempt another semantic-release pass.
`release:verify` false-positive on naive grep against crates.io API: a verify implementation of the form if curl ... | grep -q "version" matches the API's _error_ responses too, because the JSON error contains the word "version" in the detail text (e.g. "crate <name> does not have a version <X.Y.Z>"). Result: every release run reports "✓ <crate> published on crates.io" even when the actual cargo publish failed. Discovered after a multi-version, multi-day silent-failure window where every tagged version was absent from crates.io but verify reported green. Fix: match the unique success-only JSON shape "num":"<exact-version>" (or parse the JSON via Python so TOML's escape rules don't mangle the quotes inside a run = """ ... """ block). Hard-fail on miss with the actual API response logged. Reference fix:
RESPONSE=$(curl -s "https://crates.io/api/v1/crates/<crate>/${VERSION}")
PUBLISHED_NUM=$(echo "$RESPONSE" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('version',{}).get('num',''))" 2>/dev/null || echo "")
if [[ "$PUBLISHED_NUM" == "$VERSION" ]]; then
echo "✓ <crate> v${VERSION} confirmed on crates.io"
else
echo "⚠ <crate> v${VERSION} NOT on crates.io (API returned: '${PUBLISHED_NUM:-error}')"
echo " raw: $(echo "$RESPONSE" | head -c 200)"
exit 1
fi`release:full` silent-swallow of crates publish failures: any orchestrator pattern of the form mise run release:crates || { echo "non-fatal" } masks the actual cargo publish error and continues with apparent success. Combined with the verify false-positive above, this hides multi-version publish gaps for days. Crates publish failure must be a hard fail; the operator runs release:crates manually after diagnosis (token, network, registry rate-limit, etc.). Replace any ||-swallow with a plain unwrapped invocation.
Schema-drift gate parser exits at indented `)`: a parser of the form awk '... in_table { if ($0 ~ /^[[:space:]]*\)/) { in_table = 0; exit } ... }' truncates mid-table when a column's multi-line DEFAULT expression has a closing paren on its own indented line (e.g. nested multiIf(\n ...,\n ) or arithmetic group close). Late columns are silently missed and falsely flagged as drift on the live-CH side. ClickHouse formatting convention puts the actual table-end ) at column 0 — change the exit guard to ^\) (no leading whitespace) for correctness.
Parallel SSH ControlMaster race in pre-push gates: when multiple mise tasks open SSH connections in parallel (depends), the loser prints ControlSocket /Users/.../control-host:22 already exists, disabling multiplexing to stderr. If your gate script uses ssh ... 2>&1 (merging stderr into stdout), that warning leaks into the data pipe and trips identifier-shaped sanity checks with phantom "extra" entries. Fix: route SSH stderr to /dev/null (we don't need it for column extraction; real errors surface via the explicit exit-code propagation downstream), and add a defense-in-depth filter grep -E '^[a-zA-Z_][a-zA-Z0-9_]*$' so even if some other stderr path leaks in the future, only valid identifier-shaped strings flow through.
Idempotent `git push` returns non-zero: orchestrators that include a manual git push --follow-tags origin main step AFTER release:version (where semantic-release's @semantic-release/git plugin already pushed) can fail when re-pushing an already-up-to-date branch + an existing tag. Treat this as success: detect via [[ $(git rev-parse HEAD) == $(git rev-parse origin/main) ]] before pushing, and only push the latest tag if git ls-remote --tags origin "refs/tags/$LATEST_TAG" returns empty. Otherwise the wrapper exits non-zero on a release that actually shipped fine, polluting the success/failure signal.
`git stash -u` of WIP `.gitignore` re-exposes ignored dirs and fails preflight (PII hazard): when the dirty working tree includes uncommitted .gitignore changes whose purpose is to ignore untracked directories, git stash -u reverts .gitignore to its committed state — which _un-ignores_ those dirs and surfaces them as ?? untracked. preflight's clean-tree gate (which typically exempts only archive/sessions/ and node_modules/) then fails on them, and in a clinical/PII repo this momentarily exposes sensitive dirs (e.g. correspondence/, site/, cdanet/) to accidental git add -A. Root cause: an uncommitted .gitignore is _load-bearing infrastructure_, not feature WIP — stashing it inverts the ignore state. Fix: separate concerns. Commit the .gitignore hygiene (plus any tracked-companion files it references, e.g. a secret-free deploy script) as its own chore: commit so the ignore rules stay active and the tree is clean, then stash ONLY the true feature code with a pathspec'd git stash push -- <files> (NOT -u, which would re-expose the dirs). After the release, git stash pop restores the feature WIP. Verified 2026-06-02 on a clinical PII repo: a blanket git stash -u exposed three PII dirs + a machine-local TOML override; popping, committing .gitignore + the secret-free deploy script it references separately, then pathspec-stashing the feature files produced a clean preflight and a correctly-scoped release.
Step 3: Execute
# Pre-release sync
git pull origin main
# Check for unpushed commits
git log --oneline @{u}..HEAD
# (cc-skills only) Preemptively prune leaked marketplace-path hook entries.
# This recurs across sessions — see Known Issues. Running this BEFORE
# release:full saves a preflight-fail/retry round trip. No-op if clean.
[[ -x ./scripts/sync-hooks-to-settings.sh ]] && ./scripts/sync-hooks-to-settings.sh
# Route by flags
mise run release:full # default
mise run release:dry # --dry
mise run release:status # --statusIf working directory is dirty: commit related changes or stash WIP first. Caution: if the dirt includes .gitignore changes, treat them as load-bearing — commit them as a chore: rather than stashing, because git stash -u reverts .gitignore and re-exposes any dirs it was ignoring (preflight then fails on them; PII hazard — see Known Issues). Stash only true feature code with a pathspec: git stash push -- <files>. Reset lockfile drift if present:
git diff --name-only | grep -E '(uv\.lock|package-lock\.json|Cargo\.lock|bun\.lockb)$' | xargs -r git checkout --Post-Execution Reflection
After this skill completes, reflect before closing the task:
0. Locate yourself. — Find this SKILL.md's canonical path before editing. 1. What failed? — Fix the instruction that caused it. 2. What worked better than expected? — Promote to recommended practice. 3. What drifted? — Fix any script, reference, or dependency that no longer matches reality. 4. Log it. — Evolution-log entry with trigger, fix, and evidence.
Do NOT defer. The next invocation inherits whatever you leave behind.
Evolution Log
Convention: Reverse chronological order (newest on top, oldest at bottom). Prepend new entries.
---
2026-06-02: .gitignore-in-WIP Stash Trap (curve-dental v2.9.4) <!-- # SSoT-OK -->
Trigger: During a curve-dental release the working tree was dirty with mixed WIP. Choosing "stash WIP → release → restore" and running git stash -u reverted an uncommitted .gitignore whose only changes were ignore rules for local-only PII dirs (correspondence/, site/, cdanet/, .wrangler/, .mise.local.toml). Reverting .gitignore un-ignored those dirs, which then surfaced as ?? untracked — failing release:preflight's clean-tree gate AND momentarily exposing clinical PII to accidental staging.
What Changed
- Added Known Issue (Step 2d): "
git stash -uof WIP.gitignorere-exposes ignored dirs and fails preflight (PII hazard)" — root cause is that an uncommitted.gitignoreis load-bearing infrastructure, not feature WIP; fix is to commit the.gitignorehygiene (plus any tracked companion it references) as its ownchore:, then pathspec-stash only the feature code. - Strengthened Step 3 dirty-tree guidance to flag
.gitignorechanges as commit-not-stash and to prefergit stash push -- <files>over-u.
Why It Changed
The generic "stash WIP first" advice has a sharp edge in repos where .gitignore itself is part of the uncommitted change set — common when ignore rules for newly-created local-only/PII dirs haven't been committed yet. Blindly stashing inverts the ignore state. In a clinical repo this is a privacy hazard, not just a preflight annoyance.
Files Affected
SKILL.md: +1 Known Issue paragraph (Step 2d); +1 caution in Step 3 dirty-tree guidancereferences/evolution-log.md: This entry
Evidence
curve-dental v2.9.4 shipped clean after the corrected sequence: pop the over-broad stash → commit .gitignore + the secret-free scripts/publish-brief.sh it references as one chore: → git stash push -- form_audit.py chart_template.py test_schema.py (the real feature WIP) → mise run release:full (preflight green; tag + GitHub release + JSONL asset) → git stash pop. Release: <https://github.com/459ecs/curve-dental/releases/tag/v2.9.4>
---
2026-03-31: Consolidate into Single Self-Contained SKILL.md
Trigger: Skill was too big and brittle — two reference files (scaffolding-and-recovery.md at 177 lines, task-implementations.md at 173 lines) tried to cover every ecosystem with hardcoded templates. The skill was also conflating two jobs: running releases (trivial) and bootstrapping release workflows (the actual value).
What Changed
- Deleted
references/scaffolding-and-recovery.mdandreferences/task-implementations.md - Rewrote SKILL.md as a self-contained guide (98 lines, down from 55 + 350 in references)
- Restructured into 3 clear steps: Detect → Bootstrap → Execute
- Bootstrap is now a guide, not a template dump — audit the repo first, scaffold only what fits
- Known issues and recovery consolidated into compact 2d section (was 80+ lines across two files)
- Description updated to natural language "Use when..." pattern per skill-architecture guidance
Why It Changed
The reference files were not robust for diverse repos because they assumed fixed ecosystem patterns. A Python-only repo doesn't need crates.io templates. A repo with existing Makefile releases doesn't need full semantic-release scaffolding. The new approach: audit first, then scaffold what the specific repo actually needs.
---
2026-03-09: Production Learnings from opendeviationbar-py Release <!-- # SSoT-OK -->
Status: Major update — 3 new sections added from real-world release failure.
What Changed
- Added Step 0: Pre-Release Sync — mandatory
git pull origin mainbefore any release - Added Known Issue: `@semantic-release/git` Untracked File Explosion —
git ls-files -m -omissing--exclude-standardcrashes plugin with ~100MB stdout in repos with.venv/ - Added Partial Semantic-Release Recovery — manual tag creation when semantic-release partially succeeds
- Added Post-Release Deploy Reminder — prevents version drift on production hosts
- Added 3 new error recovery rows for semantic-release-specific failures
Why It Changed
During the opendeviationbar-py v13.2.0 release:
1. @semantic-release/git v10.0.1 crashed listing 100K+ gitignored .venv/ files (upstream bugs: #345, #347, #107) 2. Semantic-release partially ran (bumped Cargo.toml + CHANGELOG.md) but failed before creating tag — required manual recovery 3. Forgot to pull remote changes before release, causing diverged branch confusion 4. Forgot to deploy to bigblack after PyPI publish, causing 30+ minutes of version drift alerts
Files Affected
SKILL.md: +80 lines (Step 0, Known Issue, Partial Recovery, Deploy Reminder, error table rows)references/evolution-log.md: This entry
---
2026-02-26: Initial Evolution Log
Status: Skill is in use and maintained. Track improvements here.
Purpose
This evolution log tracks updates to the skill. Each entry should note:
- What changed (content, structure, tooling)
- Why it changed (bug fix, feature request, best practice)
- Files affected
How to Use
1. When updating SKILL.md or references, add an entry here with the date 2. Keep entries reverse-chronological (newest first) 3. Link to ADRs or GitHub issues when relevant 4. Reference specific line changes when helpful
---