Metadata-Version: 2.4
Name: gapmap-ai
Version: 0.2.2
Summary: Gapmap scans a repository to find the "Gaps", the dangerous, load-bearing parts of a codebase that are highly complex but have zero human documentation. It then compiles those risks into machine-readable guardrails so that AI coding assistants (like Cursor) are physically blocked from breaking your architecture.
Author: Utkrisht12
License-Expression: MIT
Project-URL: Homepage, https://github.com/Utkrisht12/GapMaP
Project-URL: Repository, https://github.com/Utkrisht12/GapMaP
Project-URL: Issues, https://github.com/Utkrisht12/GapMaP/issues
Project-URL: Documentation, https://github.com/Utkrisht12/GapMaP#readme
Keywords: documentation,static-analysis,python,adr,technical-debt
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Documentation
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: typer>=0.12
Requires-Dist: rich>=13.0
Requires-Dist: networkx>=3.0
Requires-Dist: tree-sitter>=0.22
Requires-Dist: tree-sitter-python>=0.23
Requires-Dist: numpy<2.0,>=1.24
Provides-Extra: vector
Requires-Dist: sentence-transformers<3.0,>=2.7.0; extra == "vector"
Requires-Dist: faiss-cpu<2.0,>=1.7.4; extra == "vector"
Provides-Extra: llm
Requires-Dist: openai<2.0,>=1.30; extra == "llm"
Provides-Extra: mcp
Requires-Dist: fastmcp<4.0,>=2.0; extra == "mcp"
Provides-Extra: dev
Requires-Dist: pytest<9.0,>=8.0; extra == "dev"
Provides-Extra: build
Requires-Dist: pyinstaller<7.0,>=6.0; extra == "build"
Provides-Extra: all
Requires-Dist: sentence-transformers<3.0,>=2.7.0; extra == "all"
Requires-Dist: faiss-cpu<2.0,>=1.7.4; extra == "all"
Requires-Dist: openai<2.0,>=1.30; extra == "all"
Requires-Dist: fastmcp<4.0,>=2.0; extra == "all"
Requires-Dist: pytest<9.0,>=8.0; extra == "all"
Dynamic: license-file

# GapMap

Find the riskiest undocumented code in a **Python** repository, generate
machine-readable **Context Manifests** for AI agents, and answer questions about
the codebase with grounded, cited retrieval.

| Component | Path | What it does |
|-----------|------|--------------|
| **GapMap CLI** | `gapmap/` | Entity call graph, documentation-debt scoring, manifest generation |
| **GapMap Shield** | `gapmap/mcp_server.py` | MCP server exposing manifests to IDE agents |
| **RAG pipeline** | `gapmap/src/`, `main.py` | Semantic search and grounded Q&A over repo text |
| **Dashboard** | `dashboard/` | Next.js UI over `.gapmap/audit.json` |

## Enterprise use case

When a senior engineer leaves, undocumented load-bearing code does not have to
become an outage. GapMap turns departure into a **risk-reduction pipeline**:
audit the blast radius, encode tacit knowledge into manifests, and gate AI-assisted
edits through MCP.

```mermaid
flowchart LR
    subgraph S1["① Senior engineer departure"]
        direction TB
        D1["Primary author offboards"]
        D2["High-centrality legacy entity<br/>e.g. payment_router.py::route_payment"]
        D3["Undocumented · bus-factor = 1"]
        D1 --> D2 --> D3
    end

    subgraph S2["② GapMap ownership audit"]
        direction TB
        A1["gapmap audit · gapmap ownership"]
        A2["tree-sitter AST → entity call graph"]
        A3["git blame → churn · temporal risk"]
        A4["Risk score = callers × LOC × git factor"]
        A5["Isolates CRITICAL asset → .gapmap/audit.json"]
        A1 --> A2 & A3 --> A4 --> A5
    end

    subgraph S3["③ Automated handover"]
        direction TB
        H1["gapmap generate &lt;entity&gt;"]
        H2["Context manifest JSON"]
        H3["upstream_callers · source_hash<br/>ai_execution_invariants"]
        H1 --> H2 --> H3
    end

    subgraph S4["④ GapMap Shield (FastMCP)"]
        direction TB
        M1["Junior engineer + IDE agent<br/>attempts refactor"]
        M2["get_execution_invariants<br/>scan_workspace_risks"]
        M3["Invariants injected pre-edit<br/>implicit contracts enforced"]
        M4["Constrained change · no regression"]
        M1 --> M2 --> M3 --> M4
    end

    S1 ==> S2 ==> S3 ==> S4
```

<details>
<summary>ASCII version (plain-text viewers)</summary>

```
Senior engineer          GapMap CLI audit              Automated handover           MCP Shield (FastMCP)
departure                + ownership                                             
    │                         │                            │                         │
    ▼                         ▼                            ▼                         ▼
┌──────────────┐      ┌──────────────────┐        ┌─────────────────┐      ┌──────────────────────┐
│ Offboard     │      │ AST call graph   │        │ gapmap generate │      │ Agent edit request   │
│ High inbound │ ───► │ git blame/churn  │ ─────► │ .gapmap/        │ ───► │ get_execution_       │
│ callers      │      │ bus-factor flag  │        │ manifests/*.json│      │ invariants()         │
│ Undocumented │      │ CRITICAL entity  │        │ execution       │      │ → safety params      │
└──────────────┘      └──────────────────┘        │ invariants      │      │ → regression avoided │
                                                  └─────────────────┘      └──────────────────────┘

Outcome: knowledge loss contained · tacit contracts machine-readable · AI edits grounded in graph + git signals
```

</details>

Configure departed authors via `GAPMAP_DEPARTED_CONTRIBUTORS` or
`.gapmap.toml` → `[ownership]` (see [Configuration](#configuration)) before
running the audit.

## Table of contents

- [Enterprise use case](#enterprise-use-case)
- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [Run on any Python repo](#run-on-any-python-repo)
- [GapMap CLI](#gapmap-cli)
- [Context manifests](#context-manifests)
- [GapMap Shield (MCP)](#gapmap-shield-mcp)
- [Documentation automation](#documentation-automation)
- [Risk dashboard](#risk-dashboard)
- [Semantic search and Q&A](#semantic-search-and-qa)
- [Configuration](#configuration)
- [CI workflows](#ci-workflows)
- [Development](#development)
- [Publishing to PyPI](#publishing-to-pypi)
- [Architecture](#architecture)

## Prerequisites

| Requirement | Needed for |
|-------------|------------|
| **Python 3.10+** | All commands |
| **Git** (recommended) | `git blame` risk signals, ownership, trend history |
| **Node.js 18+ & npm** | `gapmap dashboard` only (not required for audit/CLI) |
| **OPENAI_API_KEY** (optional) | LLM-polished invariants and Q&A (`pip install "gapmap-ai[llm]"`) |

GapMap analyzes **Python repos only** today. Point `--repo` at the root of the
project you want to scan (where your `.py` files live).

## Installation

### Option A — PyPI or uvx (recommended for users)

No clone required. Installs the CLI only (dashboard and MCP server need Option B
for development, or run MCP via `python -m gapmap.mcp_server` after install).

```bash
# Zero-install — runs in a temporary env (requires uv: https://docs.astral.sh/uv/)
uvx gapmap-ai audit --repo /path/to/your/project

# Or install permanently
pip install gapmap-ai
gapmap audit --repo /path/to/your/project

# Full features (vector search + LLM + MCP + tests)
pip install "gapmap-ai[all]"
```

Extras: `pip install "gapmap-ai[vector]"`, `"gapmap-ai[llm]"`, or `"gapmap-ai[mcp]"`.

### Option B — Clone and install (contributors / dashboard / MCP)

```bash
git clone https://github.com/Utkrisht12/GapMaP.git
cd GapMaP

python3 -m venv .venv
source .venv/bin/activate          # Windows: .venv\Scripts\activate

pip install -e ".[all]"
```

`[all]` installs the CLI, vector search (FAISS), LLM support, FastMCP, and
pytest. For a minimal audit-only install:

```bash
pip install -e .
```

### Option C — Install from Git without cloning (CLI only)

```bash
pip install "gapmap-ai[all] @ git+https://github.com/Utkrisht12/GapMaP.git"
```

The dashboard still requires a full clone (Option B) because `gapmap dashboard`
runs the Next.js app from the `dashboard/` directory in this repo.

### Verify

```bash
gapmap --help
# or
gapmap-ai --help
```

## Run on any Python repo

Replace `/path/to/your/project` with the repository you want to audit.

```bash
# 1. Scan structure and call-graph quality
gapmap parse --repo /path/to/your/project

# 2. Top undocumented risks (writes .gapmap/audit.json)
gapmap audit --repo /path/to/your/project

# 3. Explain why a specific entity is risky
gapmap ask route_payment "why is this risky?" --repo /path/to/your/project

# 4. Generate a context manifest for the top gap (or a named entity)
gapmap generate --repo /path/to/your/project

# 5. Optional — open the risk dashboard (requires Node.js + repo clone)
gapmap dashboard --repo /path/to/your/project
# → http://localhost:3000
```

**Audit this repo** after install:

```bash
gapmap audit --repo .
gapmap dashboard --repo .
```

GapMap works fully offline for audit, parse, generate, stale, ownership, and
trend. Vector search and LLM polish need the optional extras (`[vector]`,
`[llm]`).

## GapMap CLI

GapMap parses every Python file with tree-sitter, builds an **entity-level call
graph** (NetworkX), scores each entity with weighted incoming callers × LOC × a
temporal git factor, checks which risky entities are missing from docs and
docstrings, and turns the result into audits, explanations, context manifests,
and an HTML report.

### All commands

| Command | Purpose |
|---------|---------|
| `gapmap parse` | Files, LOC, imports, graph resolution breakdown |
| `gapmap audit` | Top undocumented high-risk entities |
| `gapmap ask` | Graph-grounded explanation for one entity |
| `gapmap generate` | Generate context manifest(s) under `.gapmap/manifests/` |
| `gapmap report` | Self-contained HTML report |
| `gapmap dashboard` | Interactive risk UI (clone + Node.js) |
| `gapmap stale` | Find manifests whose source has drifted |
| `gapmap ownership` | Knowledge-debt ownership and bus-factor |
| `gapmap trend` | Documentation coverage over time |
| `gapmap autodoc` | Batch-generate manifests for critical hubs (CI / auto-PR) |

Common flags: `--repo PATH` (default: `.`), `--refresh` (ignore `.gapmap_cache`).

### Example session

```bash
export REPO=/path/to/your/project

gapmap parse --repo "$REPO"
gapmap audit --repo "$REPO" --top 15
gapmap ask payment_router.py "why is this risky?" --repo "$REPO"
gapmap generate route_payment --repo "$REPO"
gapmap report --repo "$REPO" -o gapmap-report.html
```

### What makes the graph accurate

- **Import-aware call resolution.** `from auth import Handler` resolves to the
  imported entity, not every same-named class. Edges carry `confidence` and
  `resolution` (symbol, qualified, same-file, import, ambiguous).
- **Smarter documentation detection.** `MCPClient` matches "MCP client";
  single-word names stay strict to avoid false positives.
- **Temporal risk from git history.** Author churn and bus-factor signals from
  `git blame` amplify volatile or single-owner hotspots.

## Context manifests

`gapmap generate` writes **hybrid JSON Context Manifests** to
`.gapmap/manifests/<entity_name>.json` in the **target repository** (not inside
the GapMap package). Files are overwritten in place so agents always read current
ground truth.

Example: `gapmap generate route_payment --repo /path/to/project` creates
`.gapmap/manifests/route_payment.json`.

### Schema

```json
{
  "metadata": {
    "entity": "payment_router.py::route_payment",
    "generated_date": "2026-06-18",
    "source_hash": "a1b2c3d4e5f6",
    "source_loc": 142
  },
  "risk_profile": {
    "score": 500,
    "rank_overall": 1,
    "rank_undocumented": 1,
    "risk_level": "CRITICAL",
    "git_metrics": {
      "primary_author": "alice",
      "days_since_last_edit": 12,
      "recent_commit_messages": ["fix routing edge case"]
    }
  },
  "systemic_role": {
    "upstream_callers": ["a.py::invoke"],
    "downstream_callees": ["auth.py::check"]
  },
  "ai_execution_invariants": "## Structural Constraints\n* ..."
}
```

- **Structured fields** (`metadata`, `risk_profile`, `systemic_role`) are
  deterministic and machine-parseable.
- **`ai_execution_invariants`** is a markdown string: execution constraints,
  implicit contracts, and safety rules for an AI coding assistant.
- With `[llm]` and `OPENAI_API_KEY`, invariants are synthesized by GPT from
  source, callers, and git history. Offline, a deterministic template is used.

Manifests embed `source_hash` and `source_loc` so `gapmap stale` detects when
code has drifted away from the registered invariants.

## GapMap Shield (MCP)

**GapMap Shield** exposes context manifests to IDE agents over the Model Context
Protocol (stdio). It ships a `gapmap-mcp` console entry point so launchers can
start it without you having to manage a Python interpreter.

### Tools

| Tool | When to use |
|------|-------------|
| `get_execution_invariants(repo_root, entity_name)` | Before modifying or refactoring an entity — returns risk level and `ai_execution_invariants` |
| `scan_workspace_risks(repo_root)` | When orienting in a new repo — lists all HIGH and CRITICAL entities |

### Add to Cursor (one click)

[![Add to Cursor](https://img.shields.io/badge/Add%20to-Cursor-000?style=for-the-badge&logo=cursor)](cursor://anysphere.cursor-deeplink/mcp/install?name=gapmap-shield&config=eyJjb21tYW5kIjogInV2eCIsICJhcmdzIjogWyItLWZyb20iLCAiZ2FwbWFwLWFpW21jcF0iLCAiZ2FwbWFwLW1jcCJdfQ==)

> The button installs the `uvx` configuration below. Requires
> [`uv`](https://docs.astral.sh/uv/) to be installed.

### Cursor / IDE config

**Recommended — `uvx` (no manual Python setup):**

[`uv`](https://docs.astral.sh/uv/) fetches and runs `gapmap-ai[mcp]` in an
isolated environment, so there is no "which Python?" problem and no separate
install step.

```json
{
  "mcpServers": {
    "gapmap-shield": {
      "command": "uvx",
      "args": ["--from", "gapmap-ai[mcp]", "gapmap-mcp"]
    }
  }
}
```

Pin a version if you prefer: `"--from", "gapmap-ai[mcp]==0.2.2"`.

**Fallback — explicit Python interpreter:**

If you don't use `uv`, install the MCP extra and point Cursor at the **absolute
path** of the interpreter you installed it into:

```bash
pip install "gapmap-ai[mcp]"
which python    # copy this absolute path into the config below
```

```json
{
  "mcpServers": {
    "gapmap-shield": {
      "command": "/absolute/path/to/python",
      "args": ["-m", "gapmap.mcp_server"]
    }
  }
}
```

**Zero-dependency — standalone binary:**

For machines without Python at all, download the prebuilt `gapmap-mcp` binary for
your OS from the [Releases page](https://github.com/Utkrisht12/GapMaP/releases)
and point Cursor directly at it:

```json
{
  "mcpServers": {
    "gapmap-shield": {
      "command": "/absolute/path/to/gapmap-mcp"
    }
  }
}
```

> **⚠️ Cursor integration note (`spawn python ENOENT`):** Cursor runs local MCP
> servers in a detached shell context that often cannot resolve a bare `"python"`
> command. If you see a red dot or a connection failure, use the **absolute path**
> to your active Python interpreter (e.g. `/Users/yourname/miniforge3/bin/python`),
> or switch to the `uvx` configuration above.
>
> GapMap Shield also suppresses the FastMCP startup banner on stderr (`show_banner=False`)
> so Cursor does not treat banner output as a connection error.

Point agents at the target repo's `repo_root` when calling tools. Manifests must
exist under `{repo_root}/.gapmap/manifests/` (create them with `gapmap generate`
first).

## Documentation automation

```bash
export REPO=/path/to/your/project

# Batch-generate manifests for documentation gaps
gapmap generate --all-critical --repo "$REPO"
gapmap generate --top 10 --repo "$REPO"
gapmap generate --min-score 300 --repo "$REPO"

# Drift detection (exits non-zero in CI when stale manifests exist)
gapmap stale --repo "$REPO"
gapmap stale --fix --repo "$REPO"

gapmap ownership --repo "$REPO"
gapmap trend --repo "$REPO"
gapmap autodoc --regenerate-stale --repo "$REPO"
```

## Risk dashboard

Requires **Node.js**, a **clone of this repo**, and an audit JSON file.

```bash
# From the GapMap repo root, after pip install -e ".[all]"
gapmap audit --repo /path/to/your/project
gapmap dashboard --repo /path/to/your/project --port 3000
```

Opens http://localhost:3000 with risk scores, call graph, coverage trends, stale
manifests, ownership, and graph-quality metrics from `.gapmap/audit.json`.

**UI-only** (mock data, no audit):

```bash
cd dashboard
npm install    # first time only
NEXT_PUBLIC_GAPMAP_USE_MOCK=true npm run dev
```

See [`dashboard/README.md`](dashboard/README.md) for component details.

## Semantic search and Q&A

Separate from `gapmap ask`: `main.py` runs repo-wide semantic retrieval (not
entity-specific). Requires `[vector]` extra and a source checkout (`main.py` is
not shipped as a PyPI console command).

```bash
pip install -e ".[vector]"   # or .[all]

python main.py --repo /path/to/your/project -v
python main.py --repo /path/to/your/project --query "How does caching work?"
python main.py --repo /path/to/your/project --ask "How does caching work?"
python main.py --repo /path/to/your/project --interactive
python main.py --repo /path/to/your/project --save .cache/gapmap-index
```

| | `gapmap ask` | `main.py --query` | `main.py --ask` |
|---|--------------|-------------------|-----------------|
| **Scope** | One entity | Whole repo | Whole repo |
| **Output** | Risk explanation | Raw chunks + scores | Synthesized answer + citations |
| **Context** | Call graph + risk + optional vectors | Vectors only | Vectors + synthesis |

Without `openai` or an API key, synthesis falls back to a deterministic
extractive engine (works offline).

```bash
pip install -e ".[llm]"
export OPENAI_API_KEY=sk-...
python main.py --repo . --ask "How does staleness detection work?"
```

## Configuration

Copy [`.gapmap.toml.example`](.gapmap.toml.example) to `.gapmap.toml` in the
**target repo** you are auditing:

```toml
[policy]
critical_threshold = 500
auto_document = true
regenerate_when_stale = true

[docs]
output_dir = ".gapmap/manifests"
min_score = 100

[ownership]
departed_contributors = ["alice", "bob@corp.com"]
```

### Optional dependencies

```bash
pip install "gapmap-ai[vector]"   # RAG
pip install "gapmap-ai[llm]"      # LLM invariants and Q&A
pip install "gapmap-ai[mcp]"      # GapMap Shield MCP server
pip install "gapmap-ai[all]"      # full install
pip install -e ".[all]"           # from source (contributors)
```

| Extra | Enables |
|-------|---------|
| `vector` | RAG indexing and search (`sentence-transformers`, `faiss-cpu`) |
| `llm` | LLM-backed execution invariants and Q&A (`openai`) |
| `mcp` | GapMap Shield MCP server (`fastmcp`) |
| `dev` | Test suite (`pytest`) |
| `all` | Everything above |

## CI workflows

| Workflow | Purpose | Trigger |
|----------|---------|---------|
| [`gapmap-sentinel.yml`](.github/workflows/gapmap-sentinel.yml) | Fail on new critical undocumented hubs | Manual only |
| [`gapmap-autodoc.yml`](.github/workflows/gapmap-autodoc.yml) | Open a PR with generated manifests | Manual only |

Both use `workflow_dispatch` while the project is under construction. Uncomment
`push` / `pull_request` in each file when ready.

```bash
gapmap audit --repo . --json-export report.json
```

Writes CI export JSON; `.gapmap/audit.json` is always updated for the dashboard.

## Development

```bash
git clone https://github.com/Utkrisht12/GapMaP.git && cd GapMaP
pip install -e ".[all]"
pytest
```

Tests use a deterministic fake encoder — no network or model downloads.

### Project layout

```
.
├── gapmap/                 CLI, analysis engine, MCP server
│   └── mcp_server.py       GapMap Shield (FastMCP)
├── dashboard/              Next.js risk dashboard
├── docs/                   ARCHITECTURE.md
├── tests/                  pytest suite
├── main.py                 RAG pipeline entry point (source checkout only)
└── .gapmap.toml.example    policy template
```

Target repos receive generated artifacts under `.gapmap/`:

```
/path/to/your/project/
└── .gapmap/
    ├── audit.json          machine-readable audit (from `gapmap audit`)
    ├── history.jsonl       coverage trend (from `gapmap trend`)
    └── manifests/          context manifests (from `gapmap generate`)
        └── route_payment.json
```

## Publishing to PyPI

Package name on PyPI: **`gapmap-ai`** (console commands: `gapmap` and
`gapmap-ai`). Published at https://pypi.org/project/gapmap-ai/

```bash
pip install -U packaging build twine
python -m build

# Production (username is always __token__)
export TWINE_USERNAME=__token__
export TWINE_PASSWORD='pypi-...'   # your API token — never commit this
twine upload dist/*
```

Bump `version` in `pyproject.toml` before each upload — PyPI does not allow
overwriting an existing release.

After publish, users can run:

```bash
uvx gapmap-ai audit --repo /path/to/your/project
pip install gapmap-ai && gapmap audit --repo /path/to/your/project
```

Push to GitHub does **not** update PyPI automatically; publish is manual (or add
a GitHub Actions release workflow later).

## Architecture

Module-level diagrams, data flow, and CI details:
[`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md).
