diffctx 1.15.0 → 1.16.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
@@ -81,15 +81,22 @@ keeps its git meaning.
81
81
 
82
82
  | Flag | Default | Description |
83
83
  |-------------|---------|--------------------------------------------------------------------------|
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) |
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); `pit` = the same fusion on score percentiles |
85
+ | `--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) |
86
+ | `--alpha` | 0.60 | PPR continuation probability: higher = relevance travels further from the change, lower = tighter around it (`--scoring ppr` only) |
87
87
  | `--tau` | 0.05 | Relevance threshold for full fragment content; lower-scoring fragments are stubbed or dropped (lower = more context) |
88
88
  | `--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 |
89
+ | `--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
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
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"` |
92
92
 
93
+ Every JSON/YAML artifact opens with `schema: diffctx.context.v1` and validates
94
+ against [`schemas/diffctx.context.v1.json`](schemas/diffctx.context.v1.json),
95
+ generated from the engine's own type; it closes with a `provenance` block
96
+ (engine version, input object ids, the effective configuration and its hash,
97
+ the selection parameters, the tokenizer) and, when a limit stopped the run
98
+ short, a `coverage` block naming it.
99
+
93
100
  ### `graph` subcommand
94
101
 
95
102
  Explore the underlying dependency graph directly, without a diff:
@@ -141,7 +148,7 @@ from diffctx import build_diff_context, map_directory, to_json, to_markdown, to_
141
148
  ctx = build_diff_context(
142
149
  Path("."),
143
150
  "HEAD~1..HEAD",
144
- budget_tokens=None, # None = auto; 0 = strict-zero floor (empty); -1 = uncapped; N = hard cap
151
+ budget_tokens=None, # None = auto; 0 = no fragments; -1 = uncapped; N = cap on the whole artifact
145
152
  alpha=0.6,
146
153
  tau=0.05,
147
154
  full=False,
@@ -193,17 +200,45 @@ reviewing PRs, explaining changes, or investigating broken tests. It ranks the
193
200
  code that explains a diff, then reads only the fragments the assistant picked —
194
201
  two calls that pay for the selection instead of a whole pack. The wider
195
202
  `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).
203
+ `DIFFCTX_MCP_LEGACY_TOOLS=1`. Filesystem confinement via
204
+ `DIFFCTX_ALLOWED_PATHS`: see [SECURITY.md](SECURITY.md).
205
+
206
+ Every stdio client takes the same server shape; only the config file differs:
207
+
208
+ | Client | Config file | Key |
209
+ |---|---|---|
210
+ | Claude Code (project) | `.mcp.json` | `mcpServers` |
211
+ | Claude Desktop | `claude_desktop_config.json` | `mcpServers` |
212
+ | Cursor | `~/.cursor/mcp.json` | `mcpServers` |
213
+ | Windsurf | `~/.codeium/windsurf/mcp_config.json` | `mcpServers` |
214
+ | Continue | `~/.continue/config.json` | `experimental.modelContextProtocolServers` (transport object) |
215
+ | Zed | `~/.config/zed/settings.json` | `context_servers` (`command.path`) |
216
+
217
+ ```json
218
+ {
219
+ "mcpServers": {
220
+ "diffctx": {
221
+ "command": "uvx",
222
+ "args": ["--from", "diffctx[mcp]", "diffctx-mcp"]
223
+ }
224
+ }
225
+ }
226
+ ```
227
+
228
+ With `pip install 'diffctx[mcp]'` already done, `"command": "diffctx-mcp"` with
229
+ no args works everywhere instead. Use the `diffctx-mcp` entry point, not the
230
+ `diffctx mcp` subcommand: the latter only exists from 1.12.3 onward and would
231
+ map a directory named `mcp` on older releases.
199
232
 
200
233
  ## Ignore patterns
201
234
 
202
235
  Respects `.gitignore` and `.diffctx/ignore` automatically — hierarchically at
203
236
  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).
237
+ anchored `/root_only.txt`), and the output file is always auto-ignored. Three
238
+ controls are tree mode only and are refused with `--diff`: `.diffctx/whitelist`
239
+ (`-w`) as an include-only filter, `-i` for an extra ignore file, and
240
+ `--no-default-ignores` / `--no-ignores` to drop the built-in patterns or every
241
+ ignore rule.
207
242
 
208
243
  An excluded path never appears in the output in any role: in diff mode it is
209
244
  dropped both from `changed_files` and from the candidate universe, so it cannot
@@ -227,7 +262,7 @@ its size (default 512 MB, `0` disables eviction).
227
262
  | `2` | Usage error (invalid flags/arguments) |
228
263
  | `3` | Environment error (`--diff` outside a git repo, git not installed, no commits yet) |
229
264
  | `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 |
265
+ | `124`| `--diff` ran 30 s past the `--timeout` deadline without stopping cooperatively (the deadline itself yields a partial artifact and exit 0) |
231
266
  | `130`| Interrupted (Ctrl-C) |
232
267
  | `141`| Broken pipe (e.g. piping into `head`) |
233
268
 
@@ -243,12 +278,18 @@ Apache 2.0
243
278
 
244
279
  - [Documentation site](https://nikolay-e.github.io/diffctx/) — the pipeline
245
280
  end to end: diff → fragments → graph → relevance → selection
281
+ - [Command-line reference](docs/product/cli.md) — every flag with its default
282
+ and meaning, rendered from `diffctx --help`
246
283
  - [GitHub Action](docs/product/github-action.md) — diff context as a CI step
247
284
  for LLM review
248
285
  - [Token counting](docs/product/token-budget.md) — which encoder, and what
249
286
  `--budget` means for non-GPT models
250
287
  - [Comparison](COMPARISON.md) — measured results, and when a whole-repo packer
251
288
  or a persistent code-graph server fits better
289
+ - [Benchmarks](BENCHMARKS.md) — every published number with what it was
290
+ measured on, and how to reproduce it
291
+ - [FAQ](docs/product/faq.md) — heuristic or oracle, whose tokens, monorepos,
292
+ the raw diff, secrets
252
293
  - [Paper](https://doi.org/10.5281/zenodo.18824579) — budgeted typed-graph
253
294
  retrieval for diff-aware context selection (Zenodo, 2026)
254
295
  - [Changelog](CHANGELOG.md)
package/checksums.json CHANGED
@@ -1,6 +1,6 @@
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.16.0-aarch64-apple-darwin.tar.gz": "1ef7e2a1609a446f5dce62cd5060878c4d4a1c9032b8971e0fbe73b7089d5951",
3
+ "diffctx-1.16.0-aarch64-unknown-linux-gnu.tar.gz": "726b07c7925da947912da755f0348879a2510af193eb824ad516283db0a0bff2",
4
+ "diffctx-1.16.0-x86_64-pc-windows-msvc.zip": "f690a79444040efff1328380ff14b509bf39dacd1e6502fe2a5684bbcc5a0dfa",
5
+ "diffctx-1.16.0-x86_64-unknown-linux-gnu.tar.gz": "5af02c55d8ce57b868c4006ff6b055d1672530bc7911f08cb754d8b24a44bd6a"
6
6
  }
package/install.js CHANGED
@@ -24,7 +24,9 @@ function selectTarget() {
24
24
  if (!selected) {
25
25
  throw new Error(
26
26
  `diffctx has no prebuilt binary for ${key}. ` +
27
- 'Install via pip ("pip install diffctx") or cargo ("cargo install diffctx") instead.'
27
+ 'On an Intel Mac, pip has no wheel for it either: run under Rosetta with an arm64 Node, ' +
28
+ 'or build from source with "pip install diffctx --no-binary diffctx" (needs Rust) ' +
29
+ 'or "cargo install diffctx".'
28
30
  );
29
31
  }
30
32
  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.16.0",
5
5
  "description": "Selects the minimum code an LLM needs to review a git diff",
6
6
  "keywords": [
7
7
  "git",