diffctx 1.12.3 → 1.14.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 +62 -76
- package/checksums.json +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,25 +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 | ✗ | ✗ | ✗ | ✓ |
|
|
29
|
-
|
|
30
|
-
Fuller positioning — measured results, and when a whole-repo packer or a
|
|
31
|
-
persistent code-graph server fits better: [COMPARISON.md](COMPARISON.md).
|
|
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).
|
|
32
23
|
|
|
33
24
|
## Install
|
|
34
25
|
|
|
@@ -54,18 +45,11 @@ scoop bucket add diffctx https://github.com/nikolay-e/diffctx
|
|
|
54
45
|
scoop install diffctx/diffctx
|
|
55
46
|
```
|
|
56
47
|
|
|
57
|
-
The image runs as a non-root user (uid 10001) and writes to stdout — the
|
|
58
|
-
native binary has no `-o` flag, so redirect to capture: `... --diff HEAD~1 >
|
|
59
|
-
context.yaml`.
|
|
60
|
-
|
|
61
48
|
Prebuilt binaries for linux (x86_64/aarch64), macOS (arm64) and Windows (x64)
|
|
62
49
|
are attached to every [release](https://github.com/nikolay-e/diffctx/releases/latest).
|
|
63
|
-
The native binary
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
imported as `_diffctx` (`use _diffctx::pipeline::build_diff_context`), since
|
|
67
|
-
the crate doubles as the Python extension module
|
|
68
|
-
([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.
|
|
69
53
|
|
|
70
54
|
## Quick start
|
|
71
55
|
|
|
@@ -87,21 +71,24 @@ builds a code graph (imports, co-changes, type refs), propagates relevance
|
|
|
87
71
|
outward from the changed lines, and stops when relevance drops below `--tau` or
|
|
88
72
|
the `--budget` token cap is hit.
|
|
89
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
|
+
|
|
90
82
|
| Flag | Default | Description |
|
|
91
83
|
|-------------|---------|--------------------------------------------------------------------------|
|
|
92
|
-
| `--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) |
|
|
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) |
|
|
93
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) |
|
|
94
86
|
| `--alpha` | 0.60 | PPR damping; higher = context clusters tighter around changes (`--scoring ppr` only) |
|
|
95
|
-
| `--tau` | 0.
|
|
87
|
+
| `--tau` | 0.05 | Relevance threshold for full fragment content; lower-scoring fragments are stubbed or dropped (lower = more context) |
|
|
96
88
|
| `--full` | false | Only the changed files, every fragment, no related-code context |
|
|
97
89
|
| `--timeout` | 300 | Wall-clock deadline in seconds; on expiry diffctx exits 124 instead of hanging |
|
|
98
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 |
|
|
99
|
-
|
|
100
|
-
Calibration of `--alpha`, `--tau`, and the edge-weight priors:
|
|
101
|
-
[`docs/engineering/parameter-strategy.md`](docs/engineering/parameter-strategy.md).
|
|
102
|
-
Theory:
|
|
103
|
-
[diffctx: Budgeted Typed-Graph Retrieval for Diff-Aware Code Context
|
|
104
|
-
Selection (Zenodo, 2026)](https://doi.org/10.5281/zenodo.18824579).
|
|
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"` |
|
|
105
92
|
|
|
106
93
|
### `graph` subcommand
|
|
107
94
|
|
|
@@ -130,23 +117,20 @@ diffctx . -i custom.ignore # custom ignore patterns
|
|
|
130
117
|
diffctx . --diff # uncommitted changes (working tree vs HEAD)
|
|
131
118
|
diffctx . --diff HEAD~1 # context for last commit
|
|
132
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)
|
|
133
122
|
diffctx . --diff HEAD~1 --budget 30000 # limit to ~30k tokens
|
|
134
123
|
diffctx . --diff HEAD~1 -c # diff context to clipboard
|
|
135
124
|
diffctx . --diff HEAD~1 --with-raw-diff # raw patch + selected context
|
|
125
|
+
diffctx . --diff HEAD~1 --mode locate # ranked navigation JSON, no source
|
|
136
126
|
```
|
|
137
127
|
<!-- END USAGE -->
|
|
138
128
|
|
|
139
129
|
Every run reports token count and size on stderr — `12,847 tokens
|
|
140
|
-
(o200k_base), 52.3 KB
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
`<unreadable content: not utf-8>`.
|
|
145
|
-
|
|
146
|
-
Counts come from tiktoken's `o200k_base` encoder and are exact only for the
|
|
147
|
-
GPT-4o family; Claude, Gemini and others tokenize differently, so treat
|
|
148
|
-
`--budget` as an upper bound in o200k tokens and leave headroom. Details:
|
|
149
|
-
[`docs/product/token-budget.md`](docs/product/token-budget.md).
|
|
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>`.
|
|
150
134
|
|
|
151
135
|
## Python API
|
|
152
136
|
|
|
@@ -159,7 +143,7 @@ ctx = build_diff_context(
|
|
|
159
143
|
"HEAD~1..HEAD",
|
|
160
144
|
budget_tokens=None, # None = auto; 0 = strict-zero floor (empty); -1 = uncapped; N = hard cap
|
|
161
145
|
alpha=0.6,
|
|
162
|
-
tau=0.
|
|
146
|
+
tau=0.05,
|
|
163
147
|
full=False,
|
|
164
148
|
scoring_mode="ego",
|
|
165
149
|
timeout=300,
|
|
@@ -181,19 +165,36 @@ print(to_yaml(tree))
|
|
|
181
165
|
|
|
182
166
|
## MCP server
|
|
183
167
|
|
|
168
|
+
[](https://registry.modelcontextprotocol.io/v0/servers?search=io.github.nikolay-e/diffctx)
|
|
169
|
+
[](https://glama.ai/mcp/servers/nikolay-e/diffctx)
|
|
170
|
+
|
|
184
171
|
diffctx includes an [MCP](https://modelcontextprotocol.io) server that lets AI
|
|
185
172
|
assistants (Claude Code, Cursor, Windsurf, etc.) call diff context analysis
|
|
186
|
-
automatically during code review.
|
|
187
|
-
|
|
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/)):
|
|
188
176
|
|
|
189
177
|
```bash
|
|
190
|
-
|
|
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"]}'
|
|
191
186
|
```
|
|
192
187
|
|
|
193
|
-
|
|
194
|
-
`
|
|
195
|
-
|
|
196
|
-
|
|
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:
|
|
197
198
|
[`src/diffctx/mcp/README.md`](src/diffctx/mcp/README.md).
|
|
198
199
|
|
|
199
200
|
## Ignore patterns
|
|
@@ -212,19 +213,10 @@ which are filtered even without an ignore entry.
|
|
|
212
213
|
|
|
213
214
|
## Token cache
|
|
214
215
|
|
|
215
|
-
Diff mode caches per-blob tokenization
|
|
216
|
-
`~/Library/Caches/diffctx/token-cache`
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
it only costs one cold run.
|
|
220
|
-
|
|
221
|
-
| Variable | Effect |
|
|
222
|
-
|----------|--------|
|
|
223
|
-
| `DIFFCTX_TOKEN_CACHE_DIR` | Relocate the cache |
|
|
224
|
-
| `DIFFCTX_TOKEN_CACHE_MAX_BYTES` | Size cap, default `536870912` (512 MB); `0` disables eviction |
|
|
225
|
-
|
|
226
|
-
Eviction is amortized: each run trims one of the cache's 256 shards back under
|
|
227
|
-
its share of the cap, oldest entries first.
|
|
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).
|
|
228
220
|
|
|
229
221
|
## Exit codes
|
|
230
222
|
|
|
@@ -239,14 +231,6 @@ its share of the cap, oldest entries first.
|
|
|
239
231
|
| `130`| Interrupted (Ctrl-C) |
|
|
240
232
|
| `141`| Broken pipe (e.g. piping into `head`) |
|
|
241
233
|
|
|
242
|
-
## Repository layout
|
|
243
|
-
|
|
244
|
-
Rust product code lives in `crates/diffctx-native/`; the Python CLI and MCP
|
|
245
|
-
package live in `src/diffctx/`. Evaluation code, immutable datasets, and paper
|
|
246
|
-
artifacts are intentionally separated under `eval/`, `datasets/`, and `paper/`.
|
|
247
|
-
See [repository ownership boundaries](docs/architecture/repository-layout.md)
|
|
248
|
-
for the lifecycle and entry point of each area.
|
|
249
|
-
|
|
250
234
|
## License
|
|
251
235
|
|
|
252
236
|
Apache 2.0
|
|
@@ -265,6 +249,8 @@ Apache 2.0
|
|
|
265
249
|
`--budget` means for non-GPT models
|
|
266
250
|
- [Comparison](COMPARISON.md) — measured results, and when a whole-repo packer
|
|
267
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)
|
|
268
254
|
- [Changelog](CHANGELOG.md)
|
|
269
255
|
- [Security policy](SECURITY.md) — threat model and vulnerability reporting
|
|
270
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.14.0-aarch64-apple-darwin.tar.gz": "20223d07e8f304b631eff3556386564918037359dbbf12c2ca18335381c129fd",
|
|
3
|
+
"diffctx-1.14.0-aarch64-unknown-linux-gnu.tar.gz": "3922b1460a1f5e8f4f79a2c7c2dcc3d3da5dc9a9174719b44bf936cf57b86a6a",
|
|
4
|
+
"diffctx-1.14.0-x86_64-pc-windows-msvc.zip": "07b67a9da0c4331f75bb821ba3404bce349ca6c8063ff5f412c30dcc993bc6bf",
|
|
5
|
+
"diffctx-1.14.0-x86_64-unknown-linux-gnu.tar.gz": "2125f363fae2439162e4403e20bc90bc366bed098332c32714867273376b38a1"
|
|
6
6
|
}
|