squeez 1.11.1

Hook-based token compressor for 5 AI CLI hosts (Claude Code, Copilot CLI, OpenCode, Gemini CLI, Codex CLI). Up to 95% bash compression, signature-mode for code reads, cross-call dedup, MCP server, self-teaching protocol. Zero runtime deps.
Documentation
name: Auto Release on Merge to Main

on:
  push:
    branches: [main]

permissions:
  contents: write

jobs:
  tag-and-release:
    # Recursion guards:
    # 1. Subject-prefix match: the bot's own bump commits always begin with
    #    "chore(release): bump version to". Using startsWith here (not
    #    contains) means prose in PR bodies that mentions the phrase cannot
    #    trip the guard — only the actual commit subject can.
    # 2. Fallback: match by the bot's author email.
    if: |
      !startsWith(github.event.head_commit.message, 'chore(release): bump version to') &&
      github.event.head_commit.author.email != 'github-actions[bot]@users.noreply.github.com'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
          # PAT (fine-grained w/ Contents: read+write or classic w/ `repo` scope)
          # lets the bump commit bypass branch protection AND ensures the pushed
          # tag triggers release.yml (GITHUB_TOKEN-pushed tags do not).
          token: ${{ secrets.RELEASE_PAT }}

      - uses: dtolnay/rust-toolchain@stable

      - name: Determine bump type from conventional commits
        id: bump
        run: |
          set -e
          LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
          if [ -z "$LAST_TAG" ]; then
            RANGE="HEAD"
          else
            RANGE="${LAST_TAG}..HEAD"
          fi

          # SUBJECTS = one subject line per non-merge commit (first line only).
          # BODIES   = full bodies, used strictly for the `BREAKING CHANGE:`
          #            trailer match (must be line-start + colon).
          SUBJECTS=$(git log "$RANGE" --format='%s' --no-merges)
          BODIES=$(git log "$RANGE" --format='%b' --no-merges)

          if [ -z "$SUBJECTS" ]; then
            echo "No commits since $LAST_TAG."
            echo "type=none" >> "$GITHUB_OUTPUT"
            exit 0
          fi

          # Type detection. `BREAKING CHANGE:` trailer is checked at line
          # start with a required colon — avoids matching prose like
          # "- BREAKING CHANGE or !: -> major" inside a body.
          # Header prefixes are matched against SUBJECTS only, so a body
          # containing "feat: xyz" as documentation does not trigger a bump.
          if echo "$SUBJECTS" | grep -qE '^[a-zA-Z]+(\([^)]*\))?!:' \
             || echo "$BODIES"   | grep -qE '^BREAKING CHANGE:'; then
            TYPE=major
          elif echo "$SUBJECTS" | grep -qE '^feat(\([^)]*\))?:'; then
            TYPE=minor
          elif echo "$SUBJECTS" | grep -qE '^(fix|perf)(\([^)]*\))?:'; then
            TYPE=patch
          else
            echo "Only chore/docs/ci/test/refactor commits — no release."
            TYPE=none
          fi

          echo "type=$TYPE" >> "$GITHUB_OUTPUT"
          echo "Detected bump: $TYPE (since ${LAST_TAG:-<initial>})"

      - name: Compute next version
        if: steps.bump.outputs.type != 'none'
        id: version
        run: |
          set -e
          CUR=$(grep -E '^version\s*=' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')
          IFS='.' read -r MA MI PA <<< "$CUR"
          case "${{ steps.bump.outputs.type }}" in
            major) MA=$((MA+1)); MI=0; PA=0 ;;
            minor) MI=$((MI+1)); PA=0 ;;
            patch) PA=$((PA+1)) ;;
          esac
          NEW="${MA}.${MI}.${PA}"
          echo "current=$CUR" >> "$GITHUB_OUTPUT"
          echo "next=$NEW"   >> "$GITHUB_OUTPUT"
          echo "Bumping $CUR -> $NEW"

      - name: Fail fast if tag already exists
        if: steps.bump.outputs.type != 'none'
        run: |
          # Check both the local and remote tag namespaces. A tag may exist
          # locally but not remotely (e.g. a failed push from a prior run),
          # so we also consult origin explicitly.
          if git rev-parse "v${{ steps.version.outputs.next }}" >/dev/null 2>&1 \
             || git ls-remote --tags origin "v${{ steps.version.outputs.next }}" | grep -q "v${{ steps.version.outputs.next }}"; then
            echo "Tag v${{ steps.version.outputs.next }} already exists — manual intervention needed."
            exit 1
          fi

      - name: Bump Cargo.toml
        if: steps.bump.outputs.type != 'none'
        run: |
          sed -i.bak -E "s/^version = \".*\"/version = \"${{ steps.version.outputs.next }}\"/" Cargo.toml
          rm Cargo.toml.bak
          grep -E '^version\s*=' Cargo.toml | head -1

      - name: Refresh Cargo.lock
        if: steps.bump.outputs.type != 'none'
        run: cargo check --quiet

      - name: Commit, tag, push
        if: steps.bump.outputs.type != 'none'
        run: |
          git config user.name  "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add Cargo.toml Cargo.lock
          # Using the namespaced [squeez-release-bot] marker so the recursion
          # guard cannot be tripped by prose in user commit messages.
          git commit -m "chore(release): bump version to ${{ steps.version.outputs.next }} [squeez-release-bot]"
          # Annotated tag so `git push --tags` propagates it (lightweight tags
          # are not pushed by `--follow-tags`, which silently broke the prior
          # release pipeline when v1.0.0 was cut but never published).
          git tag -a "v${{ steps.version.outputs.next }}" -m "Release v${{ steps.version.outputs.next }}"
          git push origin main
          git push origin "v${{ steps.version.outputs.next }}"
          echo "Released v${{ steps.version.outputs.next }} — release.yml will take it from here."