Metadata-Version: 2.4
Name: repo-drift
Version: 1.0.1
Summary: Cross-repo contract drift checker with AI-powered discovery
License-Expression: MIT
Project-URL: Homepage, https://github.com/bristena-op/repo-drift
Project-URL: Repository, https://github.com/bristena-op/repo-drift
Project-URL: Issues, https://github.com/bristena-op/repo-drift/issues
Keywords: drift,contracts,schema,ci,github-actions,cross-repo
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Testing
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: pyrefly; extra == "dev"

# repo-drift

Cross-repo contract drift checker with AI-powered discovery and architecture mapping. Zero external dependencies.

Detects when shared contracts (API schemas, protobuf definitions, OpenAPI specs, config files) drift between repos that are supposed to stay in sync.

Ships as a **Python package** (recommended) and a **Go binary** (beta). Both implementations produce identical output.

## Install

### Python (recommended)

```bash
pip install repo-drift
```

Requires Python 3.11+. No external dependencies -- stdlib only.

### Go (beta)

```bash
go install github.com/bristena-op/repo-drift/go/cmd/repo-drift@latest
```

Or build from source:

```bash
git clone https://github.com/bristena-op/repo-drift.git
cd repo-drift/go
go build -o repo-drift ./cmd/repo-drift/
```

### Container

```bash
docker pull ghcr.io/bristena-op/repo-drift:latest
```

## Quick start

### 1. Initialize your project (AI-powered)

Point at your repos and let the agent analyze them, map the architecture, discover contracts, and generate everything you need:

```bash
# Using GitHub Copilot (default — opens browser for OAuth if no key set)
repo-drift init \
  --repos myorg/api-service myorg/web-app myorg/shared-schemas

# Using OpenAI
repo-drift init \
  --repos myorg/api-service myorg/web-app myorg/shared-schemas \
  --provider openai --api-key $OPENAI_API_KEY

# Auto-detect provider from key format (sk-... → OpenAI, otherwise Copilot)
repo-drift init \
  --repos myorg/api-service myorg/web-app myorg/shared-schemas \
  --api-key $YOUR_API_KEY
```

This runs a 5-phase pipeline: clones each repo, performs deep file analysis, uses an LLM to map cross-repo architecture and relationships, discovers contract files, and generates two output files:

- **`ARCHITECTURE.md`** — a comprehensive architecture map with mermaid diagrams, relationship details, contract expectations with risk levels, and a drift config summary
- **`.repo-drift.json`** — the drift config with repos, checks, and policy ready for `repo-drift run`

You get an interactive review before anything is written. Pass `--approve` to skip the review (useful in CI).

### 2. Or discover contracts only

If you already know your architecture and just want contract discovery:

```bash
repo-drift discover \
  --repos myorg/api-service myorg/web-app myorg/shared-schemas \
  --api-key $OPENAI_API_KEY --provider openai \
  --output .repo-drift.json
```

### 3. Run drift checks

```bash
repo-drift run --config .repo-drift.json --snapshot
```

Outputs a JSON report and a human-readable Markdown report. Exits non-zero if any policy-breaking drift is detected.

### 4. CI gate — check changed files

Use the `check` command to assess whether a PR touches contract files. This is a fast, deterministic gate — no AI needed, just config:

```bash
# Pipe changed files from git
git diff --name-only | repo-drift check --config .repo-drift.json

# Or specify files explicitly
repo-drift check --config .repo-drift.json --files src/api.json schemas/v2.json

# With explicit repo (required when config has multiple repos)
repo-drift check --config .repo-drift.json --repo my-api --files sdk/task_templates/pod-spec.json
```

Exits 0 (pass) if no contract files are affected, or exit 1 (fail) if policy rules are violated. Outputs a JSON verdict to stdout with affected checks, severity, and summary.

In CI (GitHub Actions):

```yaml
- uses: bristena-op/repo-drift@v1
  with:
    mode: check
    config_path: .repo-drift.json
```

The action auto-detects changed files from the PR diff and exports `verdict`, `affected_count`, and `summary` as step outputs.

### 5. Generate a gh-aw workflow template

Generate a [GitHub Agentic Workflows](https://docs.github.com/en/copilot/using-github-copilot/using-copilot-for-agents) (gh-aw) template for AI-powered cross-repo PR review:

```bash
# Print to stdout
repo-drift generate-aw --config .repo-drift.json

# Write to file
repo-drift generate-aw --config .repo-drift.json --output .github/workflows/drift-review.md
```

This generates a gh-aw `.md` workflow file that triggers on pull requests, checks out sibling repos, and has an AI agent review the PR diff against your contract configuration. Complementary to the deterministic `check` gate — `check` gives pass/fail, gh-aw gives a detailed review comment.

### 6. Automate in CI

Copy a workflow template into your repo and run drift checks on a schedule:

```yaml
# .github/workflows/repo-drift.yml
- uses: bristena-op/repo-drift@v1
  with:
    mode: discover
    repos: myorg/api-service myorg/web-app
    api_key: ${{ secrets.COPILOT_API_KEY }}
    app_id: ${{ vars.REPO_DRIFT_APP_ID }}
    app_private_key: ${{ secrets.REPO_DRIFT_PRIVATE_KEY }}
```

See [GitHub Actions](#github-actions) for full setup.

## Features

- **AI-powered init** — deep repo analysis, cross-repo architecture mapping, contract discovery, and config generation in a single command
- **Architecture documentation** — generates `ARCHITECTURE.md` with mermaid diagrams, relationship maps, risk-assessed contract expectations, and drift config summary. Designed for cron-based regeneration.
- **Deterministic CI gate** — `check` command assesses whether PR changes affect contracts. Fast pass/fail, no AI required.
- **AI-powered PR review** — `generate-aw` creates a gh-aw workflow template for AI-powered cross-repo PR review with full architecture context
- **AI-powered discovery** — point at your repos and let an LLM agent find contract files and generate the config
- **Multi-model consensus** — run init or discovery with multiple models and rank results by agreement for higher confidence
- **Overrides** — `.repo-drift-overrides.json` lets you customize generated config without editing it directly; overrides survive regeneration
- **Zero external dependencies** — Go uses only the standard library; Python uses only stdlib (`urllib.request` for HTTP)
- **Cross-language parity** — Go and Python produce identical JSON output for the same config
- Branch-pinned snapshot management (`develop_or_main`, `develop`, `main`, `custom`)
- Pluggable checks (`file_pair_hash`, `json_schema_compat`)
- Policy-based fail/warn behavior
- Normalized JSON + Markdown reporting
- GitHub Actions (composite action + reusable workflows)
- GitHub App auth — one-time org setup, no per-repo secrets
- Container image on GHCR

## Project layout

```
repo-drift/
  python/              # Python implementation (recommended)
    pyproject.toml
    src/repo_drift/    # config, checks, policy, report, runner, snapshot, llm, discover, init
    tests/             # 250 unit tests
  go/                  # Go implementation (beta)
    go.mod
    cmd/repo-drift/    # CLI entrypoint
    internal/          # same packages as Python
  testdata/            # Shared test fixtures (both languages use these)
  .github/workflows/   # CI + publish workflows
  action.yml           # GitHub Actions composite action
  schemas/             # JSON schemas for config and check results
  templates/           # Example workflow files for consumers
```

## CLI reference

Both Go and Python CLIs accept the same flags.

### Init options

| Flag                    | Description                                                            | Default            |
| ----------------------- | ---------------------------------------------------------------------- | ------------------ |
| `--repos URL [URL ...]` | Git repo URLs, `org/repo` shorthand, or local paths                    | (required)         |
| `--api-key KEY`         | LLM API key (or set env var per provider)                              | env lookup         |
| `--provider NAME`       | LLM provider: `copilot`, `openai` (default: auto-detect)               | auto-detect        |
| `--model NAME`          | Model(s) for analysis. Repeat for consensus.                           | `claude-sonnet-4.6`           |
| `--workspace DIR`       | Workspace root for cloning repos                                       | `.`                |
| `--output-config PATH`  | Config output path (Python) / `--output DIR` for output directory (Go) | `.repo-drift.json` |
| `--output-arch PATH`    | Architecture map output path (Python only)                             | `ARCHITECTURE.md`  |
| `--overrides PATH`      | Path to overrides JSON                                                 | auto-detect        |
| `--approve`             | Skip interactive review, write directly                                | off                |
| `--no-snapshot`         | Skip cloning, assume repos already present                             | off                |
| `--ref-policy`          | Branch policy: `develop_or_main`, `develop`, `main`                    | `develop_or_main`  |

### How init works

Init runs a 5-phase pipeline:

1. **Snapshot** — clones each repo (shallow) into `repos/<repo-id>/`
2. **Analyze** — deep file-based scan of each repo: detects manifests (`package.json`, `go.mod`, etc.), frameworks, entry points, API endpoints, contract files (proto, schemas, OpenAPI), and infrastructure components. Sends analysis to the LLM for enhancement (descriptions, confidence scoring).
3. **Map** — sends all repo analyses to the LLM, which builds a cross-repo architecture map: relationships between repos, contract expectations with risk levels, and system-wide notes.
4. **Discover** — scans for contract files and classifies them with the LLM (source vs consumer, domain, file type). Matches contract pairs across repos.
5. **Generate** — produces two output files:
   - **`ARCHITECTURE.md`** — system overview table, repo details with endpoints/infra, mermaid relationship diagram, contract expectations sorted by risk, drift config summary, and generation metadata.
   - **`.repo-drift.json`** — complete drift config with repos, checks (derived from contract pairs), and policy.

When run locally, you get an interactive review showing a summary of what was found. Press `Y` to accept or `n` to abort. In CI, pass `--approve` to skip the prompt and write files directly.

### Overrides

Create a `.repo-drift-overrides.json` to customize the generated config without editing it directly. Overrides survive regeneration — when `init` runs again, it merges your overrides on top of the fresh config.

```json
{
  "repos": [
    {
      "id": "my-extra-repo",
      "url": "https://github.com/org/extra.git",
      "ref_policy": "main"
    }
  ],
  "checks": [
    {
      "id": "custom-check",
      "type": "file_pair_hash",
      "source_repo": "org/schemas",
      "source_path": "api/v2.json",
      "target_repo": "org/consumer",
      "target_path": "schemas/api-v2.json",
      "severity": "critical"
    }
  ],
  "policy": {
    "fail_on": ["critical", "high"]
  },
  "severity_overrides": {
    "check-id-from-generated": "critical"
  }
}
```

**Merge rules:**

- `repos` — union by ID; override entry wins on conflict
- `checks` — union by ID; override entry wins on conflict
- `policy.fail_on` — union, deduplicated; override entries appear first
- `severity_overrides` — applied after merge, changes the `severity` field on matching checks
- Unknown top-level keys from overrides are copied through

### Discover options

| Flag                    | Description                                         | Default           |
| ----------------------- | --------------------------------------------------- | ----------------- |
| `--repos URL [URL ...]` | Git repo URLs, `org/repo` shorthand, or local paths | (required)        |
| `--api-key KEY`         | LLM API key (or set env var per provider)           | env lookup        |
| `--provider NAME`       | LLM provider: `copilot`, `openai` (default: auto)   | auto-detect       |
| `--model NAME`          | Model(s) for discovery. Repeat for consensus.       | `claude-sonnet-4.6`          |
| `--output PATH`         | Write config to file (omit to print to stdout)      | stdout            |
| `--approve`             | Skip interactive review, write directly             | off               |
| `--no-snapshot`         | Skip cloning, assume repos already present          | off               |
| `--ref-policy`          | Branch policy: `develop_or_main`, `develop`, `main` | `develop_or_main` |
| `--workspace DIR`       | Workspace root for cloning repos                    | `.`               |

### How discovery works

1. **Snapshot** — clones each repo (shallow) into `repos/<repo-id>/`
2. **Discover** — scans each repo for contract-like files (schemas, specs, protos), sends candidates to the LLM for classification (source vs consumer, domain, file type)
3. **Generate** — sends all discovery catalogs to the LLM, which matches contracts across repos and generates the full drift config with checks and policy

When run locally, you get an interactive review prompt showing the proposed checks. Press `Y` to accept or `n` to abort. In CI, pass `--approve` to skip the prompt and write directly.

Shorthand repo slugs (`org/repo`) are expanded to `https://github.com/org/repo.git`.

### Multi-provider LLM support

repo-drift supports multiple LLM providers. The provider is auto-detected from environment variables or key format, with `--provider` to override.

| Provider  | Auto-detect                               | Base URL                | Auth                         |
| --------- | ----------------------------------------- | ----------------------- | ---------------------------- |
| `copilot` | `COPILOT_API_KEY` or `GITHUB_COPILOT_KEY` | `api.githubcopilot.com` | OAuth device flow or env var |
| `openai`  | `OPENAI_API_KEY` or key starts with `sk-` | `api.openai.com/v1`     | API key required             |

**Auto-detection order:**

1. `--provider` flag (if set, always wins)
2. `--api-key` value: keys starting with `sk-` → OpenAI, otherwise → Copilot
3. Environment variables: `OPENAI_API_KEY` → OpenAI, `COPILOT_API_KEY`/`GITHUB_COPILOT_KEY` → Copilot
4. Default: Copilot (starts OAuth device flow if no key found)

**Copilot OAuth device flow:**

When using the Copilot provider without an API key, repo-drift starts an interactive OAuth device flow:

1. Prints a URL (`github.com/login/device`) and a one-time code
2. You open the URL in your browser and enter the code
3. The tool polls until you authorize, then caches the token in `~/.config/repo-drift/copilot-token.json`

The cached token is reused on subsequent runs until it expires.

### Multi-model consensus

Run init or discovery with multiple models to increase confidence through agreement-based ranking:

```bash
# Init with consensus (Python)
repo-drift init \
  --repos myorg/api-service myorg/web-app \
  --model claude-sonnet-4.6 --model gpt-4o \
  --api-key $COPILOT_API_KEY

# Init with consensus (Go)
repo-drift init \
  --repos myorg/api-service,myorg/web-app \
  -model claude-sonnet-4.6 -model gpt-4o \
  --api-key $COPILOT_API_KEY

# Discover with consensus (Python)
repo-drift discover \
  --repos myorg/api-service myorg/web-app \
  --model claude-sonnet-4.6 --model gpt-4o \
  --api-key $COPILOT_API_KEY \
  --output .repo-drift.json
```

When multiple `--model` flags are specified:

1. Each model independently analyzes repos, maps architecture, classifies contracts, and generates pairs
2. Results are merged: entries found by multiple models get higher agreement scores
3. Output is ranked by agreement count (descending), then confidence
4. The summary and `ARCHITECTURE.md` show how many models agree on each finding

The output includes `agreement_count` and `agreed_models` fields on each discovered contract entry and pair, so you can filter by confidence level. A single `--model` flag (the default) skips consensus and behaves as before.

### List available models

Query which models are available on your LLM provider:

```bash
# Auto-detect provider (Copilot default)
repo-drift models

# Specify provider explicitly
repo-drift models --provider openai --api-key $OPENAI_API_KEY

# With Copilot (starts OAuth device flow if no key cached)
repo-drift models --provider copilot
```

This queries the provider's `/models` endpoint and prints a table of available model IDs. Useful for checking which models you can pass to `--model` in `init` or `discover`.

| Flag              | Description                               | Default     |
| ----------------- | ----------------------------------------- | ----------- |
| `--api-key KEY`   | LLM API key (or set env var per provider) | env lookup  |
| `--provider NAME` | LLM provider: `copilot`, `openai`         | auto-detect |

### Run options

```bash
repo-drift run \
  --config .repo-drift.json \
  --workspace . \
  --snapshot \
  --output-json report.json \
  --output-md report.md
```

| Flag              | Description                   | Default    |
| ----------------- | ----------------------------- | ---------- |
| `--config PATH`   | Path to drift config JSON     | (required) |
| `--workspace DIR` | Workspace root                | `.`        |
| `--snapshot`      | Clone/update repos before run | off        |
| `--output-json`   | JSON report output path       | none       |
| `--output-md`     | Markdown report output path   | none       |

### Snapshot-only

```bash
repo-drift snapshot --config .repo-drift.json --workspace .
```

### Check options

Assess whether changed files affect contracts. Designed for CI pipelines:

```bash
# Pipe from git diff
git diff --name-only | repo-drift check --config .repo-drift.json

# Explicit files (Python uses --files, Go uses positional args)
repo-drift check --config .repo-drift.json --files src/api.json  # Python
repo-drift check -config .repo-drift.json src/api.json           # Go

# Specify repo when config has multiple repos
repo-drift check --config .repo-drift.json --repo my-api --files sdk/pod-spec.json

# Write verdict to file instead of stdout
repo-drift check --config .repo-drift.json --files a.json --output-json verdict.json
```

| Flag              | Description                                                           | Default    |
| ----------------- | --------------------------------------------------------------------- | ---------- |
| `--config PATH`   | Path to .repo-drift.json                                              | (required) |
| `--repo ID`       | Repo ID where the PR is opened (auto-detected if config has one repo) | auto       |
| `--files FILE...` | Changed files relative to repo root (Python); positional args in Go   | stdin      |
| `--output-json`   | Write JSON verdict to file                                            | stdout     |

**Verdict JSON format:**

```json
{
  "repo": "my-api",
  "changed_files": ["sdk/task_templates/pod-spec.json"],
  "affected": [
    {
      "check_id": "pod-spec-hash-sync",
      "check_type": "file_pair_hash",
      "severity": "high",
      "matched_file": "sdk/task_templates/pod-spec.json",
      "matched_param": "left",
      "counterpart": "repos/acme-web/packages/desktop/generated/pod-spec.json"
    }
  ],
  "verdict": "fail",
  "summary": "1 contract file(s) affected across 1 check(s). Highest severity: high."
}
```

### Generate-aw options

Generate a GitHub Agentic Workflows (gh-aw) template for AI-powered cross-repo PR review:

```bash
# Print to stdout
repo-drift generate-aw --config .repo-drift.json

# Write to file
repo-drift generate-aw --config .repo-drift.json --output .github/workflows/drift-review.md

# Custom paths referenced in the template
repo-drift generate-aw --config .repo-drift.json \
  --architecture-path docs/ARCHITECTURE.md \
  --config-path config/.repo-drift.json
```

| Flag                  | Description                                        | Default            |
| --------------------- | -------------------------------------------------- | ------------------ |
| `--config PATH`       | Path to .repo-drift.json                           | (required)         |
| `--output PATH`       | Write template to file (omit for stdout)           | stdout             |
| `--architecture-path` | Relative path to ARCHITECTURE.md in the workspace  | `ARCHITECTURE.md`  |
| `--config-path`       | Relative path to .repo-drift.json in the workspace | `.repo-drift.json` |

The generated template is a gh-aw `.md` workflow file with:

- **Checkout frontmatter** for all remote repos in the config
- **Contract summary** listing all monitored file pairs with severities
- **AI agent instructions** for reviewing PR diffs against contracts
- **Output format** for the review comment (table of affected contracts + recommendations)

## Authentication for private repos

repo-drift needs read access to clone repos listed in your config or `--repos`.
For **public repos**, no auth is needed. For **private repos**, choose one of:

### Option A: GitHub App (recommended)

A GitHub App provides automatic short-lived tokens — no per-repo PATs to manage.
Set it up once at the org level and every repo gets access automatically.

**One-time setup:**

1. Create a GitHub App in your org settings:
   - **Name**: `repo-drift` (or any name)
   - **Permissions**: Repository contents → **Read-only**
   - **Installation**: Install on the org, grant access to all repos (or select specific ones)
2. Note the **App ID** and generate a **private key** (PEM file).
3. Add org-level config:
   - **Variable**: `REPO_DRIFT_APP_ID` = your App ID (Settings → Secrets and variables → Actions → Variables)
   - **Secret**: `REPO_DRIFT_PRIVATE_KEY` = contents of the PEM file (Settings → Secrets and variables → Actions → Secrets)
4. For discover mode, also add: **Secret**: `COPILOT_API_KEY` = your LLM API key

That's it. Every repo in the org inherits these — no per-repo secret configuration.

**How it works at runtime**: The action uses [`actions/create-github-app-token`](https://github.com/actions/create-github-app-token) to mint a short-lived installation token (expires in 1 hour). This token is used for cloning and has only the permissions you granted the app.

### Option B: Personal access token (PAT)

Create a fine-grained PAT with `contents: read` on the target repos, then pass it as `cross_repo_read_token`. This works but requires manual token management and per-repo (or org-level) secret setup.

### Auth priority

When multiple auth methods are configured, the action resolves in this order:

1. **GitHub App** (`app_id` + `app_private_key`) — mints installation token
2. **Explicit PAT** (`cross_repo_read_token`) — used as-is
3. **Default `GITHUB_TOKEN`** — only works for same-repo access

### Environment variables (CLI usage)

| Variable                                  | Purpose                            |
| ----------------------------------------- | ---------------------------------- |
| `COPILOT_API_KEY` or `GITHUB_COPILOT_KEY` | Copilot provider API key           |
| `OPENAI_API_KEY`                          | OpenAI provider API key            |
| `LLM_API_KEY`                             | Fallback API key (any provider)    |
| `REPO_DRIFT_GIT_TOKEN`                    | Git auth for cloning private repos |
| `CROSS_REPO_READ_TOKEN`                   | Git auth (CI fallback)             |
| `GITHUB_TOKEN`                            | Git auth (Actions fallback)        |

## GitHub Actions

The action supports two modes via the `mode` input: `run` (default) and `discover`.

### Discover mode — auto-generate config in CI

Add this workflow to have the agent scan your repos and open a PR with the generated config:

```yaml
# .github/workflows/repo-drift-discover.yml
name: Repo Drift — Discover
on:
  workflow_dispatch:
    inputs:
      repos:
        description: "Space-separated repo URLs or org/repo slugs"
        required: true
        type: string
  schedule:
    - cron: "0 9 * * 1"

permissions:
  contents: write
  pull-requests: write

jobs:
  discover:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
      - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
        with:
          python-version: "3.11"

      - uses: bristena-op/repo-drift@v1
        with:
          mode: discover
          repos: ${{ github.event.inputs.repos || 'org/repo-a org/repo-b' }}
          api_key: ${{ secrets.COPILOT_API_KEY }}
          model: claude-sonnet-4.6
          # GitHub App auth (recommended)
          app_id: ${{ vars.REPO_DRIFT_APP_ID }}
          app_private_key: ${{ secrets.REPO_DRIFT_PRIVATE_KEY }}
```

Full example with PR creation: `templates/workflows/repo-drift-discover.example.yml`

### Discover + Run — fully automated

Discover contracts then immediately run drift checks, no config file needed:

```yaml
- uses: bristena-op/repo-drift@v1
  with:
    mode: discover
    repos: org/repo-a org/repo-b
    api_key: ${{ secrets.COPILOT_API_KEY }}
    discover_output: .repo-drift.json
    app_id: ${{ vars.REPO_DRIFT_APP_ID }}
    app_private_key: ${{ secrets.REPO_DRIFT_PRIVATE_KEY }}

- uses: bristena-op/repo-drift@v1
  with:
    mode: run
    config_path: .repo-drift.json
    snapshot: "false"
    app_id: ${{ vars.REPO_DRIFT_APP_ID }}
    app_private_key: ${{ secrets.REPO_DRIFT_PRIVATE_KEY }}
```

Full example: `templates/workflows/repo-drift-discover-and-run.example.yml`

### Run mode — manual config

If you already have a config file:

```yaml
- uses: bristena-op/repo-drift@v1
  with:
    mode: run
    config_path: .repo-drift.json
    snapshot: "true"
    app_id: ${{ vars.REPO_DRIFT_APP_ID }}
    app_private_key: ${{ secrets.REPO_DRIFT_PRIVATE_KEY }}
```

### Action inputs

| Input                   | Mode     | Description                                  | Default                          |
| ----------------------- | -------- | -------------------------------------------- | -------------------------------- |
| `mode`                  | both     | `run` or `discover`                          | `run`                            |
| `workspace`             | both     | Workspace root                               | `.`                              |
| `app_id`                | both     | GitHub App ID for automatic token generation | `""`                             |
| `app_private_key`       | both     | GitHub App private key (PEM)                 | `""`                             |
| `cross_repo_read_token` | both     | Git auth token (fallback if no app auth)     | `""`                             |
| `config_path`           | run      | Path to config JSON                          | `.repo-drift.json`               |
| `snapshot`              | run      | Refresh snapshots before checks              | `"true"`                         |
| `output_json`           | run      | JSON report output path                      | `generated/workflow-report.json` |
| `output_md`             | run      | Markdown report output path                  | `generated/workflow-report.md`   |
| `repos`                 | discover | Space-separated repo URLs/slugs              | `""`                             |
| `api_key`               | discover | LLM API key                                  | `""`                             |
| `model`                 | discover | Model name                                   | `claude-sonnet-4.6`              |
| `discover_output`       | discover | Generated config output path                 | `.repo-drift.json`               |
| `ref_policy`            | discover | Branch resolution policy                     | `develop_or_main`                |

### Reusable workflows

For the `workflow_call` pattern:

- **Run**: `.github/workflows/repo-drift-reusable.yml`
- **Discover**: `.github/workflows/repo-drift-discover-reusable.yml`

Both accept `app_id` (input) and `app_private_key` (secret) for GitHub App auth.

### Consumer setup checklist

1. **Install the GitHub App** on your org (see [Authentication](#authentication-for-private-repos)).
2. Set org-level **variable** `REPO_DRIFT_APP_ID` and **secret** `REPO_DRIFT_PRIVATE_KEY`.
3. For discover mode, add org-level **secret** `COPILOT_API_KEY`.
4. Copy a workflow template from `templates/workflows/` into your repo's `.github/workflows/`.
5. Pin `uses:` to a release tag or commit SHA.

If CI shows `could not read Username for 'https://github.com'`, the app is not installed on the target repos or the private key is wrong.

## Development

### Prerequisites

- Python 3.11+ (for the Python implementation)
- Go 1.24+ (for the Go implementation, beta)
- [pre-commit](https://pre-commit.com/) (optional, for git hooks)

### Setup

```bash
# Install pre-commit hooks
pip install pre-commit && pre-commit install

# Python — install dev deps and test (250 tests)
cd python
pip install -e ".[dev]"
pyrefly check
pytest tests/ -v

# Go (beta) — build and test (239 tests)
cd go
go build ./...
go test ./... -v
```

### Running checks

```bash
# Python
cd python
ruff check src/ tests/
ruff format --check src/ tests/
pyrefly check
pytest tests/ -v

# Go (beta)
cd go
go vet ./...
go test ./... -v -count=1 -race
go build ./cmd/repo-drift/
```

CI runs all of the above on every push and PR (see `.github/workflows/ci.yml`).

### Release process

- Pull requests are expected to use Conventional Commit titles.
- Releases are created automatically from merges to `main`.
- `CHANGELOG.md` and the Python package version are managed by release automation.

## License

MIT
