diffctx 1.15.0 → 1.17.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 +66 -17
- package/checksums.json +6 -4
- package/install.js +4 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -45,8 +45,15 @@ scoop bucket add diffctx https://github.com/nikolay-e/diffctx
|
|
|
45
45
|
scoop install diffctx/diffctx
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
Every [release](https://github.com/nikolay-e/diffctx/releases/latest) carries
|
|
49
|
+
prebuilt binaries; the current target set is Linux (x86_64/aarch64), macOS
|
|
50
|
+
(arm64/x86_64) and Windows (x64/arm64), and a release older than a target's
|
|
51
|
+
first build lacks it (v1.16.0 predates the macOS x86_64 and Windows arm64
|
|
52
|
+
archives). On Windows on ARM, `diffctx[mcp]` builds `cryptography` from source,
|
|
53
|
+
which needs OpenSSL: that project publishes no wheel for the platform.
|
|
54
|
+
Free-threaded CPython (`3.14t`) cannot load the `abi3` wheel, so each release
|
|
55
|
+
after 1.16.0 also ships a `cp314t` wheel per platform (the extension runs with
|
|
56
|
+
the GIL off); on 1.16.0 itself `pip` and `uv` fall back to compiling the sdist.
|
|
50
57
|
The native binary and Docker image cover diff mode with YAML/JSON output and
|
|
51
58
|
write to stdout (redirect to capture); tree mode, Markdown output, the `graph`
|
|
52
59
|
subcommand and the MCP server live in the Python package.
|
|
@@ -81,15 +88,22 @@ keeps its git meaning.
|
|
|
81
88
|
|
|
82
89
|
| Flag | Default | Description |
|
|
83
90
|
|-------------|---------|--------------------------------------------------------------------------|
|
|
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 |
|
|
86
|
-
| `--alpha` | 0.60 | PPR
|
|
91
|
+
| `--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); `pit` = the same fusion on score percentiles |
|
|
92
|
+
| `--budget` | auto | Cap in o200k_base tokens on the whole rendered artifact (see [Token counting](docs/product/token-budget.md)): every changed file gets one witness first, relevance fills the rest, and the renderer drops context from the tail until the document fits. A budget smaller than the summary yields the summary alone. `N` = fixed cap, `-1` disables it, `0` is a strict-zero floor (no fragments; use `--full` for changed files only) |
|
|
93
|
+
| `--alpha` | 0.60 | PPR continuation probability: higher = relevance travels further from the change, lower = tighter around it (`--scoring ppr` only) |
|
|
87
94
|
| `--tau` | 0.05 | Relevance threshold for full fragment content; lower-scoring fragments are stubbed or dropped (lower = more context) |
|
|
88
95
|
| `--full` | false | Only the changed files, every fragment, no related-code context |
|
|
89
|
-
| `--timeout` | 300 | Wall-clock deadline in seconds; on expiry
|
|
96
|
+
| `--timeout` | 300 | Wall-clock deadline in seconds; on expiry the run stops cooperatively and emits a partial artifact whose `coverage` block names the limit (exit 0). 124 is the watchdog behind it, 30 s later, for a phase that could not stop |
|
|
90
97
|
| `--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
98
|
| `--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"` |
|
|
92
99
|
|
|
100
|
+
Every JSON/YAML artifact opens with `schema: diffctx.context.v1` and validates
|
|
101
|
+
against [`schemas/diffctx.context.v1.json`](schemas/diffctx.context.v1.json),
|
|
102
|
+
generated from the engine's own type; it closes with a `provenance` block
|
|
103
|
+
(engine version, input object ids, the effective configuration and its hash,
|
|
104
|
+
the selection parameters, the tokenizer) and, when a limit stopped the run
|
|
105
|
+
short, a `coverage` block naming it.
|
|
106
|
+
|
|
93
107
|
### `graph` subcommand
|
|
94
108
|
|
|
95
109
|
Explore the underlying dependency graph directly, without a diff:
|
|
@@ -141,7 +155,7 @@ from diffctx import build_diff_context, map_directory, to_json, to_markdown, to_
|
|
|
141
155
|
ctx = build_diff_context(
|
|
142
156
|
Path("."),
|
|
143
157
|
"HEAD~1..HEAD",
|
|
144
|
-
budget_tokens=None, # None = auto; 0 =
|
|
158
|
+
budget_tokens=None, # None = auto; 0 = no fragments; -1 = uncapped; N = cap on the whole artifact
|
|
145
159
|
alpha=0.6,
|
|
146
160
|
tau=0.05,
|
|
147
161
|
full=False,
|
|
@@ -193,17 +207,46 @@ reviewing PRs, explaining changes, or investigating broken tests. It ranks the
|
|
|
193
207
|
code that explains a diff, then reads only the fragments the assistant picked —
|
|
194
208
|
two calls that pay for the selection instead of a whole pack. The wider
|
|
195
209
|
`get_tree_map` and `get_file_context` tools are opt-in via
|
|
196
|
-
`DIFFCTX_MCP_LEGACY_TOOLS=1`.
|
|
197
|
-
|
|
198
|
-
|
|
210
|
+
`DIFFCTX_MCP_LEGACY_TOOLS=1`. Filesystem confinement via
|
|
211
|
+
`DIFFCTX_ALLOWED_PATHS`: see [SECURITY.md](SECURITY.md).
|
|
212
|
+
|
|
213
|
+
Every stdio client takes the same server shape; only the config file differs:
|
|
214
|
+
|
|
215
|
+
| Client | Config file | Key |
|
|
216
|
+
|---|---|---|
|
|
217
|
+
| Claude Code (project) | `.mcp.json` | `mcpServers` |
|
|
218
|
+
| Claude Desktop | `claude_desktop_config.json` | `mcpServers` |
|
|
219
|
+
| Cursor | `~/.cursor/mcp.json` | `mcpServers` |
|
|
220
|
+
| Windsurf | `~/.codeium/windsurf/mcp_config.json` | `mcpServers` |
|
|
221
|
+
| Continue | `~/.continue/config.json` | `experimental.modelContextProtocolServers` (transport object) |
|
|
222
|
+
| Zed | `~/.config/zed/settings.json` | `context_servers` (`command.path`) |
|
|
223
|
+
|
|
224
|
+
```json
|
|
225
|
+
{
|
|
226
|
+
"mcpServers": {
|
|
227
|
+
"diffctx": {
|
|
228
|
+
"command": "uvx",
|
|
229
|
+
"args": ["--from", "diffctx[mcp]", "diffctx-mcp"]
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
With `pip install 'diffctx[mcp]'` already done, `"command": "diffctx-mcp"` with
|
|
236
|
+
no args works everywhere instead. Use the `diffctx-mcp` entry point, not the
|
|
237
|
+
`diffctx mcp` subcommand: the latter only exists from 1.12.3 onward and would
|
|
238
|
+
map a directory named `mcp` on older releases.
|
|
199
239
|
|
|
200
240
|
## Ignore patterns
|
|
201
241
|
|
|
202
242
|
Respects `.gitignore` and `.diffctx/ignore` automatically — hierarchically at
|
|
203
243
|
every directory level, with full gitignore semantics (negation `!important.log`,
|
|
204
|
-
anchored `/root_only.txt`)
|
|
205
|
-
|
|
206
|
-
|
|
244
|
+
anchored `/root_only.txt`), and in tree mode the output file is always
|
|
245
|
+
auto-ignored (`--diff` reports a saved `tree.md` like any other change). Three
|
|
246
|
+
controls are tree mode only and are refused with `--diff` and `graph`: `.diffctx/whitelist`
|
|
247
|
+
(`-w`) as an include-only filter, `-i` for an extra ignore file, and
|
|
248
|
+
`--no-default-ignores` / `--no-ignores` to drop the built-in patterns or every
|
|
249
|
+
ignore rule.
|
|
207
250
|
|
|
208
251
|
An excluded path never appears in the output in any role: in diff mode it is
|
|
209
252
|
dropped both from `changed_files` and from the candidate universe, so it cannot
|
|
@@ -224,10 +267,10 @@ its size (default 512 MB, `0` disables eviction).
|
|
|
224
267
|
|------|---------|
|
|
225
268
|
| `0` | Success — output contains content |
|
|
226
269
|
| `1` | Runtime error (bad path, permission denied, etc.) |
|
|
227
|
-
| `2` | Usage error (invalid flags/arguments) |
|
|
228
|
-
| `3` | Environment error (`--diff` outside a git repo, git not installed, no commits yet) |
|
|
270
|
+
| `2` | Usage error (invalid flags/arguments, an empty `--diff ""`, a malformed duration such as `1.5h`, an out-of-range `--alpha`/`--tau`/`--budget`/`--timeout`, or a tree-mode flag — `-i`/`-w`/`--no-default-ignores`/`--no-ignores` — given with `--diff` or `graph`) |
|
|
271
|
+
| `3` | Environment error (`--diff` outside a git repo, in a bare one, or on a missing path, git not installed or refusing to start — its `fatal:` line is printed, no commits yet, a revision missing from a shallow clone) |
|
|
229
272
|
| `4` | `--diff` produced no semantic context (clean tree, binary-only, everything filtered); output is still emitted. Deletion/rename/lockfile-only diffs list `deleted_files`/`renamed_files`/`lockfile_changes` and exit `0` |
|
|
230
|
-
| `124`| `--diff`
|
|
273
|
+
| `124`| `--diff` ran 30 s past the `--timeout` deadline without stopping cooperatively (the deadline itself yields a partial artifact and exit 0) |
|
|
231
274
|
| `130`| Interrupted (Ctrl-C) |
|
|
232
275
|
| `141`| Broken pipe (e.g. piping into `head`) |
|
|
233
276
|
|
|
@@ -241,14 +284,20 @@ Apache 2.0
|
|
|
241
284
|
|
|
242
285
|
---
|
|
243
286
|
|
|
244
|
-
- [Documentation site](https://
|
|
287
|
+
- [Documentation site](https://diffctx.com/) — the pipeline
|
|
245
288
|
end to end: diff → fragments → graph → relevance → selection
|
|
289
|
+
- [Command-line reference](docs/product/cli.md) — every flag with its default
|
|
290
|
+
and meaning, rendered from `diffctx --help`
|
|
246
291
|
- [GitHub Action](docs/product/github-action.md) — diff context as a CI step
|
|
247
292
|
for LLM review
|
|
248
293
|
- [Token counting](docs/product/token-budget.md) — which encoder, and what
|
|
249
294
|
`--budget` means for non-GPT models
|
|
250
295
|
- [Comparison](COMPARISON.md) — measured results, and when a whole-repo packer
|
|
251
296
|
or a persistent code-graph server fits better
|
|
297
|
+
- [Benchmarks](BENCHMARKS.md) — every published number with what it was
|
|
298
|
+
measured on, and how to reproduce it
|
|
299
|
+
- [FAQ](docs/product/faq.md) — heuristic or oracle, whose tokens, monorepos,
|
|
300
|
+
the raw diff, secrets
|
|
252
301
|
- [Paper](https://doi.org/10.5281/zenodo.18824579) — budgeted typed-graph
|
|
253
302
|
retrieval for diff-aware context selection (Zenodo, 2026)
|
|
254
303
|
- [Changelog](CHANGELOG.md)
|
package/checksums.json
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"diffctx-1.
|
|
3
|
-
"diffctx-1.
|
|
4
|
-
"diffctx-1.
|
|
5
|
-
"diffctx-1.
|
|
2
|
+
"diffctx-1.17.0-aarch64-apple-darwin.tar.gz": "5e69dcea8229c8622bca301c40dcd21edbd1867f2be0b8f65bb321c37864aea3",
|
|
3
|
+
"diffctx-1.17.0-aarch64-pc-windows-msvc.zip": "499d32a204a7e11e50009a9b244ca74eece0ca859c45257bb2060f5de797f4cd",
|
|
4
|
+
"diffctx-1.17.0-aarch64-unknown-linux-gnu.tar.gz": "dfb3408e07dc89175327a742f856f6b915a7aefcd73dbe12779138fe10e53086",
|
|
5
|
+
"diffctx-1.17.0-x86_64-apple-darwin.tar.gz": "6188aca98dae1bef57b1bdcb3602deb61c9b036a4e103c5681b058037e425663",
|
|
6
|
+
"diffctx-1.17.0-x86_64-pc-windows-msvc.zip": "c774f09790f64b6a104029dcf41b49b37cecea3176c52231417f0ecb58f6c84b",
|
|
7
|
+
"diffctx-1.17.0-x86_64-unknown-linux-gnu.tar.gz": "3106217351d1a7e00fbd1c29593814cf633be45931cb9a4a5eb9edb18910d74c"
|
|
6
8
|
}
|
package/install.js
CHANGED
|
@@ -15,7 +15,9 @@ const TARGETS = {
|
|
|
15
15
|
'linux-x64': { target: 'x86_64-unknown-linux-gnu', archive: 'tar.gz' },
|
|
16
16
|
'linux-arm64': { target: 'aarch64-unknown-linux-gnu', archive: 'tar.gz' },
|
|
17
17
|
'darwin-arm64': { target: 'aarch64-apple-darwin', archive: 'tar.gz' },
|
|
18
|
+
'darwin-x64': { target: 'x86_64-apple-darwin', archive: 'tar.gz' },
|
|
18
19
|
'win32-x64': { target: 'x86_64-pc-windows-msvc', archive: 'zip' },
|
|
20
|
+
'win32-arm64': { target: 'aarch64-pc-windows-msvc', archive: 'zip' },
|
|
19
21
|
};
|
|
20
22
|
|
|
21
23
|
function selectTarget() {
|
|
@@ -24,7 +26,8 @@ function selectTarget() {
|
|
|
24
26
|
if (!selected) {
|
|
25
27
|
throw new Error(
|
|
26
28
|
`diffctx has no prebuilt binary for ${key}. ` +
|
|
27
|
-
'
|
|
29
|
+
'Build from source with "pip install diffctx --no-binary diffctx" (needs Rust) ' +
|
|
30
|
+
'or "cargo install diffctx".'
|
|
28
31
|
);
|
|
29
32
|
}
|
|
30
33
|
return selected;
|
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.
|
|
4
|
+
"version": "1.17.0",
|
|
5
5
|
"description": "Selects the minimum code an LLM needs to review a git diff",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"git",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"llm-context",
|
|
18
18
|
"codebase"
|
|
19
19
|
],
|
|
20
|
-
"homepage": "https://
|
|
20
|
+
"homepage": "https://diffctx.com/",
|
|
21
21
|
"bugs": "https://github.com/nikolay-e/diffctx/issues",
|
|
22
22
|
"repository": {
|
|
23
23
|
"type": "git",
|