development
claude/github-issue-40-0au5ua
claude/linked-issue-acp-tui-u4vvpa
claude/minor-release-cut-vkugow
claude/sigit-acp-local-chat-cx6380
claude/sigit-cloud-agent-expansion-reox0a
claude/tool-permission-system
claude/zen-feynman-0u78dk
development
feature/acp-cancel-request
feature/acp-session-list-cursor
feature/acp-v1-conformance
feature/agent-tools-multiedit-glob-todos-remember
feature/cargo-workspace
feature/hooks
feature/load-local-model-explicitly
feature/sigit-agent-profile
feature/sigit-code-cloud
feature/xcode-mcp-server
fix/acp-replay-loaded-session-history
fix/acp-replay-reasoning-and-attachments
fix/dsml-doubled-bar-markers
main
release/v1.5.10
release/v1.5.6
release/v1.6.1
| 1 | --- |
| 2 | name: sigit-code-release |
| 3 | description: Prepare and verify siGit Code releases. Use when bumping versions, checking Cargo, npm, or PyPI package metadata, validating release notes, confirming CI readiness, or assembling a release checklist for a new siGit Code version. |
| 4 | --- |
| 5 | |
| 6 | # siGit Code Release |
| 7 | |
| 8 | Use this skill when preparing a release for this repository. |
| 9 | |
| 10 | ## Focus areas |
| 11 | |
| 12 | - Keep the branded product name as `siGit Code` in prose and `sigit` for commands, crate names, repo paths, and package identifiers. |
| 13 | - Verify version consistency across release surfaces, but respect which files are real sources of truth versus generated or release-time rewritten artifacts. |
| 14 | - Check release-facing docs such as `README.md`, `CHANGELOG.md`, workflow notes, and package metadata for stale version references or incorrect naming. |
| 15 | - Validate the local release path pragmatically: formatting, linting, targeted tests, and any repo-specific release checks that matter for the requested version. |
| 16 | |
| 17 | ## Working approach |
| 18 | |
| 19 | 1. Read the files that define the release version and distribution metadata before proposing any changes. |
| 20 | 2. Treat `Cargo.toml` as the primary release version source for the Rust crate and the PyPI package in this repository. |
| 21 | 3. Check how the release workflows rewrite or derive versions before editing checked-in npm or Python metadata. |
| 22 | 4. Prefer targeted verification commands that match CI or packaging workflows already present in the repository. |
| 23 | 5. Report blockers clearly: failed checks, version mismatches, missing changelog entries, or unpublished packaging changes. |
| 24 | |
| 25 | ## Repo-specific release rules |
| 26 | |
| 27 | - Bump the crate version in `Cargo.toml`. |
| 28 | - Update the root `sigit` package entry in `Cargo.lock` when the crate version changes. |
| 29 | - Add or update the top changelog entry in `CHANGELOG.md` for the release being cut. |
| 30 | - Do not treat `npm/sigit/package.json` `0.0.0-dev` as a bug by default. The npm release workflow rewrites it at publish time using `npm/scripts/render-main-package.cjs` and the release tag. |
| 31 | - Every npm package publishes twice, under `@getsigit` and under `@smbcloud`. `@smbcloud` is the pre-1.5.5 name, mirrored so an install predating the rename keeps updating; `@getsigit` is canonical and is what the README, the Homebrew tap, and the ACP registry entry point at. All fourteen names (two base packages, twelve platform packages) publish over OIDC and each needs its own Trusted Publisher on npm pointing at `release-npm.yml`. |
| 32 | - The scope is a render-time argument, not a second copy of the packaging: `render-platform-package.cjs` takes it as a fifth argument, and the base package picks it up from `package-main.json.tmpl` versus `package-compat.json.tmpl`. `npm/sigit/src/index.ts` is built once and shipped under both names — it reads its own `package.json` at runtime to decide which scope to resolve the platform binary from, and prints the migration notice only when it finds itself running as `@smbcloud/sigit`. |
| 33 | - Do not add a hardcoded version to `pypi/pyproject.toml` for normal releases. PyPI uses `maturin` with `dynamic = ["version"]` and derives the published package version from `Cargo.toml`. |
| 34 | - Release workflows are tag-driven. `release-github.yml`, `release-npm.yml`, `release-pypi.yml`, `release-crates.yml`, `release-homebrew.yml`, `release-nuget.yml`, and `release-mcp-registry.yml` all derive `RELEASE_VERSION` from a `v*.*.*` tag or a manually supplied tag input. |
| 35 | - The crate is published to crates.io (`release-crates.yml`) and the Homebrew tap is updated (`release-homebrew.yml`) as part of the tag-driven flow. Per the siGit release flow, Homebrew is auto-triggered — do not dispatch it manually. |
| 36 | - `release-github.yml` builds the binaries, attaches them (plus a `.deb`/`.rpm` per Linux target built with nfpm from `packaging/nfpm.yaml`) to the GitHub release, then dispatches `release-homebrew`, `release-scoop`, `release-winget`, and `release-aur`. A dispatch failure in any one of those four is logged as a warning, not a hard failure — an unconfigured channel doesn't block the rest of the release. `release-nuget.yml` and `release-mcp-registry.yml`, by contrast, are tag-triggered directly like the other language-registry workflows, not dispatched from `release-github.yml`. |
| 37 | - Every release asset carries a `.sha256` sidecar (not just the macOS Homebrew tarball). Scoop, winget, and the AUR PKGBUILD each consume the raw binaries plus their sidecar checksum, rather than the tarball. |
| 38 | - The GitHub release body is generated, not hand-written: `release-github.yml`'s release job checks out the tag, awk-extracts the `## <version>` section from `CHANGELOG.md` (a missing section is a hard failure), appends a full-changelog link and a `sigit.si/code` footnote, and publishes it via `body_path: release_notes.md`. The only file a release-prep change needs to touch is `CHANGELOG.md` — never edit release notes in the GitHub UI, because the next tag push would overwrite them. |
| 39 | - npm and NuGet publish via OIDC trusted publishing — neither has a stored token in the repo. On npm this is configured per package (each `@getsigit/*` name has a trusted publisher pointing at `release-npm.yml`), so a new package needs the same setup before its first publish will work. OIDC requires npm >= 11.5.1 and Node >= 22.14.0. |
| 40 | - The NuGet package (`SiGit.Code`, `dotnet tool install --global SiGit.Code`) bundles all six platform binaries under `native/<os>-<arch>/` behind a managed shim, rather than shipping one package per platform like npm/PyPI. Watch the nuget.org 250 MB package-size limit if it ever grows. |
| 41 | - The winget channel (`release-winget.yml`) files a Package Request issue first and then opens the manifest PR that resolves it, authored by **two separate classic PATs** (not fine-grained) — `WINGET_ISSUE_TOKEN` for the issue and `WINGET_PR_TOKEN` for the PR, replacing the old single `WINGET_TOKEN`. `WINGET_ISSUE_TOKEN` needs `public_repo`. `WINGET_PR_TOKEN` needs `public_repo` **plus `read:org` and `read:discussion`**: wingetcreate forks `microsoft/winget-pkgs` and opens the PR (fork needs `public_repo`; a fine-grained PAT scoped to `getsigit/sigit` can't fork an outside repo and 403/404s), and the "Set the winget PR body" step then runs `gh pr edit`, whose GraphQL query resolves org/team and discussion fields and fails with a scope error on a `public_repo`-only token (CI run 38262301933). The PR token is passed as `WINGET_CREATE_GITHUB_TOKEN` (not `--token`) so it doesn't leak into logs. `wingetcreate update` only works on a package that already exists in `microsoft/winget-pkgs`, so the workflow checks `manifests/g/getSigit/siGitCode` first and falls back to `wingetcreate submit` with `packaging/winget/*.yaml.in` for the first release; every later release takes the `update` path. If a winget release fails on the fork step, check the token type; if it fails on the `gh pr edit` step with a scope error, check `WINGET_PR_TOKEN`'s scopes before re-dispatching. |
| 42 | - The baked-in official MCP server is separately listed in the public MCP Registry as `si.sigit/sigit` (`release-mcp-registry.yml`, driven by `server.json` at the repo root). It's a remote Streamable-HTTP listing, so it's verified by a DNS TXT record on the `sigit.si` domain rather than the GitHub-OIDC scheme used for package listings. |
| 43 | |
| 44 | ## Git release flow |
| 45 | |
| 46 | Releases are shipped on `main`, and the published tags (`v1.2.0`, `v1.2.1`) point at the |
| 47 | `main`-side merge commit, never at a release branch or at `development`. There are two flows, |
| 48 | and picking the wrong one ships the wrong set of commits. Decide first which this is. |
| 49 | |
| 50 | ### Full release (minor/major — ships everything on `development`) |
| 51 | |
| 52 | Cut from `development` when the release is meant to carry all the work that has accumulated there. |
| 53 | |
| 54 | 1. Branch `release/v<version>` off `development`. |
| 55 | 2. Apply the version bump and changelog edits, then commit (e.g. `Release v<version>`). |
| 56 | 3. Merge `release/v<version>` back into `development`. |
| 57 | 4. Merge `development` into `main` (commit message: `Merge development into main for v<version> release`). |
| 58 | 5. Tag `v<version>` on that `main` merge commit, then push `main` and the tag. |
| 59 | |
| 60 | For a full release, step 4 is the only way commits are meant to reach `main`. A feature or fix |
| 61 | PR targeting `main` breaks that: it puts work on the release branch that `development` does not |
| 62 | have, so the next release is cut without it. Such PRs belong on `development` (see the pull |
| 63 | request target rule in `AGENTS.md`). |
| 64 | |
| 65 | ### Scoped patch (hotfix — ships only specific fixes on top of the last tag) |
| 66 | |
| 67 | Use this when the patch must ship **only** a named set of fixes on top of the last released tag, |
| 68 | **not** the unreleased work sitting on `development` (e.g. a patch over `v1.6.7` that must carry |
| 69 | just a winget/Windows hotfix while ACP features stay unreleased). The giveaway is wording like |
| 70 | "scoped patch off `v<prev>`" or "hotfix" — if `development` is far ahead, a `development → main` |
| 71 | merge would drag all of it out under a patch tag, which is wrong. |
| 72 | |
| 73 | 1. Branch `release/v<version>` off the **last released tag** (`v<prev>`), not `development`. |
| 74 | 2. Cherry-pick (or commit) only the in-scope fixes, then the version bump + changelog, and commit |
| 75 | (e.g. `Release v<version>`). |
| 76 | 3. Merge `release/v<version>` back into `development` so the bump and changelog carry forward. |
| 77 | 4. Merge `release/v<version>` **directly into `main`** (commit message: `Release v<version>`) — |
| 78 | **never `development → main` for a scoped patch**, since that would pull `development`'s |
| 79 | unreleased work onto `main`. |
| 80 | 5. Tag `v<version>` on that `main` merge commit, then push `main` and the tag. |
| 81 | |
| 82 | Before tagging a scoped patch, audit the scope: `git log --oneline v<prev>^{commit}..main` and |
| 83 | `git diff --stat v<prev>^{commit}..main` must show only the intended fixes and the release |
| 84 | metadata files (`Cargo.toml`, `Cargo.lock`, `CHANGELOG.md`), nothing from `development`. |
| 85 | |
| 86 | Pushing the `v*.*.*` tag is what fires every release workflow, so create and push it only after the merge into `main` has landed. Do not commit, tag, or push until the user explicitly asks — confirm the version and that they want the release to go out first. |
| 87 | |
| 88 | Decide up front which flow you are in and confirm the exact scope with the user. If in doubt about |
| 89 | whether a release ships everything on `development` or only a scoped set of fixes, ask — the two |
| 90 | flows start from different base commits and are not interchangeable. |
| 91 | |
| 92 | ## Typical files to inspect |
| 93 | |
| 94 | - `Cargo.toml` |
| 95 | - `Cargo.lock` |
| 96 | - `CHANGELOG.md` |
| 97 | - `README.md` |
| 98 | - `npm/sigit/package.json` |
| 99 | - `npm/package-main.json.tmpl`, `npm/package-compat.json.tmpl`, `npm/package.json.tmpl` (publish-time package metadata for both scopes) |
| 100 | - `npm/scripts/render-main-package.cjs` |
| 101 | - `npm/` |
| 102 | - `pypi/` |
| 103 | - `nuget/sigit/` (bundled-binary `.NET` tool packaging) |
| 104 | - `packaging/` (`nfpm.yaml` for `.deb`/`.rpm`, plus `aur/` and `winget/` templates) |
| 105 | - `server.json` (MCP Registry listing) |
| 106 | - `.github/workflows/` (`release-github.yml`, `release-npm.yml`, `release-pypi.yml`, `release-crates.yml`, `release-homebrew.yml`, `release-scoop.yml`, `release-winget.yml`, `release-aur.yml`, `release-nuget.yml`, `release-mcp-registry.yml`) |
| 107 | |
| 108 | ## Release checklist |
| 109 | |
| 110 | - Version bump is applied everywhere it needs to be. |
| 111 | - `Cargo.toml` and the root crate entry in `Cargo.lock` match. |
| 112 | - `CHANGELOG.md` has a correct top entry for the new release. |
| 113 | - `npm/sigit/package.json` is left at `0.0.0-dev` unless the packaging flow itself changed. |
| 114 | - `pypi/pyproject.toml` still uses dynamic versioning unless there is a deliberate packaging change. |
| 115 | - Release workflows still derive their version from the tag as expected. |
| 116 | - Correct git flow for the release type followed, with `v<version>` tagged on the `main` merge commit either way: |
| 117 | - **Full release:** bump committed on `release/v<version>` (off `development`), merged back to `development`, then `development` merged into `main`. |
| 118 | - **Scoped patch:** `release/v<version>` branched off the last tag `v<prev>`, in-scope fixes + bump committed, merged back to `development`, then `release/v<version>` merged **directly into `main`** (never `development → main`), and the scope audited (`git log --oneline v<prev>^{commit}..main` / `git diff --stat v<prev>^{commit}..main`) to show only the intended fixes plus release metadata. |
| 119 | - Release notes or changelog entries match the actual changes. |
| 120 | - CI-equivalent local checks pass for the relevant platform or target. |
| 121 | - Package names, install commands, and branding stay consistent across npm, PyPI, crates.io, Homebrew, NuGet, Scoop, winget, AUR, and the `.deb`/`.rpm` packages. |
| 122 | - Every release asset (including the new `.deb`/`.rpm`/raw binaries consumed by Scoop, winget, and AUR) has a matching `.sha256` sidecar. |
| 123 | - Any known release limitations are called out explicitly, including a dispatch failure in `release-scoop`/`release-winget`/`release-aur` (non-fatal — logged as a warning by `release-github.yml`) versus a hard failure in a directly tag-triggered workflow like `release-nuget` or `release-mcp-registry`. |
| 124 | - The winget channel uses two classic PATs (not fine-grained): `WINGET_ISSUE_TOKEN` (`public_repo`) for the Package Request issue and `WINGET_PR_TOKEN` (`public_repo` + `read:org` + `read:discussion`) for the manifest PR, so wingetcreate can fork `microsoft/winget-pkgs` and `gh pr edit` can set the PR body. If a winget release fails on the fork step, verify the token type; if it fails on the `gh pr edit` step, verify `WINGET_PR_TOKEN`'s scopes before re-dispatching. |