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 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
- Prebuilt binaries for linux (x86_64/aarch64), macOS (arm64) and Windows (x64)
49
- are attached to every [release](https://github.com/nikolay-e/diffctx/releases/latest).
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 | 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) |
86
- | `--alpha` | 0.60 | PPR damping; higher = context clusters tighter around changes (`--scoring ppr` only) |
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 diffctx exits 124 instead of hanging |
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 = strict-zero floor (empty); -1 = uncapped; N = hard cap
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`. Tool reference and JSON configs for Claude
197
- Desktop, Cursor, Continue, Windsurf, and Zed:
198
- [`src/diffctx/mcp/README.md`](src/diffctx/mcp/README.md).
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`). `.diffctx/whitelist` acts as an include-only filter,
205
- and the output file is always auto-ignored. `--no-default-ignores` disables the
206
- built-in patterns; `--no-ignores` disables all ignore rules (tree mode only).
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` exceeded the `--timeout` wall-clock deadline |
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://nikolay-e.github.io/diffctx/) — the pipeline
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.15.0-aarch64-apple-darwin.tar.gz": "721f534c2f6c3c6b6bd1a8e9e1a9595116202cb9177c294d625f55d34fb00bed",
3
- "diffctx-1.15.0-aarch64-unknown-linux-gnu.tar.gz": "1c9bc1fb064547f24f74401a9e7bab4b85b1c93006e740faae6c1ee2a9bf1a87",
4
- "diffctx-1.15.0-x86_64-pc-windows-msvc.zip": "91bb8437cab735b674bb14a76ddc04f8477d8e37766336157551854dc5ab2e1d",
5
- "diffctx-1.15.0-x86_64-unknown-linux-gnu.tar.gz": "bfe4b6efc300bd9f38832d10e8a1bb63d6c1e3a328ccf774901f34c914fa513f"
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
- 'Install via pip ("pip install diffctx") or cargo ("cargo install diffctx") instead.'
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.15.0",
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://github.com/nikolay-e/diffctx",
20
+ "homepage": "https://diffctx.com/",
21
21
  "bugs": "https://github.com/nikolay-e/diffctx/issues",
22
22
  "repository": {
23
23
  "type": "git",