| name | publish |
| description | Cut a release of cc-connect — Rust binaries (`v*` tag), VSCode extension (`vscode-extension-v*` tag), or both. Use when the user says "publish", "release", "cut a release", "tag a release", "ship", or anything that implies pushing a versioned artifact. Handles version bumps, tag namespaces, CI watch, and the cross-artifact dependencies users keep forgetting. |
Publishing cc-connect
This repo ships two independent artifacts with their own release cadences. Pick the right one for the change being released; if the change touches both halves, release both in the right order.
The two artifacts
| Artifact | Tag pattern | CI workflow | What it ships |
|---|
| Rust binaries | v[0-9]*.*.* (e.g. v0.5.0-alpha, v1.0.0) | .github/workflows/release.yml | Per-platform tarballs of cc-connect, cc-connect-tui, cc-connect-hook, cc-connect-mcp, cc-chat-ui plus install.sh. Attached to GitHub Release. |
| VSCode extension | vscode-extension-v*.*.* | .github/workflows/vscode-extension-release.yml | cc-connect-vscode-<version>.vsix attached to GitHub Release. |
The workflows are independent — tagging one does not trigger the other.
Decide what to release
Ask the user, or infer from git log --oneline <last-tag>..HEAD:
- Rust-only — changes touched any of:
crates/, install.sh, scripts/, layouts/*.md, chat-ui/, Cargo.toml, release.yml. Tag v*.
- VSCode-only — changes are confined to
vscode-extension/. Tag vscode-extension-v*.
- Both — common when a Rust-side feature needs an extension UI to be useful (e.g. layout-prompt changes affect both
claude-wrap.sh and vscode-extension/src/host/prompts.ts). Release Rust first, then the extension that depends on it.
If unsure, default to the more conservative answer: release the Rust side first, then check whether anything in the extension needs the new binaries before tagging the extension.
Cross-artifact dependency gotchas (the things users forget)
Read these before tagging. Each has burned at least one release.
-
bootstrap.sh fetches the latest Rust release tarball. If you change install.sh (e.g. adding a --skip-build flag), users only get the new behavior after you push a new v* tag — bootstrap.sh lives on main but the install logic comes from the tarball. Touched install.sh? You probably need a Rust release.
-
release.yml packages install.sh into the tarball. Same logic — install.sh updates need a Rust release to actually reach users.
-
layouts/*.md is a single source of truth across both halves. TUI uses include_str! (compile-time embed); the VSCode extension copies them to dist/layouts/ at compile time. Touching them means both halves need re-releasing.
-
VSCode extension's package.json::version MUST match the tag. The workflow rejects mismatched tags. Bump package.json first, commit, then tag.
-
Tag pattern collision (now fixed but check anyway). release.yml matches v[0-9]*.*.* (anchored on a digit) so vscode-extension-v0.2.0 no longer double-fires. If you see a release.yml run for a vscode-extension-* tag, it's a regression — cancel the run and re-check the matcher.
-
Tarball must contain all five binaries. install.sh hard-fails when cc-connect-mcp is missing; warns when cc-connect-tui is missing. Both must be in the cp loop in release.yml::Package step.
-
Rust release matrix is 2 platforms, not 3. As of v0.5.0 the matrix is aarch64-apple-darwin (Apple Silicon) + x86_64-unknown-linux-gnu. x86_64-apple-darwin (Apple Intel / macos-13) was dropped because GitHub's public macos-13 runner pool sustains multi-hour queues for tagged release runs. Apple Intel users use CC_CONNECT_FROM_SOURCE=1 in bootstrap.sh. timeout-minutes does NOT solve the queue stall — it counts wall-clock after the job picks up, not queue time. If you see a build job stuck queued for 30+ minutes with no runner, cancel and inspect runner availability rather than waiting. Re-add Intel to the matrix only if the public queue recovers (or we self-host).
Step-by-step: VSCode extension release
git status
git branch --show-current
git fetch origin && git status -sb
node -p "require('./vscode-extension/package.json').version"
$EDITOR vscode-extension/package.json
git add vscode-extension/package.json
git commit -m "chore(vscode-extension): bump to X.Y.Z"
git push origin main
git tag -a vscode-extension-vX.Y.Z -m "vscode-extension-vX.Y.Z — <one-line summary>
<2-5 line bullet list of major changes since last extension release>"
git push origin vscode-extension-vX.Y.Z
gh run watch --exit-status
gh release view vscode-extension-vX.Y.Z
Step-by-step: Rust binary release
git status; git branch --show-current; git fetch origin
git tag --list 'v[0-9]*' --sort=-v:refname | head -1
git tag -a vX.Y.Z[-alpha] -m "vX.Y.Z[-alpha] — <one-line summary>
<3-6 line bullets covering protocol/CLI/install changes>"
git push origin vX.Y.Z[-alpha]
gh run watch --exit-status
gh release view vX.Y.Z[-alpha]
Both halves at once
Always do Rust first, then VSCode extension. The extension's behavior may depend on the binary it shells out to.
git tag -a v0.6.0-alpha -m "..."
git push origin v0.6.0-alpha
gh run watch --exit-status
curl -fsSL https://raw.githubusercontent.com/Minara-AI/cc-connect/main/scripts/bootstrap.sh \
| CC_CONNECT_VERSION=v0.6.0-alpha bash
~/.local/bin/cc-connect doctor
$EDITOR vscode-extension/package.json
git commit -am "chore(vscode-extension): bump to 0.3.0"
git push origin main
git tag -a vscode-extension-v0.3.0 -m "..."
git push origin vscode-extension-v0.3.0
gh run watch --exit-status
Pre-flight checklist
Before tagging, verify:
Release notes (the body of the GitHub Release)
softprops/action-gh-release auto-generates notes from commits since the last tag of the same namespace. The annotated tag message you supply via -m becomes the lead — keep it tight:
- One headline sentence summarising the release theme.
- 3-6 bullets, ranked by user impact: features → bug fixes → internal refactors.
- Mention any follow-up that's still pending (e.g. "Marketplace publish deferred to vscode-extension-v0.3.x").
Watching CI
gh run watch --exit-status
gh run list --limit 5
gh run view <run-id> --log-failed
If CI fails:
vscode-extension-release.yml validation: tag and package.json::version disagree. Bump the file, amend the bump commit, force-push (only main has the bump; the tag points at the same commit you'd amend, so git tag -f vscode-extension-vX.Y.Z after the amend then git push origin vscode-extension-vX.Y.Z --force).
release.yml Rust build failure: usually a flake or a real compile error on a less-tested platform. gh run rerun <id> for flakes; otherwise fix locally and re-tag with the next patch version (don't force-move release tags — users may have already downloaded the artifacts).
release.yml stuck-queued for hours: GitHub public-runner pool starvation (we hit this on macos-13 in 2026-05). Symptoms: matrix entry shows no checkmark and no log output; gh run view <id> reports the job as still queued well past the timeout-minutes value. timeout-minutes does NOT help — it's a runtime budget that only applies after the job picks up. Recovery: cancel the run, drop or skip the affected runner from the matrix in release.yml, push the workflow change, then gh workflow run release.yml -f tag=vX.Y.Z to re-trigger using the patched workflow against the existing tag.
Stage B (deferred)
Marketplace auto-publish via vsce publish is intentionally not wired:
- Needs an Azure DevOps PAT stored as
secrets.VSCE_PAT.
- Needs the
minara publisher account verified.
- Should only run on stable tags (no
-rc.1 / -alpha).
When ready, add a final step to vscode-extension-release.yml:
- name: Publish to Marketplace (stable only)
if: ${{ !contains(github.event.inputs.tag || github.ref_name, '-') }}
working-directory: vscode-extension
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
run: bunx @vscode/vsce publish --packagePath cc-connect-vscode-${{ steps.meta.outputs.version }}.vsix
Reference