# GitScope — Full Documentation Context

---
# Source: index.md

# GitScope

GitScope is a CLI tool that turns git diffs into structured, machine-readable summaries of **what changed semantically** — not what lines changed, but what functions, classes, and APIs were added, removed, modified, or moved. A 500-line diff becomes a ~200-token structured summary with signatures, breaking changes, and review focus areas.

## Who it's for

**AI agents doing code review.** GitScope gives reviewer agents a structural map of a PR before they read the diff — a pre-pass that shifts attention from line-by-line scanning to design-level analysis.

## Why it exists

In a controlled A/B test, a reviewer agent with GitScope's structured output found **37% more issues** than with the raw diff alone — and the unique findings were disproportionately design-level bugs, not surface-level style issues. ([Full results](validation.md))

## Key properties

- **~200-300 tokens of output**, not thousands. Context conservation matters.
- **Structured JSON** with function signatures, breaking change flags, and tiered summaries
- **Sub-500ms** for any diff under 1000 lines
- **CLI-first.** `git diff | gitscope summarize` — works with every agent platform via exec
- **Accuracy over comprehensiveness.** Wrong summaries are worse than no summaries.

## Quick start

```bash
uvx gitscope summarize HEAD~1..HEAD
```

→ [Installation & usage](quickstart.md)

---
# Source: quickstart.md

# Quick Start

## Install

```bash
# Zero-install (recommended)
uvx gitscope summarize HEAD~1..HEAD

# Or install permanently
pip install gitscope
```

## Basic usage

```bash
# Summarize last commit
gitscope summarize HEAD~1..HEAD

# Summarize a PR range
gitscope summarize main..feature-branch

# Pipe a diff
git diff main | gitscope summarize

# Choose output tier
gitscope summarize HEAD~1..HEAD --format oneliner
gitscope summarize HEAD~1..HEAD --format short
gitscope summarize HEAD~1..HEAD --format json
```

## Example output

### `--format oneliner`

```
Replace API key auth with OAuth2 PKCE; 2 breaking changes in auth client
```

### `--format short`

```
Removes authenticate(apiKey), adds authenticateOAuth(config) with PKCE.
refreshToken now returns TokenPair instead of string. 1 new dep: oauth4webapi.
```

### `--format json`

```json
{
  "schema_version": "1.0",
  "meta": {
    "ref_range": "abc1234..def5678",
    "stats": { "files": 3, "additions": 340, "deletions": 89 },
    "warnings": [],
    "timing_ms": 187.4
  },
  "files": [
    {
      "path": "src/auth/client.ts",
      "language": "typescript",
      "change_type": "modified",
      "changes": [
        {
          "kind": "function_removed",
          "name": "authenticate",
          "signature": "authenticate(apiKey: string): Promise<Session>",
          "line": 45,
          "breaking": true
        },
        {
          "kind": "function_added",
          "name": "authenticateOAuth",
          "signature": "authenticateOAuth(config: OAuthConfig): Promise<Session>",
          "line": 47,
          "breaking": false
        }
      ]
    }
  ],
  "summary": {
    "change_types": { "feature": 1, "refactor": 2 },
    "breaking_changes": [
      {
        "kind": "function_removed",
        "name": "authenticate",
        "signature": "authenticate(apiKey: string): Promise<Session>",
        "breaking": true
      }
    ],
    "focus": [
      "authenticate() removed — callers need migration to authenticateOAuth()",
      "refreshToken() return type changed from string to TokenPair"
    ]
  },
  "tiered": {
    "oneliner": "Replace API key auth with OAuth2 PKCE; 2 breaking changes in auth client",
    "short": "Removes authenticate(apiKey), adds authenticateOAuth(config) with PKCE. refreshToken now returns TokenPair. 1 new dep: oauth4webapi.",
    "detailed": "..."
  }
}
```

## What you get

GitScope tells you **what changed structurally**: which functions were added, removed, or modified — with their signatures. It flags breaking changes and generates tiered summaries at different token budgets.

It does **not** tell you *why* something changed or whether it's good. That's the reviewer's job. GitScope gives the map; you navigate it.

---
# Source: schema.md

# Schema Reference

GitScope output is defined by Pydantic v2 models in `src/gitscope/schema.py`. This is the contract — all output validates against these models.

## `GitScopeOutput` (top-level)

| Field | Type | Description |
|-------|------|-------------|
| `schema_version` | `str` | Always `"1.0"` |
| `meta` | `Meta` | Run metadata: ref range, stats, timing |
| `files` | `list[FileChange]` | Per-file semantic changes |
| `summary` | `Summary` | Aggregate: change types, breaking changes, focus areas |
| `tiered` | `TieredSummary` | Human-readable summaries at different token budgets |

## `Meta`

| Field | Type | Description |
|-------|------|-------------|
| `ref_range` | `str` | Git ref range analyzed (e.g. `"abc1234..def5678"`) |
| `stats` | `DiffStats` | `files`, `additions`, `deletions` counts |
| `warnings` | `list[str]` | Degraded output signals (parse errors, truncation) |
| `timing_ms` | `float \| None` | Wall-clock time for analysis |

## `FileChange`

| Field | Type | Description |
|-------|------|-------------|
| `path` | `str` | File path relative to repo root |
| `language` | `str \| None` | Detected language (e.g. `"python"`, `"typescript"`) |
| `change_type` | `"added" \| "removed" \| "modified" \| "renamed"` | File-level change type |
| `generated` | `bool` | Lock files, protobuf output, etc. |
| `binary` | `bool` | Binary file (skipped for analysis) |
| `parse_error` | `bool` | Tree-sitter couldn't parse this file |
| `unsupported_language` | `bool` | No grammar available for this language |
| `changes` | `list[SymbolChange]` | Symbol-level changes in this file |

## `SymbolChange`

| Field | Type | Description |
|-------|------|-------------|
| `kind` | `Literal[...]` | One of: `function_added`, `function_removed`, `function_modified`, `class_added`, `class_removed`, `class_modified`, `signature_changed`, `moved` |
| `name` | `str` | Symbol name (e.g. `"authenticate"`) |
| `signature` | `str \| None` | Full signature for added/removed symbols |
| `before_signature` | `str \| None` | Old signature (for `signature_changed`) |
| `after_signature` | `str \| None` | New signature (for `signature_changed`) |
| `file_from` | `str \| None` | Source file path (for `moved`) |
| `line` | `int \| None` | Line number in the new file |
| `breaking` | `bool` | Whether this change breaks the public API |
| `detail` | `dict \| None` | Escape hatch for language-specific metadata |

## `Summary`

| Field | Type | Description |
|-------|------|-------------|
| `change_types` | `dict[str, int]` | Counts by category (e.g. `{"feature": 3, "refactor": 5}`) |
| `breaking_changes` | `list[SymbolChange]` | All breaking changes, surfaced at top level |
| `focus` | `list[str]` | Short list of the most important items for reviewer agents. See [Agent Integration](agent-integration.md#the-focus-field). |

## `TieredSummary`

| Field | Type | Description |
|-------|------|-------------|
| `oneliner` | `str` | ~20 tokens. One sentence. |
| `short` | `str` | ~80 tokens. Key changes with signatures. |
| `detailed` | `str` | Full narrative. Per-file breakdown. |

## Example output by tier

### `oneliner` (~20 tokens)

```
Replace API key auth with OAuth2 PKCE; 2 breaking changes
```

### `short` (~80 tokens)

```
Removes authenticate(apiKey), adds authenticateOAuth(config) with PKCE.
refreshToken now returns TokenPair. 1 new dep: oauth4webapi.
```

### `detailed` (full JSON)

See the complete JSON example in [Quick Start](quickstart.md#--format-json).

## Design principles

- **Semantic change units.** Function/class/type level, with signatures — not line numbers.
- **Breaking changes at top level.** Not buried in file details.
- **No opinions.** Structural facts only. Risk assessment is the reviewer's job.
- **Tiered output.** Consumers pick their token budget.
- **Graceful degradation.** Parse errors, unsupported languages, and binary files are flagged — never crashes, always produces valid output.

---
# Source: agent-integration.md

# Agent Integration

GitScope is designed for AI agents doing code review. Here's how to integrate it.

## The one-liner

Add this to your `CLAUDE.md`, `AGENTS.md`, or system prompt:

```
Before reviewing a PR, run `uvx gitscope summarize <ref-range> --format short` to get a structural overview.
```

That's it. One line. The agent figures out the rest.

## Why one line is enough

Modern coding agents (Claude Code, Codex, Cursor) can:

1. Parse the instruction
2. Run the command via exec
3. Read the structured output
4. Use it to guide their review

You don't need MCP, tool definitions, or complex integration. CLI + exec is universal.

## How a reviewer agent should use GitScope

**Before reading the diff:**

```bash
gitscope summarize main..feature-branch --format json
```

This gives the agent a map: what files changed, what functions were added/removed/modified, what's breaking, and where to focus. The agent then reads the actual diff with this structural context.

**The workflow:**

1. Agent runs `gitscope summarize` (~200-300 tokens of output)
2. Agent reads the structural map — knows there are 20 functions, 2 breaking changes, 6 untested
3. Agent reads the full diff with targeted attention
4. Agent produces a review that catches design-level issues, not just line-level style

Without the map, agents read diffs linearly and catch surface issues. With it, they allocate attention to the important parts.

## The `focus` field

The `summary.focus` field is a short list of the most important items for a reviewer:

```json
{
  "focus": [
    "authenticate() removed — callers need migration to authenticateOAuth()",
    "refreshToken() return type changed from string to TokenPair",
    "_is_high_impact uses string set matching that silently fails on case mismatch"
  ]
}
```

This is the "read these first" list. If an agent only has budget for a quick review, `focus` tells it where to spend tokens.

## Context conservation

GitScope's output is deliberately small:

| Tier | Typical size |
|------|-------------|
| `oneliner` | ~20 tokens |
| `short` | ~80 tokens |
| `detailed` | ~200-300 tokens |
| Full JSON | ~300-500 tokens |

Compare this to a raw diff: a 500-line change is ~2000-4000 tokens of diff text. GitScope compresses that to a structural summary that fits in a fraction of the context window.

## Platform-specific examples

### Claude Code (`CLAUDE.md`)

```markdown
Before reviewing PRs, run `uvx gitscope summarize <ref-range> --format short` for structural context.
```

### Cursor / Windsurf (rules)

```
When reviewing code changes, first run: gitscope summarize <ref-range> --format json
Use the output to identify breaking changes and focus areas before reading the diff.
```

### Generic agent (system prompt)

```
You have access to `gitscope`, a CLI tool that summarizes git diffs structurally.
Run `gitscope summarize <ref-range> --format json` before reviewing any PR.
Pay special attention to the `summary.focus` and `summary.breaking_changes` fields.
```

## No MCP required

GitScope v0.1.0 is CLI-only. Every agent platform supports running CLI tools via exec. MCP integration is planned for v0.1.1 but is not required — the CLI covers all platforms.

---
# Source: architecture.md

# Architecture

## Pipeline

```
git diff ──→ parse ──→ extract ──→ match ──→ classify ──→ summarize ──→ JSON
             │         │           │         │            │
             │         │           │         │            └─ tiered summaries
             │         │           │         └─ added/removed/modified/moved
             │         │           └─ name-match old↔new symbols (O(n) dict)
             │         └─ tree-sitter queries → functions, classes, methods
             └─ py-tree-sitter parses old + new file versions
```

**Typical timing:** ~200ms for a 1000-line diff.

## Modules

Each module has a single responsibility. No horizontal imports between engine modules.

| Module | Input | Output | Responsibility |
|--------|-------|--------|---------------|
| `git.py` | ref range | changed files + old/new content | All git subprocess calls. Nothing else touches git. |
| `engine/parser.py` | source file | syntax tree | Tree-sitter parsing. No git logic, no matching. |
| `engine/matcher.py` | old symbols + new symbols | matched pairs | Name-based symbol matching. O(n) dict lookup. |
| `engine/classifier.py` | matched pairs | classified changes | Labels: added, removed, modified, moved. |
| `engine/signatures.py` | old + new signatures | breaking change flags | Signature comparison. Detects parameter/return type changes. |
| `engine/summarizer.py` | classified changes | tiered text | Generates oneliner, short, detailed summaries. |
| `schema.py` | — | — | Pydantic models. The contract. |

## Symbol extraction

GitScope uses tree-sitter `tags.scm` queries — the same queries GitHub uses for symbol navigation. This extracts:

- Function/method definitions with signatures
- Class/struct/interface definitions
- Line numbers and scope

For each changed file, GitScope parses both the old and new versions, extracts symbols from each, then matches them by name.

## Matching algorithm

1. Build a dict of old symbols keyed by `(name, kind)`
2. Build a dict of new symbols keyed by `(name, kind)`
3. Symbols in both → **modified** (compare bodies/signatures)
4. Symbols only in old → **removed**
5. Symbols only in new → **added**
6. Removed symbol name appears in a different file as added → **moved**

This is O(n) and handles the common case well. It deliberately does not attempt fuzzy rename detection — accuracy over comprehensiveness.

## Language support

### v0.1.0

| Language | Tree-sitter grammar | Status |
|----------|-------------------|--------|
| Python | tree-sitter-python | ✅ Validated |
| TypeScript | tree-sitter-typescript | ✅ Validated |
| Go | tree-sitter-go | ✅ Validated |

### Graceful degradation

- **Unsupported language:** File included in output with `unsupported_language: true`, line-level stats only.
- **Parse error:** File included with `parse_error: true`, falls back to line-level stats.
- **Binary file:** Skipped with `binary: true`.

GitScope never crashes on unsupported input. It always produces valid JSON.

## Stack

- **Python** — fast enough with native tree-sitter bindings
- **py-tree-sitter** — C-speed parsing, pre-built binaries for 40+ languages
- **Pydantic v2** — schema definition and validation
- **typer** — CLI framework
- **difflib** — per-function body comparison (no GumTree, no full AST diff)

### Why not these alternatives

| Alternative | Why not |
|-------------|---------|
| GumTree | O(n³), Java dependency, killed v1 |
| Rust/TypeScript core | Premature optimization. Python + native tree-sitter is fast enough. |
| difftastic | Line-oriented JSON output, not semantic. Great visual tool, wrong abstraction. |
| ast-grep | Pattern search, not a differ. Possible future add-on. |

---
# Source: validation.md

# Validation

## A/B test: Does GitScope actually help?

We ran a controlled test to answer one question: does a reviewer agent produce better reviews with GitScope's structured output than with the raw diff alone?

### Setup

- **Commit:** 7ae6492 — 944 lines changed across 3 files
- **Baseline:** AI reviewer with raw diff only (974 lines of diff text)
- **Assisted:** AI reviewer with GitScope v2 JSON output + the same raw diff

### Results

| Metric | Baseline | Assisted |
|--------|----------|----------|
| Total findings | 19 | **26 (+37%)** |
| Design-level issues | 4 | **6** |
| Specific missing test coverage | 1 (generic) | **6 (named functions)** |
| Bugs found | 5 | 5 |

### What the assisted reviewer caught that the baseline missed

1. **`_is_high_impact` dual-set case matching bug** — a real logic error where `"If_Statement"` silently fails. The standout finding.
2. **`_get_ref_content` can't distinguish "file missing" from "git error"** — error handling gap
3. **`reshape_engine_output` mutates input dict** — callers don't expect this
4. **Specific untested functions** — named 6 functions with no test coverage, vs baseline's generic "no CLI tests"

### What the baseline caught that the assisted missed

1. Type annotation `str` with default `None` (should be `str | None`)
2. `ref~1` doesn't exist for initial commit
3. `_risk_level_value` returns -1 for unknown

The baseline's unique findings were surface-level. The assisted reviewer's unique findings were structural.

## Why it works

The structured output gives the reviewer a **map** of all 20 functions before it reads the diff. Instead of scanning 974 lines linearly, it knows the shape of the change and allocates attention to the important parts.

This is the core value: **structural maps shift AI reviewers from line-level scanning to design-level analysis.**

## Honest assessment

GitScope's value scales with PR size:

| PR size | Value added |
|---------|------------|
| Small (<100 lines, 1-2 files) | **5-10%.** The reviewer can read the whole diff easily. GitScope adds marginal context. |
| Medium (200-500 lines) | **15-20%.** Structural overview saves the reviewer from getting lost. |
| Large (500+ lines, multiple files) | **20-30%.** The map is essential. Linear reading of 1000+ lines of diff misses structural patterns. |

GitScope is not magic. On small, focused PRs, you don't need it. On large, multi-file changes — the kind where reviewers most often miss design issues — it earns its keep.

## Philosophy: accuracy over comprehensiveness

GitScope reports structural facts: what functions changed, what signatures broke, what moved. It does **not** assess risk, rate severity, or guess intent. Those are opinions, and opinions can be wrong.

A tool that says "this change is high risk" and is wrong erodes trust fast. A tool that says "authenticate() was removed and authenticateOAuth() was added with a different signature" is always right — and the reviewer can draw their own conclusions.

This is deliberate. Wrong summaries are worse than no summaries.

