tkach 0.5.0

Provider-independent Rust agent runtime — streaming, reasoning summaries, prompt caching, and per-call approval gating.
Documentation
name: CI Checks

# Reusable workflow. Called by:
#   - ci.yml          on push/PR
#   - release.yml     as a publish gate when release-please cuts a release
#
# Keep all check jobs (fmt, clippy, test, audit, deny, msrv, package) in this
# file so PR-time CI and release-time CI run the exact same suite.

on:
  workflow_call:

env:
  CARGO_TERM_COLOR: always
  RUSTFLAGS: -Dwarnings

jobs:
  fmt:
    name: Format
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@nightly
        with:
          components: rustfmt
      - run: cargo fmt --check

  clippy:
    name: Clippy
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy
      - uses: Swatinem/rust-cache@v2
      - uses: taiki-e/install-action@v2
        with:
          tool: clippy-sarif,sarif-fmt
      - name: Run clippy with SARIF output
        # `set -eo pipefail` is required because GitHub Actions runs
        # multi-line `run:` via `bash -e` without pipefail — a non-zero
        # `cargo clippy` exit would otherwise be masked by `sarif-fmt`
        # exiting 0.
        #
        # `--force-warn` keeps cognitive_complexity and too_many_lines
        # at warn level even under the global `RUSTFLAGS: -Dwarnings`
        # cap. Result: violations surface in SARIF / Code Scanning but
        # don't fail CI; other lints stay strict.
        run: |
          set -eo pipefail
          cargo clippy --all-targets --message-format=json -- \
            --force-warn=clippy::cognitive_complexity \
            --force-warn=clippy::too_many_lines \
            | clippy-sarif \
            | tee clippy.sarif \
            | sarif-fmt
      - name: Render clippy summary
        if: always()
        env:
          REPO: ${{ github.repository }}
          SHA: ${{ github.sha }}
        run: |
          if [[ ! -f clippy.sarif ]]; then
            echo "_clippy.sarif was not generated._" >> "$GITHUB_STEP_SUMMARY"
            exit 0
          fi
          python3 - >> "$GITHUB_STEP_SUMMARY" <<'PY'
          import json, os, re
          from collections import defaultdict

          repo = os.environ.get("REPO", "")
          sha = os.environ.get("SHA", "")

          with open("clippy.sarif") as f:
              sarif = json.load(f)

          # rule -> {(file, line): value} — dedupes lib + lib-test duplicates
          seen = defaultdict(dict)
          for run in sarif.get("runs", []):
              for r in run.get("results", []):
                  rule = r.get("ruleId", "") or "(unknown)"
                  msg = (r.get("message") or {}).get("text", "")
                  m = re.search(r"\((\d+)/\d+\)", msg)
                  val = int(m.group(1)) if m else 0
                  for loc in r.get("locations", []):
                      phys = loc.get("physicalLocation", {})
                      art = phys.get("artifactLocation", {}).get("uri", "?")
                      line = phys.get("region", {}).get("startLine", 0)
                      seen[rule][(art, line)] = val

          total = sum(len(v) for v in seen.values())
          print(f"## Clippy complexity findings: **{total}**\n")
          if not seen:
              print("_All clean — no complexity warnings._")
          else:
              for rule in sorted(seen.keys()):
                  short = rule.split("::")[-1]
                  items = sorted(seen[rule].items(), key=lambda kv: -kv[1])
                  print(f"### `{short}` — {len(items)} findings\n")
                  print("| File | Line | Value |")
                  print("|---|---:|---:|")
                  for (path, line), val in items:
                      url = f"https://github.com/{repo}/blob/{sha}/{path}#L{line}" if repo and sha else ""
                      label = f"[`{path}`]({url})" if url else f"`{path}`"
                      print(f"| {label} | {line} | {val} |")
                  print()
          PY
      - name: Upload SARIF to GitHub Code Scanning
        if: success() || failure()
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: clippy.sarif
          wait-for-processing: true
          category: clippy

  test:
    name: Test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: llvm-tools-preview
      - uses: Swatinem/rust-cache@v2
      - uses: taiki-e/install-action@v2
        with:
          tool: cargo-llvm-cov
      # `--all-targets` also builds examples — catches regressions in
      # examples/basic.rs, custom_tool.rs, parallel_tools.rs that plain
      # `cargo test` would silently miss.
      - name: Run tests with coverage
        run: cargo llvm-cov --all-targets --lcov --output-path lcov.info
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v5
        with:
          files: lcov.info
          fail_ci_if_error: false

  audit:
    name: Security Audit
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: rustsec/audit-check@v2.0.0
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

  deny:
    name: Cargo Deny
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: EmbarkStudios/cargo-deny-action@v2

  msrv:
    name: MSRV (1.86.0)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@1.86.0
      - uses: Swatinem/rust-cache@v2
      - run: cargo check

  package:
    name: Package (cargo publish --dry-run)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      # Catches packaging issues (manifest, file inclusion, doc-test fail)
      # before merge — so release.yml's real `cargo publish` doesn't fail
      # after release-please has already cut a tag.
      - run: cargo publish --dry-run