oy-cli 0.13.5

Focused OpenCode agent with repeatable audits, code reviews, and one-finding fixes
Documentation
name: CI

on:
  pull_request:
  push:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: read

jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - uses: jdx/mise-action@e6a8b3978addb5a52f2b4cd9d91eafa7f0ab959d # v4.2.0
      - name: Install Rust components
        run: rustup component add rustfmt clippy
      - name: Install cargo-nextest
        run: cargo install cargo-nextest --locked --version 0.9.135
      - name: Install Miri toolchain
        run: rustup toolchain install nightly --profile minimal --component miri --component rust-src
      - name: Format
        run: cargo fmt --check
      - name: Clippy
        run: cargo clippy --all-targets --locked -- -D warnings
      - name: Test
        run: |
          cargo nextest run --all-targets --locked --profile ci
          cargo test --doc --locked
      - name: Miri smoke
        run: cargo +nightly miri test --locked miri_smoke
      - name: Rustdoc
        run: RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --locked
      - name: mdBook
        run: mdbook build
      - name: CLI help smoke
        run: |
          cargo run --locked -- --help
          cargo run --locked -- setup --help
          cargo run --locked -- run --help
          cargo run --locked -- audit --help | tee /tmp/oy-audit-help.txt
          grep -F -- "--max-chunks <N>" /tmp/oy-audit-help.txt
          cargo run --locked -- audit prepare --help
          cargo run --locked -- audit finalize --help
          cargo run --locked -- review --help
          cargo run --locked -- review prepare --help
          cargo run --locked -- review finalize --help
          cargo run --locked -- enhance --help
          cargo run --locked -- recover --help
          cargo run --locked -- doctor --help
          cargo run --locked -- upgrade --help
      - name: OpenCode 2 integration smoke
        run: |
          npm install --global @opencode-ai/cli@0.0.0-next-15353
          opencode2 --version | grep -F '0.0.0-next-15353'
          opencode2 run --help | grep -F -- '--agent string'
          cargo build --locked
          export PATH="$PWD/target/debug:$PATH"
          oy setup
          grep -F '"@oy-cli/opencode@0.13.5"' "$HOME/.config/opencode/opencode.json"
          python3 -c 'import json, pathlib; config=json.loads(pathlib.Path.home().joinpath(".config/opencode/opencode.json").read_text()); assert "command" not in config; assert "oy-audit" not in config.get("commands", {}); assert "tool_output" not in config'
          # The release version does not exist in npm until the tag publishes it.
          # Load this checkout directly for the pre-publish runtime smoke instead.
          npm ci --prefix packages/opencode --ignore-scripts
          python3 - <<'PY'
          import json
          from pathlib import Path

          path = Path.home() / ".config/opencode/opencode.json"
          config = json.loads(path.read_text())
          config["plugins"] = [str(Path("packages/opencode/src/index.js").resolve())]
          path.write_text(json.dumps(config, indent=2) + "\n")
          PY
          opencode2 service start
          # The first location-scoped query boots and caches project services.
          opencode2 api v2.agent.list --param "location[directory]=$PWD" > /dev/null
          opencode2 api v2.agent.list --param "location[directory]=$PWD" > /tmp/opencode-agents.json
          opencode2 api v2.command.list --param "location[directory]=$PWD" > /tmp/opencode-commands.json
          grep -F '"id":"oy"' /tmp/opencode-agents.json
          ! grep -F '"id":"oy-auto"' /tmp/opencode-agents.json
          grep -F '"name":"oy-audit"' /tmp/opencode-commands.json
          grep -F '"agent":"oy"' /tmp/opencode-commands.json
          oy doctor --check --json | tee /tmp/oy-doctor.json
          python3 -c 'import json; data=json.load(open("/tmp/oy-doctor.json")); assert data["check_ok"] is True'
          opencode2 service stop
      - name: Documentation drift checks
        run: |
          ! grep -R -n -E 'src/(app|chat|config|model|session|ui)\.rs' README.md CONTRIBUTING.md docs
      - name: Installer smoke
        run: sh scripts/test_install.sh
      - name: Package crate
        run: cargo package --locked

  opencode-package:
    name: OpenCode npm package
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: packages/opencode
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.0.0
        with:
          node-version: "24"
          package-manager-cache: false
      - name: Check Cargo and npm version alignment
        working-directory: .
        run: |
          python3 - <<'PY'
          import json
          import tomllib
          from pathlib import Path

          cargo = tomllib.loads(Path("Cargo.toml").read_text())["package"]["version"]
          npm = json.loads(Path("packages/opencode/package.json").read_text())["version"]
          assert cargo == npm, f"Cargo version {cargo} != npm version {npm}"
          PY
      - name: Install locked dependencies
        run: npm ci --ignore-scripts
      - name: Build and test
        run: |
          npm run build
          npm test
      - name: Pack publishable tarball
        run: |
          mkdir -p ../../.tmp/npm-package
          npm pack --pack-destination ../../.tmp/npm-package
      - name: Verify the packed package installs
        working-directory: .
        run: |
          mkdir -p .tmp/npm-smoke
          cd .tmp/npm-smoke
          npm init --yes >/dev/null
          npm install --ignore-scripts ../npm-package/*.tgz
          node --input-type=module -e 'import("@oy-cli/opencode").then(({default: plugin}) => { if (plugin.id !== "oy") process.exit(1) })'
      - name: Upload npm package artifact
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: opencode-npm-package
          path: .tmp/npm-package/*.tgz
          if-no-files-found: error