Metadata-Version: 2.4
Name: whyfile
Version: 0.8.3
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)
[![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. **Everything below
reproduces for $0**: `graphify update .` then `whyfile sync` builds the deterministic, LLM-free
tier in seconds — no API key, no reconstruction run. On this repo at v0.8.2 (2026-07-21) that
yields its whole intent layer from its own [`docs/adr/`](docs/adr/) and
[`docs/decisions/`](docs/decisions/) trail — every unit `authored`, `captured`, or `attested`,
anchored back to the code it explains. The counts below grow as the decision trail does; the
shape is the point. (A full reconstruction run adds a `reconstructed`
tier on top; it costs money, and nothing on this page depends on it.)

**Then the why bears load.** [ADR-0016](docs/adr/0016-intent-query-layer.md) decided the query
layer stays lexical — no embeddings, no graph traversal. That constraint is compiled into
[`intent-rules.json`](intent-rules.json). Add `import numpy` to `src/whyfile/query.py` and the
gate stops the merge:

```console
$ whyfile check --format text
Conformance check: 1 rule(s), 1 file(s) checked.
  ✗ src/whyfile/query.py imports 'numpy': violates ADR-0016 (No mutation, traversal, or embeddings). the read-only query layer stays lexical: no embedding retrieval and no graph-traversal library. Disagree? Supersede the record, do not fight the linter.
$ echo $?
5
```

That exit 5 *is* the product. The change conforms to the recorded constraint, or the same PR
supersedes ADR-0016 out loud — silently breaking a decision the code is built on is not a path the
merge leaves open. Reproduce it yourself: `whyfile sync` (LLM-free, seconds, $0), make that
one-line edit, run the command above.

**And it stays honest about what it knows.** Every unit wears its provenance, and the tiers are
counted separately — a model's guess is never folded into the trusted number:

```console
$ whyfile coverage --format text
Intent coverage
  Files:   3/121 (2%)
  Symbols: 3/2116 (0%)
  By kind: decision 71 · assumption 20
  Golden fraction: 100% (91/91 trusted) by provenance: authored 58 · captured 28 · attested 5
  Intent debt: 97 dark file(s), 62 orphaned intent node(s) (stale: see `drift`; disputes: audit phase)
```

100% here is not a boast — it is what "no reconstruction has been run" looks like, stated plainly.
Run the paid pass and a `reconstructed` tier appears in that same breakdown, **outside** the
trusted count, never blended into it. The tool would rather report a small honest number than a
large blended one. **Gating on the why — 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.
