Metadata-Version: 2.4
Name: whyfile
Version: 0.8.2
Summary: Whyfile: intent-driven development for your codebase. Extract the decisions, constraints, and trade-offs behind your code as grounded, anchored graph nodes, and query them back.
Project-URL: Homepage, https://github.com/whyfile/whyfile
Project-URL: Repository, https://github.com/whyfile/whyfile
Project-URL: Issues, https://github.com/whyfile/whyfile/issues
Project-URL: Changelog, https://github.com/whyfile/whyfile/blob/main/CHANGELOG.md
Author-email: Will Neill <willneill@gmail.com>
License: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Keywords: claude,claude-code,code-intelligence,documentation,embeddings,graphify,intent,knowledge-graph,llm,rag,static-analysis
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Documentation
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Classifier: Topic :: Software Development :: Documentation
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: graphifyy>=0.9.3
Requires-Dist: rapidfuzz>=3
Provides-Extra: anthropic
Requires-Dist: graphifyy[anthropic]; extra == 'anthropic'
Provides-Extra: bedrock
Requires-Dist: graphifyy[bedrock]; extra == 'bedrock'
Provides-Extra: dev
Requires-Dist: mcp<2,>=1.19; extra == 'dev'
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: networkx>=3; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-cov>=5; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Provides-Extra: embeddings
Requires-Dist: model2vec>=0.3; extra == 'embeddings'
Provides-Extra: gemini
Requires-Dist: graphifyy[gemini]; extra == 'gemini'
Provides-Extra: mcp
Requires-Dist: mcp<2,>=1.19; extra == 'mcp'
Description-Content-Type: text/markdown

# Whyfile

[![PyPI version](https://img.shields.io/pypi/v/whyfile.svg)](https://pypi.org/project/whyfile/)
[![Python versions](https://img.shields.io/pypi/pyversions/whyfile.svg)](https://pypi.org/project/whyfile/)
[![License: Apache-2.0](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](LICENSE)
[![CI](https://github.com/whyfile/whyfile/actions/workflows/ci.yml/badge.svg)](https://github.com/whyfile/whyfile/actions/workflows/ci.yml)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)

Your codebase remembers **what** it does. It forgot **why.**

`whyfile` reconstructs that why, keeps it honest — every unit tagged `authored` or `reconstructed`, so a model's guess never poses as a decision you made — and puts it on a gate that fails the PR quietly breaking it. Every repo has a Makefile; this is the missing Whyfile. (Formerly `graphify-intent`; see ADR-0024.)

## Why "Whyfile"?

Every repo has a Makefile that says how to build, a Dockerfile that says how to run, and a lockfile that says exactly what ships. None of them say why. Whyfile is the missing file: the decisions, constraints, and trade-offs behind your code, extracted into a queryable graph, anchored to the code they govern, and kept honest by provenance tiers that separate what a human recorded from what a model reconstructed.

The name is also the roadmap. Intent-driven development has one invariant: no unexplained change. Every change either conforms to recorded intent, supersedes it explicitly, or records a new decision. The conformance gate (`whyfile intent-diff --gate`) enforces it: a pull request that silently violates a recorded constraint does not merge. The why goes on file, and the file bears load.

## What it does

`whyfile` reconstructs the *why* your code lost: the decisions, the dead ends, and the constraints the awkward bits are quietly paying for — the reasoning that never became a comment because nobody writes `# WHY:` above the thing they decided *not* to do. Surfacing rationale that's already written down is table stakes; two things make *reconstructed* intent trustworthy instead of plausible-sounding fiction:

- **It's kept honest by provenance.** Every unit is tagged `authored` (a human recorded it) or `reconstructed` (a model inferred it), grounded to the exact source span it came from, so a model's guess never outranks the record or poses as a decision you made.
- **It's load-bearing, not decorative.** `whyfile intent-diff --gate` fails a pull request that changes constraint-governed code without conforming to the constraint or explicitly superseding the decision behind it. The why doesn't just sit in a graph you query — it can block your merge.

Under the hood it reads a [graphify](https://github.com/safishamsi/graphify) knowledge graph alongside your prose docs, runs a short pipeline of LLM passes (extract → anchor → cross-doc relate, plus an opt-in concept-resolution pass), and writes a sidecar JSON, an enriched `graph.json`, and a report. Three LLM-free query commands read it back. Think of it as the month of code archaeology every new hire does on their way in — done once, checked in, and enforced.

```mermaid
flowchart LR
    D["docs/*.md"] --> A
    G["graph.json"] --> B
    A["Pass A<br/>extract intent"] --> B["Pass B<br/>anchor to concepts"]
    B --> C["Pass C<br/>cross-doc intent"]
    B --> R["Pass D<br/>concept resolution<br/>(opt-in)"]
    A --> OUT
    C --> OUT
    R --> OUT
    OUT["outputs:<br/>.whyfile.json<br/>graph.enriched.json<br/>enrichment_report.md"]
```

## Quick start

```bash
# Prefers an API key (ANTHROPIC_API_KEY) — exact prompt tokens, visible cost.
# Falls back to your Claude Pro/Max subscription (needs the `claude` CLI).
whyfile \
  --graph graphify-out/graph.json \
  --docs docs/ \
  --passes A,B,C
```

## See it on this repo

We point the tool at itself, because it's the most honest demo we can give you. A full run over
this project's own [`docs/adr/`](docs/adr/) trail grows 172 code concepts into 234 nodes and pulls
**62 intent units** (18 decisions, 18 mechanisms, 16 constraints, 10 trade-offs) out of the prose,
each grounded to the exact span it came from (**100% grounding**), 53 anchored back to the code
they explain.

**Then the why bears load.** Open a PR that edits `cache.py` — code governed by four constraints
recorded in [ADR-0015](docs/adr/0015-cache-resume.md) — without accounting for them, and the gate
stops the merge:

```console
$ whyfile intent-diff --files src/whyfile/cache.py --gate
Intent diff: introduces 0, supersedes 0, governed 1, conformance-review 1
  ! src/whyfile/cache.py governed by constraint: Restores extraction, not re-sync, …
Conformance gate: action required — conform to each constraint, or supersede its decision record:
  ✗ src/whyfile/cache.py — Restores extraction, not re-sync (ADR-0015) → conform, or supersede ADR-0015
  ✗ src/whyfile/cache.py — Consistent --docs spelling required (ADR-0015) → conform, or supersede ADR-0015
  ✗ src/whyfile/cache.py — End-of-run sidecar write loses work (ADR-0015) → conform, or supersede ADR-0015
  ✗ src/whyfile/cache.py — Corrupt entry treated as miss (ADR-0015) → conform, or supersede ADR-0015
$ echo $?
5
```

That exit 5 *is* the product. The change conforms to each recorded constraint, or the same PR
supersedes ADR-0015 out loud — silently breaking a decision the code is built on is not a path the
merge leaves open.

**And it stays honest about what it knows.** Ask why the code looks the way it does, and every
answer wears its provenance — `reconstructed` (a model inferred it) never dressed up as `authored`
(a human recorded it):

```console
$ whyfile explain cache.py --format text
cache.py (code)
  • Orphan cache entries accumulate [reconstructed tradeoff] — Orphan cache entries accumulate
    over a corpus's lifetime with no automatic pruning.
    why: Deferring cleanup accepts unbounded cache growth to avoid deleting live entries, because
    auto-prune cannot safely distinguish orphaned entries from ones excluded by the current run.
    alternatives: Auto-prune or an immediate --prune-cache flag, deferred because --docs subsetting
    makes automatic pruning unsafe.
```

That rationale isn't a comment in `cache.py` — it lives in
[ADR-0015](docs/adr/0015-cache-resume.md), which the tool read, distilled, tagged `reconstructed`,
and wired back to the file the decision governs. Surfacing that round trip is table stakes.
**Gating on it — and never letting a model's guess pose as ground truth — is the product.** (The
query commands are covered in [Querying the intent layer](docs/guide/querying.md).)

## Team collaboration

whyfile turns the tribal "why is the code like this?" into a layer that is queryable,
PR-reviewable, and survives turnover. Three jobs a team gets for free (all LLM-free, $0 at query
time):

- **Onboard.** A new hire runs `whyfile explain <file>` instead of interrupting a senior.
- **Review.** `whyfile changed --base main` surfaces the intent a PR touches, so a
  reviewer sees the constraints a change is bound by; `whyfile review-context --base main`
  turns that into a brief an external reviewer like Codex can act on directly.
- **Retain.** `whyfile coverage` shows which code has recorded intent and which is dark,
  and `whyfile digest` reports what was decided since a checkpoint.

### The PR check

```bash
git diff --name-only origin/main...HEAD | whyfile changed --format markdown
```

Maps each changed file to the intent that governs it (constraints and trade-offs first). It is
informational by default (exit 0). Add `--fail-on-constraint` to turn it into a soft gate that
exits 5 when a PR touches a file bound by a constraint, so the constraint gets a reviewer's
acknowledgement.

## Development

```bash
git clone <repo>
cd whyfile
pip install -e ".[dev]"
python -m pytest tests/ -v
```

Tests cover every module: IDs, section splitting with spans, relation/confidence validation, all four passes (including Pass D's candidate resolution and embedding fallback), merge/enriched-graph assembly, the report, backend resolution, and an end-to-end smoke test. The LLM boundary and the graphify-runtime probe are injected/mocked, so the suite makes no network calls and needs no API key or live backend.

## Building the plane while we fly it

Structure tools map imports and call graphs: the *what* and the *how-it-connects*. What they
can't hand you is the reasoning: which decision a file is the consequence of, what got tried and
thrown away, why the ugly workaround earns its keep. `whyfile` is a thin *reasoning* layer
over the structural graph graphify already builds: a decision/strategy view, not another entity
extractor, and deliberately **not** a vector DB or a RAG stack (that would be a much heavier
machine than the job needs).

This whole [`docs/adr/`](docs/adr/) trail is fair game — decisions, rationale, and the
alternatives considered — written by hand *as we built it*, so the reasoning actually moves:

- **[ADR-0002](docs/adr/0002-intent-rationale-wedge-and-data-model.md)** bets the whole tool on
  intent, not generic concept extraction.
- **[ADR-0004](docs/adr/0004-embedding-free-same-file-anchoring.md)** ships v1
  embedding-free and defers cross-document resolution, which
  **[ADR-0009](docs/adr/0009-cross-document-concept-resolution.md)** later delivers.
- **[ADR-0010](docs/adr/0010-graphify-hard-runtime-dependency.md)** openly supersedes an earlier
  README claim after a clean install exposed a silently broken package.
- **[ADR-0011](docs/adr/0011-cheaper-intent-passes.md)** catches extraction quietly running on
  Opus and puts it on a budget.

That recurring beat — a decision that doesn't survive contact with the code — is the whole reason
this tool exists.

## Explore the docs

- **[Installation](docs/guide/installation.md)** — requirements, install paths, LLM backend setup.
- **[CLI reference](docs/guide/cli-reference.md)** — every flag, exit code, and output file.
- **[Caching, resume, and cost](docs/guide/caching-and-cost.md)** — what a run costs, on either backend.
- **[Querying the intent layer](docs/guide/querying.md)** — `explain`, `list-intent`, `why`.
- **[Gates](docs/guide/gates.md)** — the audit panel's convergence gate, `intent-diff`, and `check`.
- **[Team collaboration](docs/guide/team-collaboration.md)** — `changed`, `coverage`, `digest`,
  `drift`, `affirm`, `trust`, and `review-context`.
- **[Configuring whyfile](docs/guide/configuration.md)** — the `whyfile.config.json` artifact.
- **[MCP server](docs/guide/mcp-server.md)** — expose the query layer to IDEs and coding agents.
- **[How it works](docs/guide/how-it-works.md)** — the extraction pipeline's Pass A-D mechanics.
- **[Architecture decisions](docs/adr/)** — every ADR behind this tool, in the order they were made.
- **[Decision records](docs/decisions/)** — the everyday, `captured`-tier decisions this repo makes
  about itself, including this rename.

## License and attribution

`whyfile` is licensed under the [Apache License 2.0](LICENSE), Copyright © 2026
[Four Birds Limited](NOTICE) (New Zealand), which maintains the project. The name
"whyfile" is a trademark of Four Birds Limited — code is free under Apache-2.0; naming
and compatibility claims are governed by the [trademark policy](TRADEMARKS.md).

This project is an independent post-processor built to interoperate with
[graphify](https://github.com/safishamsi/graphify) by Safi Shamsi. It reuses graphify's
graph schema and relation vocabulary and calls graphify as a separately-installed runtime
dependency; no graphify source code is bundled with or distributed as part of this project.
graphify is licensed under the MIT License (Copyright (c) 2026 Safi Shamsi); see the
`ACKNOWLEDGEMENT AND ATTRIBUTION` section of this repository's [NOTICE](NOTICE) file and
the [upstream license](https://github.com/safishamsi/graphify/blob/v8/LICENSE) for the
full text. With thanks to the graphify project.
