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 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
- > Coming from [`treemapper`](https://pypi.org/project/treemapper/)? That name is
14
- > deprecated. Every command, flag, and API call works unchanged:
15
- > `treemapper` → `diffctx`, `treemapper-mcp` → `diffctx-mcp`.
16
-
17
- ## Why not just use `tree` or repomix?
18
-
19
- | | `tree` | repomix | Claude Code Review | **diffctx** |
20
- |---|:---:|:---:|:---:|:---:|
21
- | **Primary use case** | directory listing | full repo export | automated PR review | **diff context for code review** |
22
- | Smart diff context | ✗ | ✗ | ✓ | ✓ |
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 covers diff mode with YAML/JSON output; tree mode, Markdown
64
- output, the `graph` subcommand and the MCP server live in the Python package.
65
- `cargo add diffctx` embeds the pipeline in a Rust project — the library is
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.12 | Relevance threshold for full fragment content; lower-scoring fragments are stubbed or dropped (lower = more context) |
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` (tiktoken, the GPT-4o tokenizer; `~`-prefixed above
141
- 1 MB). `-c/--copy` copies output via `pbcopy` (macOS), `clip` (Windows), or
142
- `wl-copy`/`xclip`/`xsel` (Linux). Unreadable files become placeholders like
143
- `<binary file: N bytes>`, `<file too large: N bytes>`, or
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.12,
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
+ [![MCP Registry](https://img.shields.io/badge/MCP_Registry-io.github.nikolay--e%2Fdiffctx-blue)](https://registry.modelcontextprotocol.io/v0/servers?search=io.github.nikolay-e/diffctx)
169
+ [![diffctx MCP server](https://glama.ai/mcp/servers/nikolay-e/diffctx/badges/score.svg)](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. Install with `pip install 'diffctx[mcp]'`,
187
- then register it — for Claude Code:
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
- claude mcp add diffctx -- diffctx-mcp
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
- The server exposes three tools — `get_diff_context`, `get_tree_map`, and
194
- `get_file_context` — that assistants call when reviewing PRs, explaining
195
- changes, or investigating broken tests. Tool reference and configs for
196
- Cursor, Continue, Windsurf, and Zed:
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 under
216
- `~/Library/Caches/diffctx/token-cache` (macOS),
217
- `$XDG_CACHE_HOME/diffctx/token-cache` (Linux) or
218
- `%LOCALAPPDATA%\diffctx\token-cache` (Windows). It is a pure speedup: deleting
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.12.3-aarch64-apple-darwin.tar.gz": "d62da2cd29e08130dc82936080d0b5734dc91bf392b9d7beb7696d7869bbd56c",
3
- "diffctx-1.12.3-aarch64-unknown-linux-gnu.tar.gz": "a064e1edf242ddd448d8ed12445dacdcc32c5084ee82c62d54854e3b6b15abca",
4
- "diffctx-1.12.3-x86_64-pc-windows-msvc.zip": "daf1064d4a4983305812443f79cb9a3d350c53468a513a127a4d5f079a19489c",
5
- "diffctx-1.12.3-x86_64-unknown-linux-gnu.tar.gz": "83019a9eb2fa8a49eaa7999f5f6f9d88c72a88719c6ab456135ef0ed38f6f7fa"
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
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "diffctx",
3
3
  "mcpName": "io.github.nikolay-e/diffctx",
4
- "version": "1.12.3",
4
+ "version": "1.14.0",
5
5
  "description": "Selects the minimum code an LLM needs to review a git diff",
6
6
  "keywords": [
7
7
  "git",