diffctx 1.12.2 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -77
- package/checksums.json +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,22 +10,16 @@
|
|
|
10
10
|
Instead of pasting whole files, it walks the dependency graph outward from the
|
|
11
11
|
changed lines and stops once more context stops paying for itself.
|
|
12
12
|
|
|
13
|
-
>
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
| Works with any LLM | ✓ | ✓ | Claude only | ✓ |
|
|
24
|
-
| Free / local / offline | ✓ | ✓ | $15–25/review | ✓ |
|
|
25
|
-
| GitHub required | ✗ | ✗ | ✓ | ✗ |
|
|
26
|
-
| Multiple output formats | ✗ | limited | — | YAML/JSON/MD/txt |
|
|
27
|
-
| Python API | ✗ | ✗ | ✗ | ✓ |
|
|
28
|
-
| MCP server | ✗ | ✗ | ✗ | ✓ |
|
|
13
|
+
> Formerly published as `treemapper` — every command, flag, and API call works unchanged.
|
|
14
|
+
|
|
15
|
+
## How it compares
|
|
16
|
+
|
|
17
|
+
Whole-repo packers (repomix and friends) seed on the repository and export
|
|
18
|
+
everything; persistent code-graph servers answer structural queries against a
|
|
19
|
+
maintained index. diffctx is **diff-seeded**: the input is a change, the
|
|
20
|
+
output is the fragments needed to understand it, packed under a hard token
|
|
21
|
+
budget — local, deterministic, no index, no model calls. Measured results and
|
|
22
|
+
when the other two families fit better: [COMPARISON.md](COMPARISON.md).
|
|
29
23
|
|
|
30
24
|
## Install
|
|
31
25
|
|
|
@@ -44,18 +38,18 @@ npx diffctx . --diff HEAD~1 # npm wrapper over the native binary
|
|
|
44
38
|
docker run --rm -v "$PWD:/repo" ghcr.io/nikolay-e/diffctx . --diff HEAD~1
|
|
45
39
|
```
|
|
46
40
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
41
|
+
On Windows, via Scoop (this repository is the bucket):
|
|
42
|
+
|
|
43
|
+
```powershell
|
|
44
|
+
scoop bucket add diffctx https://github.com/nikolay-e/diffctx
|
|
45
|
+
scoop install diffctx/diffctx
|
|
46
|
+
```
|
|
50
47
|
|
|
51
48
|
Prebuilt binaries for linux (x86_64/aarch64), macOS (arm64) and Windows (x64)
|
|
52
49
|
are attached to every [release](https://github.com/nikolay-e/diffctx/releases/latest).
|
|
53
|
-
The native binary
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
imported as `_diffctx` (`use _diffctx::pipeline::build_diff_context`), since
|
|
57
|
-
the crate doubles as the Python extension module
|
|
58
|
-
([docs.rs](https://docs.rs/diffctx)).
|
|
50
|
+
The native binary and Docker image cover diff mode with YAML/JSON output and
|
|
51
|
+
write to stdout (redirect to capture); tree mode, Markdown output, the `graph`
|
|
52
|
+
subcommand and the MCP server live in the Python package.
|
|
59
53
|
|
|
60
54
|
## Quick start
|
|
61
55
|
|
|
@@ -77,20 +71,24 @@ builds a code graph (imports, co-changes, type refs), propagates relevance
|
|
|
77
71
|
outward from the changed lines, and stops when relevance drops below `--tau` or
|
|
78
72
|
the `--budget` token cap is hit.
|
|
79
73
|
|
|
74
|
+
`--diff` takes a git range (`HEAD~1..HEAD`, `main..feature`) or a **duration
|
|
75
|
+
window ending now** — `24h`, `8d`, `90min`, `1h30m`, `2w` (units `s`, `m`/`min`,
|
|
76
|
+
`h`, `d`, `w`, composable). A window diffs the working tree against the last
|
|
77
|
+
commit before it, so it covers the commits made inside the window *plus* the
|
|
78
|
+
uncommitted and untracked work on top — `diffctx . --diff 24h` is "everything I
|
|
79
|
+
touched today". A ref that happens to look like a duration (a branch `24h`)
|
|
80
|
+
keeps its git meaning.
|
|
81
|
+
|
|
80
82
|
| Flag | Default | Description |
|
|
81
83
|
|-------------|---------|--------------------------------------------------------------------------|
|
|
82
|
-
| `--scoring` | `ego` | `ego` = bounded expansion around changed nodes (fast, predictable radius); `ppr` = Personalized PageRank (global, smoother decay, slower); `bm25` = lexical retrieval against the diff hunks (baseline for sparse graphs) |
|
|
83
|
-
| `--budget` | auto | Hard token
|
|
84
|
+
| `--scoring` | `ego` | `ego` = bounded expansion around changed nodes (fast, predictable radius); `ppr` = Personalized PageRank (global, smoother decay, slower); `bm25` = lexical retrieval against the diff hunks (baseline for sparse graphs); `rrf` = reciprocal-rank fusion of `ego` and `bm25` (widest recall, no scale calibration between the two signals) |
|
|
85
|
+
| `--budget` | auto | Hard cap in o200k_base tokens (see [Token counting](docs/product/token-budget.md)): `N` enforces a fixed cap, `-1` disables it, `0` is a strict-zero floor (empty selection; use `--full` for changed files only) |
|
|
84
86
|
| `--alpha` | 0.60 | PPR damping; higher = context clusters tighter around changes (`--scoring ppr` only) |
|
|
85
87
|
| `--tau` | 0.12 | Relevance threshold for full fragment content; lower-scoring fragments are stubbed or dropped (lower = more context) |
|
|
86
88
|
| `--full` | false | Only the changed files, every fragment, no related-code context |
|
|
87
89
|
| `--timeout` | 300 | Wall-clock deadline in seconds; on expiry diffctx exits 124 instead of hanging |
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
[`docs/engineering/parameter-strategy.md`](docs/engineering/parameter-strategy.md).
|
|
91
|
-
Theory:
|
|
92
|
-
[diffctx: Budgeted Typed-Graph Retrieval for Diff-Aware Code Context
|
|
93
|
-
Selection (Zenodo, 2026)](https://doi.org/10.5281/zenodo.18824579).
|
|
90
|
+
| `--with-raw-diff` | false | Also embed git's raw unified diff ahead of the selected fragments — additive (selection unchanged), not charged to `--budget`, lock/ignored/secret-like sections omitted. Python CLI only |
|
|
91
|
+
| `--mode` | `pack` | `locate` emits the same ranked selection as compact `diffctx.locate.v1` JSON — path, lines, score, provenance reasons, a blast-radius `summary` and per-item impact `group` (`test`/`type`/`config`), NO source bodies. Adds a `coverage` block naming what the run could not see (`unparsed_files`, `zero_edge_files`, `ppr_truncated`, `next_up`, a heuristic `confidence`) and an `overflow` ranking of what the budget left behind — omitted entirely when there is nothing to disclose. `diffctx . --diff --mode locate` = impact of your uncommitted change. The MCP tool takes it as `mode="locate"` |
|
|
94
92
|
|
|
95
93
|
### `graph` subcommand
|
|
96
94
|
|
|
@@ -108,28 +106,31 @@ diffctx graph . --level file -f graphml -o g.xml # file-level graph as GraphML
|
|
|
108
106
|
<!-- BEGIN USAGE -->
|
|
109
107
|
```bash
|
|
110
108
|
# full codebase export:
|
|
111
|
-
diffctx .
|
|
112
|
-
diffctx . -f md -c
|
|
113
|
-
diffctx . -f json -o tree.json
|
|
114
|
-
diffctx . --no-content
|
|
115
|
-
diffctx . --max-depth 3
|
|
116
|
-
diffctx . -i custom.ignore
|
|
109
|
+
diffctx . # Markdown to stdout + token count
|
|
110
|
+
diffctx . -f md -c # Markdown → clipboard
|
|
111
|
+
diffctx . -f json -o tree.json # JSON → file
|
|
112
|
+
diffctx . --no-content # structure only, no file contents
|
|
113
|
+
diffctx . --max-depth 3 # limit depth
|
|
114
|
+
diffctx . -i custom.ignore # custom ignore patterns
|
|
117
115
|
|
|
118
116
|
# diff context mode (requires git repo):
|
|
119
|
-
diffctx . --diff
|
|
120
|
-
diffctx . --diff HEAD~1
|
|
121
|
-
diffctx . --diff main..feature
|
|
122
|
-
diffctx . --diff
|
|
123
|
-
diffctx . --diff
|
|
117
|
+
diffctx . --diff # uncommitted changes (working tree vs HEAD)
|
|
118
|
+
diffctx . --diff HEAD~1 # context for last commit
|
|
119
|
+
diffctx . --diff main..feature # context for feature branch
|
|
120
|
+
diffctx . --diff 24h # everything changed in the last 24 hours
|
|
121
|
+
diffctx . --diff 8d # same over 8 days (also 90s, 10min, 1h30m, 2w)
|
|
122
|
+
diffctx . --diff HEAD~1 --budget 30000 # limit to ~30k tokens
|
|
123
|
+
diffctx . --diff HEAD~1 -c # diff context to clipboard
|
|
124
|
+
diffctx . --diff HEAD~1 --with-raw-diff # raw patch + selected context
|
|
125
|
+
diffctx . --diff HEAD~1 --mode locate # ranked navigation JSON, no source
|
|
124
126
|
```
|
|
125
127
|
<!-- END USAGE -->
|
|
126
128
|
|
|
127
129
|
Every run reports token count and size on stderr — `12,847 tokens
|
|
128
|
-
(o200k_base), 52.3 KB
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
`<unreadable content: not utf-8>`.
|
|
130
|
+
(o200k_base), 52.3 KB`. Counts are exact only for the GPT-4o family; Claude,
|
|
131
|
+
Gemini and others tokenize differently, so treat `--budget` as an upper bound
|
|
132
|
+
and leave headroom ([details](docs/product/token-budget.md)). Unreadable files
|
|
133
|
+
become placeholders like `<binary file: N bytes>`.
|
|
133
134
|
|
|
134
135
|
## Python API
|
|
135
136
|
|
|
@@ -146,6 +147,7 @@ ctx = build_diff_context(
|
|
|
146
147
|
full=False,
|
|
147
148
|
scoring_mode="ego",
|
|
148
149
|
timeout=300,
|
|
150
|
+
with_raw_diff=False, # True also embeds the raw unified diff (not charged to budget)
|
|
149
151
|
)
|
|
150
152
|
print(to_markdown(ctx))
|
|
151
153
|
|
|
@@ -163,19 +165,36 @@ print(to_yaml(tree))
|
|
|
163
165
|
|
|
164
166
|
## MCP server
|
|
165
167
|
|
|
168
|
+
[](https://registry.modelcontextprotocol.io/v0/servers?search=io.github.nikolay-e/diffctx)
|
|
169
|
+
[](https://glama.ai/mcp/servers/nikolay-e/diffctx)
|
|
170
|
+
|
|
166
171
|
diffctx includes an [MCP](https://modelcontextprotocol.io) server that lets AI
|
|
167
172
|
assistants (Claude Code, Cursor, Windsurf, etc.) call diff context analysis
|
|
168
|
-
automatically during code review.
|
|
169
|
-
|
|
173
|
+
automatically during code review. It is published in the official MCP registry
|
|
174
|
+
as `io.github.nikolay-e/diffctx`. One-line setup (zero-install via
|
|
175
|
+
[uv](https://docs.astral.sh/uv/)):
|
|
170
176
|
|
|
171
177
|
```bash
|
|
172
|
-
|
|
178
|
+
# Claude Code
|
|
179
|
+
claude mcp add diffctx -- uvx --from 'diffctx[mcp]' diffctx-mcp
|
|
180
|
+
# Codex CLI
|
|
181
|
+
codex mcp add diffctx -- uvx --from 'diffctx[mcp]' diffctx-mcp
|
|
182
|
+
# Gemini CLI
|
|
183
|
+
gemini mcp add diffctx uvx -- --from 'diffctx[mcp]' diffctx-mcp
|
|
184
|
+
# VS Code
|
|
185
|
+
code --add-mcp '{"name":"diffctx","command":"uvx","args":["--from","diffctx[mcp]","diffctx-mcp"]}'
|
|
173
186
|
```
|
|
174
187
|
|
|
175
|
-
|
|
176
|
-
`
|
|
177
|
-
|
|
178
|
-
|
|
188
|
+
With `pip install 'diffctx[mcp]'` already done, replace the
|
|
189
|
+
`uvx --from 'diffctx[mcp]' diffctx-mcp` tail with plain `diffctx-mcp`.
|
|
190
|
+
|
|
191
|
+
The server exposes one tool, `diffctx_context`, that assistants call when
|
|
192
|
+
reviewing PRs, explaining changes, or investigating broken tests. It ranks the
|
|
193
|
+
code that explains a diff, then reads only the fragments the assistant picked —
|
|
194
|
+
two calls that pay for the selection instead of a whole pack. The wider
|
|
195
|
+
`get_tree_map` and `get_file_context` tools are opt-in via
|
|
196
|
+
`DIFFCTX_MCP_LEGACY_TOOLS=1`. Tool reference and JSON configs for Claude
|
|
197
|
+
Desktop, Cursor, Continue, Windsurf, and Zed:
|
|
179
198
|
[`src/diffctx/mcp/README.md`](src/diffctx/mcp/README.md).
|
|
180
199
|
|
|
181
200
|
## Ignore patterns
|
|
@@ -186,21 +205,18 @@ anchored `/root_only.txt`). `.diffctx/whitelist` acts as an include-only filter,
|
|
|
186
205
|
and the output file is always auto-ignored. `--no-default-ignores` disables the
|
|
187
206
|
built-in patterns; `--no-ignores` disables all ignore rules (tree mode only).
|
|
188
207
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
`%LOCALAPPDATA%\diffctx\token-cache` (Windows). It is a pure speedup: deleting
|
|
195
|
-
it only costs one cold run.
|
|
208
|
+
An excluded path never appears in the output in any role: in diff mode it is
|
|
209
|
+
dropped both from `changed_files` and from the candidate universe, so it cannot
|
|
210
|
+
come back as a related-context fragment either (including under `--full`). The
|
|
211
|
+
same guarantee covers secret-like paths (`id_rsa`, `*.pem`, `*.key`, ...),
|
|
212
|
+
which are filtered even without an ignore entry.
|
|
196
213
|
|
|
197
|
-
|
|
198
|
-
|----------|--------|
|
|
199
|
-
| `DIFFCTX_TOKEN_CACHE_DIR` | Relocate the cache |
|
|
200
|
-
| `DIFFCTX_TOKEN_CACHE_MAX_BYTES` | Size cap, default `536870912` (512 MB); `0` disables eviction |
|
|
214
|
+
## Token cache
|
|
201
215
|
|
|
202
|
-
|
|
203
|
-
|
|
216
|
+
Diff mode caches per-blob tokenization in the OS cache directory (e.g.
|
|
217
|
+
`~/Library/Caches/diffctx/token-cache`) — a pure speedup, safe to delete.
|
|
218
|
+
`DIFFCTX_TOKEN_CACHE_DIR` relocates it; `DIFFCTX_TOKEN_CACHE_MAX_BYTES` caps
|
|
219
|
+
its size (default 512 MB, `0` disables eviction).
|
|
204
220
|
|
|
205
221
|
## Exit codes
|
|
206
222
|
|
|
@@ -215,14 +231,6 @@ its share of the cap, oldest entries first.
|
|
|
215
231
|
| `130`| Interrupted (Ctrl-C) |
|
|
216
232
|
| `141`| Broken pipe (e.g. piping into `head`) |
|
|
217
233
|
|
|
218
|
-
## Repository layout
|
|
219
|
-
|
|
220
|
-
Rust product code lives in `crates/diffctx-native/`; the Python CLI and MCP
|
|
221
|
-
package live in `src/diffctx/`. Evaluation code, immutable datasets, and paper
|
|
222
|
-
artifacts are intentionally separated under `eval/`, `datasets/`, and `paper/`.
|
|
223
|
-
See [repository ownership boundaries](docs/architecture/repository-layout.md)
|
|
224
|
-
for the lifecycle and entry point of each area.
|
|
225
|
-
|
|
226
234
|
## License
|
|
227
235
|
|
|
228
236
|
Apache 2.0
|
|
@@ -235,6 +243,14 @@ Apache 2.0
|
|
|
235
243
|
|
|
236
244
|
- [Documentation site](https://nikolay-e.github.io/diffctx/) — the pipeline
|
|
237
245
|
end to end: diff → fragments → graph → relevance → selection
|
|
246
|
+
- [GitHub Action](docs/product/github-action.md) — diff context as a CI step
|
|
247
|
+
for LLM review
|
|
248
|
+
- [Token counting](docs/product/token-budget.md) — which encoder, and what
|
|
249
|
+
`--budget` means for non-GPT models
|
|
250
|
+
- [Comparison](COMPARISON.md) — measured results, and when a whole-repo packer
|
|
251
|
+
or a persistent code-graph server fits better
|
|
252
|
+
- [Paper](https://doi.org/10.5281/zenodo.18824579) — budgeted typed-graph
|
|
253
|
+
retrieval for diff-aware context selection (Zenodo, 2026)
|
|
238
254
|
- [Changelog](CHANGELOG.md)
|
|
239
255
|
- [Security policy](SECURITY.md) — threat model and vulnerability reporting
|
|
240
256
|
- [Parameter strategy](docs/engineering/parameter-strategy.md) — how `--alpha`,
|
package/checksums.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"diffctx-1.
|
|
3
|
-
"diffctx-1.
|
|
4
|
-
"diffctx-1.
|
|
5
|
-
"diffctx-1.
|
|
2
|
+
"diffctx-1.13.0-aarch64-apple-darwin.tar.gz": "bc657ca9ceafee4127c454940e9d4700dd9ba8df338326b1e67a04a430479902",
|
|
3
|
+
"diffctx-1.13.0-aarch64-unknown-linux-gnu.tar.gz": "46b726b644f4a7d3759ecb5c4b48f1467f26f3c0880c7db66b6deb64c92da5c7",
|
|
4
|
+
"diffctx-1.13.0-x86_64-pc-windows-msvc.zip": "43a862384d393ca8f54e81812f530fc434e07f8d85faa1219cd893fd5932ef88",
|
|
5
|
+
"diffctx-1.13.0-x86_64-unknown-linux-gnu.tar.gz": "0585f5df531dbac369ced4398fa745a161c6f6fdaff52e457e646da43bc79f50"
|
|
6
6
|
}
|