diffctx 1.12.0__tar.gz → 1.12.2__tar.gz
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.
- {diffctx-1.12.0 → diffctx-1.12.2}/CHANGELOG.md +106 -4
- {diffctx-1.12.0/diffctx → diffctx-1.12.2}/Cargo.lock +35 -24
- diffctx-1.12.2/Cargo.toml +12 -0
- {diffctx-1.12.0 → diffctx-1.12.2}/PKG-INFO +95 -48
- {diffctx-1.12.0 → diffctx-1.12.2}/README.md +88 -44
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/Cargo.toml +10 -18
- diffctx-1.12.2/crates/diffctx-native/README.md +241 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/env_overrides.rs +1 -1
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/limits.rs +0 -1
- diffctx-1.12.2/crates/diffctx-native/src/excerpt.rs +191 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/lib.rs +1 -0
- diffctx-1.12.2/crates/diffctx-native/src/main.rs +244 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/memory_pipeline.rs +4 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/pipeline.rs +91 -2
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/postpass.rs +12 -6
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/pybridge.rs +7 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/render.rs +6 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/select.rs +12 -1
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/test_harness.rs +1 -1
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/token_corpus.rs +154 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/types.rs +10 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/utility/scoring.rs +2 -0
- {diffctx-1.12.0 → diffctx-1.12.2}/pyproject.toml +19 -14
- {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/__init__.py +2 -2
- {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/__main__.py +1 -1
- diffctx-1.12.0/src/diffctx/main.py → diffctx-1.12.2/src/diffctx/_app.py +10 -10
- {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/cli.py +12 -1
- {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/mcp/README.md +8 -4
- diffctx-1.12.2/src/diffctx/mcp/__main__.py +4 -0
- {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/mcp/server.py +32 -6
- diffctx-1.12.2/src/diffctx/version.py +1 -0
- {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/writer.py +20 -1
- diffctx-1.12.0/diffctx/src/main.rs +0 -123
- diffctx-1.12.0/diffctx/tests/common/mod.rs +0 -274
- diffctx-1.12.0/diffctx/tests/fixtures/garbage/garbage_api.py +0 -31
- diffctx-1.12.0/diffctx/tests/fixtures/garbage/garbage_constants.py +0 -11
- diffctx-1.12.0/diffctx/tests/fixtures/garbage/garbage_handlers.py +0 -27
- diffctx-1.12.0/diffctx/tests/fixtures/garbage/garbage_models.py +0 -25
- diffctx-1.12.0/diffctx/tests/fixtures/garbage/garbage_module.js +0 -23
- diffctx-1.12.0/diffctx/tests/fixtures/garbage/garbage_services.py +0 -37
- diffctx-1.12.0/diffctx/tests/fixtures/garbage/garbage_types.py +0 -31
- diffctx-1.12.0/diffctx/tests/fixtures/garbage/garbage_unrelated.yaml +0 -8
- diffctx-1.12.0/diffctx/tests/fixtures/garbage/garbage_utils.py +0 -31
- diffctx-1.12.0/diffctx/tests/fixtures/garbage/garbage_validators.py +0 -23
- diffctx-1.12.0/diffctx/tests/yaml_cases.rs +0 -276
- diffctx-1.12.0/src/diffctx/mcp/__main__.py +0 -26
- diffctx-1.12.0/src/diffctx/version.py +0 -1
- {diffctx-1.12.0 → diffctx-1.12.2}/LICENSE +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/analytics.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/candidate_files.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/analytics.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/bm25.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/budget.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/category_weights.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/edge_weights.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/extensions.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/filtering.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/fragmentation.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/git.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/graph_filtering.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/importance.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/mod.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/mode.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/needs.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/parsers.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/render.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/scoring.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/selection.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/tokenization.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/weights.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/core.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/discovery.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/base.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/config_edges/build_system.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/config_edges/cicd.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/config_edges/docker.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/config_edges/generic.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/config_edges/helm.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/config_edges/kubernetes.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/config_edges/mod.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/document/mod.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/history/cochange.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/history/mod.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/mod.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/ansible.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/bazel.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/c_family.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/cargo_edges.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/clojure.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/css.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/dart.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/dbt.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/dotnet.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/elixir.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/erlang.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/go.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/graphql.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/haskell.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/javascript.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/julia.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/jvm.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/latex.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/lua.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/mod.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/nim.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/nix.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/ocaml.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/openapi.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/perl.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/php.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/prisma.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/protobuf.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/python.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/r_lang.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/ruby.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/rust_lang.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/shell.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/sql.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/swift.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/tags.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/terraform.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/zig.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/similarity/lexical.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/similarity/mod.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/structural/containment.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/structural/mod.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/structural/sibling.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/structural/testing.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/filtering.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/fragmentation.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/git.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/graph.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/graph_export.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/interval.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/languages.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/mode.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/parsers/config_parser.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/parsers/generic.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/parsers/markdown.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/parsers/mod.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/parsers/tree_sitter_strategy.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/peak_rss.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/ppr.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/project_graph.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/scoring.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/signatures.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/stopwords.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/tokenizer.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/utility/boltzmann.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/utility/importance.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/utility/mod.rs +0 -0
- {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/utility/needs.rs +0 -0
- {diffctx-1.12.0 → diffctx-1.12.2}/rust-toolchain.toml +0 -0
- {diffctx-1.12.0/src/diffctx/diffctx → diffctx-1.12.2/src/diffctx/_native}/__init__.py +0 -0
- {diffctx-1.12.0/src/diffctx/diffctx → diffctx-1.12.2/src/diffctx/_native}/graph_analytics.py +0 -0
- {diffctx-1.12.0/src/diffctx/diffctx → diffctx-1.12.2/src/diffctx/_native}/graph_export.py +0 -0
- {diffctx-1.12.0/src/diffctx/diffctx → diffctx-1.12.2/src/diffctx/_native}/pipeline.py +0 -0
- {diffctx-1.12.0/src/diffctx/diffctx → diffctx-1.12.2/src/diffctx/_native}/project_graph.py +0 -0
- {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/clipboard.py +0 -0
- {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/ignore.py +0 -0
- {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/logger.py +0 -0
- {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/mcp/__init__.py +0 -0
- {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/mcp/formatting.py +0 -0
- {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/mcp/security.py +0 -0
- {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/py.typed +0 -0
- {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/tokens.py +0 -0
- {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/tree.py +0 -0
|
@@ -7,6 +7,108 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **The container images ran as root.** The published 1.12.1 image had no
|
|
13
|
+
`USER` directive, so every `docker run` executed as uid 0 while the README
|
|
14
|
+
promised an unprivileged user. The image now runs as uid 10001.
|
|
15
|
+
- **The MCP server advertised the SDK's version as its own.** `FastMCP` takes
|
|
16
|
+
no version argument, so `initialize` reported the installed `mcp` package
|
|
17
|
+
version (e.g. `1.28.1`) as the diffctx server version — a number that drifts
|
|
18
|
+
with every SDK bump and never matched the shipped package. Clients now see
|
|
19
|
+
the real version.
|
|
20
|
+
- **The native binary hard-capped every run at 4096 tokens.** `--budget`
|
|
21
|
+
carried a fixed clap default instead of leaving the budget unset, so the
|
|
22
|
+
auto-sizing the Python CLI has always used never ran on the binary shipped
|
|
23
|
+
via crates.io, npm, the container images, Scoop, AUR and the release
|
|
24
|
+
archives — the same command returned a truncated selection there (14-file
|
|
25
|
+
self-eat range: 15 fragments across 10 files, against 172 across 44). The
|
|
26
|
+
binary now defaults to auto sizing, and `--budget -1` means unlimited as it
|
|
27
|
+
does in Python.
|
|
28
|
+
- The token cache grew without bound (a machine that had analyzed a few dozen
|
|
29
|
+
repositories reached 817k files / 4.0 GB). It is now capped at 512 MB,
|
|
30
|
+
overridable with `DIFFCTX_TOKEN_CACHE_MAX_BYTES` (`0` disables eviction);
|
|
31
|
+
each run trims one of the 256 shards back under its share of the cap,
|
|
32
|
+
oldest entries first (#122).
|
|
33
|
+
- The native binary exited `0` on a diff that produced no semantic context
|
|
34
|
+
(clean tree, binary-only, everything over the size cap), so callers on the
|
|
35
|
+
binary channels could not tell an empty selection from a successful one. It
|
|
36
|
+
now mirrors the Python CLI: the `no semantic context` warning plus its hint
|
|
37
|
+
on stderr and exit code `4`.
|
|
38
|
+
- The native binary printed no token summary, although the README documents
|
|
39
|
+
one for every run. It now writes `N tokens (o200k_base), SIZE` to stderr,
|
|
40
|
+
silenced by the new `-q/--quiet`.
|
|
41
|
+
- `diffctx -v` was a usage error on the native binary while it printed the
|
|
42
|
+
version on the Python CLI. Both short forms (`-v`, `-V`) now work.
|
|
43
|
+
- **A diff could lose its change signal entirely.** When the only fragment
|
|
44
|
+
covering a hunk was a whole-file chunk — flat data files, languages without a
|
|
45
|
+
tree-sitter parser, parse degradation — that chunk had no cheap variant to
|
|
46
|
+
fall back on, so an oversized core was skipped and the output carried *zero*
|
|
47
|
+
`role: "changed"` fragments while rendering the unchanged rest of the file as
|
|
48
|
+
context. Such cores now fall back to an excerpt: the changed lines plus three
|
|
49
|
+
lines of context, cut from the chunk itself and rendered as `kind: excerpt`,
|
|
50
|
+
`role: "changed"`. On the elasticsearch `muted-tests.yml` repro the output
|
|
51
|
+
goes from 121 fragments and no change signal to 118 fragments carrying the
|
|
52
|
+
appended entry, 5,337 tokens instead of 5,970 (#103). The whole-file
|
|
53
|
+
fragmentation of flat lists that the same repro shows is the separate
|
|
54
|
+
granularity issue (#105) and is unchanged.
|
|
55
|
+
- A diff whose selection came back empty rendered as a bare `name`/`type` stub
|
|
56
|
+
in every format: the writer gated *all* diff metadata on there being
|
|
57
|
+
fragments, so the commit message and the list of changed files — the only
|
|
58
|
+
actionable facts about such a run — were dropped. They are now always
|
|
59
|
+
written.
|
|
60
|
+
|
|
61
|
+
### Changed
|
|
62
|
+
|
|
63
|
+
- **Lock files no longer render their hunks in diff mode.** A dependency bump
|
|
64
|
+
is signal, the checksum churn carrying it is not: `diffctx . --diff` used to
|
|
65
|
+
spend 12 KB of a 48 KB output on a `Cargo.lock` chunk. The touched lock files
|
|
66
|
+
are now listed under `lockfile_changes:` (paths only, like `deleted_files:`),
|
|
67
|
+
mirroring the tree-mode ignore policy while keeping the fact that they
|
|
68
|
+
changed — on the reported range the output drops from 12,947 to 7,888 tokens
|
|
69
|
+
(#112). Recognized: `Cargo.lock`, `package-lock.json`, `npm-shrinkwrap.json`,
|
|
70
|
+
`yarn.lock`, `pnpm-lock.yaml`, `bun.lock(b)`, `deno.lock`, `Pipfile.lock`,
|
|
71
|
+
`poetry.lock`, `uv.lock`, `pdm.lock`, `composer.lock`, `Gemfile.lock`,
|
|
72
|
+
`flake.lock`, `go.sum`, `mix.lock`, `packages.lock.json`, `gradle.lockfile`,
|
|
73
|
+
`Package.resolved`, `cabal.project.freeze`. `--full` still renders their
|
|
74
|
+
content — it is the escape hatch that promises every fragment of the changed
|
|
75
|
+
files.
|
|
76
|
+
- Native binary CLI parity: `-f` is accepted as the short form of `--format`,
|
|
77
|
+
bare `--diff` means the working tree against `HEAD`, `--scoring` advertises
|
|
78
|
+
its accepted values, and every option carries help text.
|
|
79
|
+
- The standalone binary is covered by its own integration suite
|
|
80
|
+
(`crates/diffctx-native/tests/native_cli.rs`, run in CI): exit codes, both
|
|
81
|
+
version flags,
|
|
82
|
+
the token summary, `--quiet`, format validation and budget handling are now
|
|
83
|
+
contract-tested against real git repositories. Every parity defect above
|
|
84
|
+
shipped because nothing exercised the clap parser.
|
|
85
|
+
|
|
86
|
+
## [1.12.1] - 2026-07-23
|
|
87
|
+
|
|
88
|
+
### Added
|
|
89
|
+
|
|
90
|
+
- Standalone native binaries (linux x86_64/aarch64, macOS arm64, Windows
|
|
91
|
+
x64) are built and attached to every GitHub release. The README had
|
|
92
|
+
promised them since 1.9.x; releases only ever carried wheels and an sdist.
|
|
93
|
+
- The Rust engine is published to crates.io as the `diffctx` crate
|
|
94
|
+
(`cargo install diffctx` for the native CLI, `cargo add diffctx` to embed the
|
|
95
|
+
selection pipeline). Previously the name held only a reservation stub; the
|
|
96
|
+
crate now carries the released engine, starting at 1.12.0.
|
|
97
|
+
- Container image `ghcr.io/nikolay-e/diffctx` (linux amd64/arm64), built from
|
|
98
|
+
the release tag and smoke-tested against a real repository before the tag
|
|
99
|
+
moves: `docker run --rm -v "$PWD:/repo" ghcr.io/nikolay-e/diffctx . --diff HEAD~1`.
|
|
100
|
+
Mirrored to Docker Hub as `nikolajer/diffctx`.
|
|
101
|
+
- Packaging manifests generated from the release checksums: Scoop
|
|
102
|
+
(`packaging/scoop/diffctx.json`), AUR (`packaging/aur/`) and an npm wrapper
|
|
103
|
+
(`packaging/npm/`) that downloads the platform binary and verifies its
|
|
104
|
+
SHA-256 against the published checksum.
|
|
105
|
+
|
|
106
|
+
### Fixed
|
|
107
|
+
|
|
108
|
+
- The native binary silently emitted YAML for every unrecognized `--format`,
|
|
109
|
+
including `md` — the documented default of the Python CLI. It now accepts
|
|
110
|
+
only `yaml`/`json` and exits 2 on anything else.
|
|
111
|
+
|
|
10
112
|
## [1.12.0] - 2026-07-23
|
|
11
113
|
|
|
12
114
|
### Added
|
|
@@ -55,7 +157,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
55
157
|
log instead of re-running the builders, so generation cost stays 1x.
|
|
56
158
|
Verified: gitpod 8000s-hang -> 35.7s,
|
|
57
159
|
pytorch 1848s-SIGKILL -> 7.4s, mui/material-ui OOM class recovered.
|
|
58
|
-
Outputs are bit-identical (gated by `
|
|
160
|
+
Outputs are bit-identical (gated by `eval/analysis/equivalence_gate.py`).
|
|
59
161
|
|
|
60
162
|
### Known limitations
|
|
61
163
|
|
|
@@ -94,7 +196,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
94
196
|
- **The default `--format` is now `md` (Markdown), previously `yaml`.** Markdown
|
|
95
197
|
is ~7% more token-efficient than YAML on real diffs and is preferred by
|
|
96
198
|
reviewers for code-fragment-heavy output (evidence:
|
|
97
|
-
`
|
|
199
|
+
`datasets/real-world-diff/v1/`). YAML remains available via `-f yaml`.
|
|
98
200
|
This also changes the default stdout format of `treemapper` (a passthrough
|
|
99
201
|
wrapper over this engine); downstream scripts that parse the default output
|
|
100
202
|
must now pass `-f yaml` explicitly. (#104)
|
|
@@ -229,7 +331,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
229
331
|
optional `diffctx-mcp` server confines its filesystem reach via
|
|
230
332
|
`DIFFCTX_ALLOWED_PATHS`.
|
|
231
333
|
- Footer of `README.md` links `CHANGELOG.md`, `SECURITY.md`, and
|
|
232
|
-
`docs/parameter-strategy.md` so they are no longer orphaned.
|
|
334
|
+
`docs/engineering/parameter-strategy.md` so they are no longer orphaned.
|
|
233
335
|
|
|
234
336
|
### Changed
|
|
235
337
|
|
|
@@ -288,5 +390,5 @@ Earlier releases shipped as `treemapper`; see
|
|
|
288
390
|
<https://github.com/nikolay-e/diffctx/releases> for the corresponding GitHub
|
|
289
391
|
release notes (`1.0.0` through `1.6.1`).
|
|
290
392
|
|
|
291
|
-
[Unreleased]: https://github.com/nikolay-e/diffctx/compare/v1.
|
|
393
|
+
[Unreleased]: https://github.com/nikolay-e/diffctx/compare/v1.12.1...HEAD
|
|
292
394
|
[1.7.0]: https://github.com/nikolay-e/diffctx/compare/v1.6.1...v1.7.0
|
|
@@ -123,9 +123,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
|
|
123
123
|
|
|
124
124
|
[[package]]
|
|
125
125
|
name = "clap"
|
|
126
|
-
version = "4.6.
|
|
126
|
+
version = "4.6.4"
|
|
127
127
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
128
|
-
checksum = "
|
|
128
|
+
checksum = "d91e0c145792ef73a6ad36d27c75ac09f1832222a3c209689d90f534685ee5b7"
|
|
129
129
|
dependencies = [
|
|
130
130
|
"clap_builder",
|
|
131
131
|
"clap_derive",
|
|
@@ -145,14 +145,14 @@ dependencies = [
|
|
|
145
145
|
|
|
146
146
|
[[package]]
|
|
147
147
|
name = "clap_derive"
|
|
148
|
-
version = "4.6.
|
|
148
|
+
version = "4.6.4"
|
|
149
149
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
150
|
-
checksum = "
|
|
150
|
+
checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061"
|
|
151
151
|
dependencies = [
|
|
152
152
|
"heck",
|
|
153
153
|
"proc-macro2",
|
|
154
154
|
"quote",
|
|
155
|
-
"syn",
|
|
155
|
+
"syn 3.0.3",
|
|
156
156
|
]
|
|
157
157
|
|
|
158
158
|
[[package]]
|
|
@@ -194,7 +194,7 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
|
|
194
194
|
|
|
195
195
|
[[package]]
|
|
196
196
|
name = "diffctx"
|
|
197
|
-
version = "1.12.
|
|
197
|
+
version = "1.12.2"
|
|
198
198
|
dependencies = [
|
|
199
199
|
"anyhow",
|
|
200
200
|
"clap",
|
|
@@ -330,9 +330,9 @@ dependencies = [
|
|
|
330
330
|
|
|
331
331
|
[[package]]
|
|
332
332
|
name = "glob"
|
|
333
|
-
version = "0.3.
|
|
333
|
+
version = "0.3.4"
|
|
334
334
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
335
|
-
checksum = "
|
|
335
|
+
checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b"
|
|
336
336
|
|
|
337
337
|
[[package]]
|
|
338
338
|
name = "hashbrown"
|
|
@@ -500,7 +500,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
500
500
|
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
|
501
501
|
dependencies = [
|
|
502
502
|
"proc-macro2",
|
|
503
|
-
"syn",
|
|
503
|
+
"syn 2.0.117",
|
|
504
504
|
]
|
|
505
505
|
|
|
506
506
|
[[package]]
|
|
@@ -554,7 +554,7 @@ dependencies = [
|
|
|
554
554
|
"proc-macro2",
|
|
555
555
|
"pyo3-macros-backend",
|
|
556
556
|
"quote",
|
|
557
|
-
"syn",
|
|
557
|
+
"syn 2.0.117",
|
|
558
558
|
]
|
|
559
559
|
|
|
560
560
|
[[package]]
|
|
@@ -566,7 +566,7 @@ dependencies = [
|
|
|
566
566
|
"heck",
|
|
567
567
|
"proc-macro2",
|
|
568
568
|
"quote",
|
|
569
|
-
"syn",
|
|
569
|
+
"syn 2.0.117",
|
|
570
570
|
]
|
|
571
571
|
|
|
572
572
|
[[package]]
|
|
@@ -675,9 +675,9 @@ checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
|
|
|
675
675
|
|
|
676
676
|
[[package]]
|
|
677
677
|
name = "serde"
|
|
678
|
-
version = "1.0.
|
|
678
|
+
version = "1.0.229"
|
|
679
679
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
680
|
-
checksum = "
|
|
680
|
+
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
|
|
681
681
|
dependencies = [
|
|
682
682
|
"serde_core",
|
|
683
683
|
"serde_derive",
|
|
@@ -685,29 +685,29 @@ dependencies = [
|
|
|
685
685
|
|
|
686
686
|
[[package]]
|
|
687
687
|
name = "serde_core"
|
|
688
|
-
version = "1.0.
|
|
688
|
+
version = "1.0.229"
|
|
689
689
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
690
|
-
checksum = "
|
|
690
|
+
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
|
|
691
691
|
dependencies = [
|
|
692
692
|
"serde_derive",
|
|
693
693
|
]
|
|
694
694
|
|
|
695
695
|
[[package]]
|
|
696
696
|
name = "serde_derive"
|
|
697
|
-
version = "1.0.
|
|
697
|
+
version = "1.0.229"
|
|
698
698
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
699
|
-
checksum = "
|
|
699
|
+
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
|
700
700
|
dependencies = [
|
|
701
701
|
"proc-macro2",
|
|
702
702
|
"quote",
|
|
703
|
-
"syn",
|
|
703
|
+
"syn 3.0.3",
|
|
704
704
|
]
|
|
705
705
|
|
|
706
706
|
[[package]]
|
|
707
707
|
name = "serde_json"
|
|
708
|
-
version = "1.0.
|
|
708
|
+
version = "1.0.151"
|
|
709
709
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
710
|
-
checksum = "
|
|
710
|
+
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
|
|
711
711
|
dependencies = [
|
|
712
712
|
"indexmap",
|
|
713
713
|
"itoa",
|
|
@@ -783,6 +783,17 @@ dependencies = [
|
|
|
783
783
|
"unicode-ident",
|
|
784
784
|
]
|
|
785
785
|
|
|
786
|
+
[[package]]
|
|
787
|
+
name = "syn"
|
|
788
|
+
version = "3.0.3"
|
|
789
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
790
|
+
checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
|
|
791
|
+
dependencies = [
|
|
792
|
+
"proc-macro2",
|
|
793
|
+
"quote",
|
|
794
|
+
"unicode-ident",
|
|
795
|
+
]
|
|
796
|
+
|
|
786
797
|
[[package]]
|
|
787
798
|
name = "target-lexicon"
|
|
788
799
|
version = "0.13.5"
|
|
@@ -819,7 +830,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
|
|
819
830
|
dependencies = [
|
|
820
831
|
"proc-macro2",
|
|
821
832
|
"quote",
|
|
822
|
-
"syn",
|
|
833
|
+
"syn 2.0.117",
|
|
823
834
|
]
|
|
824
835
|
|
|
825
836
|
[[package]]
|
|
@@ -865,7 +876,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
|
|
865
876
|
dependencies = [
|
|
866
877
|
"proc-macro2",
|
|
867
878
|
"quote",
|
|
868
|
-
"syn",
|
|
879
|
+
"syn 2.0.117",
|
|
869
880
|
]
|
|
870
881
|
|
|
871
882
|
[[package]]
|
|
@@ -1459,7 +1470,7 @@ dependencies = [
|
|
|
1459
1470
|
"heck",
|
|
1460
1471
|
"indexmap",
|
|
1461
1472
|
"prettyplease",
|
|
1462
|
-
"syn",
|
|
1473
|
+
"syn 2.0.117",
|
|
1463
1474
|
"wasm-metadata",
|
|
1464
1475
|
"wit-bindgen-core",
|
|
1465
1476
|
"wit-component",
|
|
@@ -1475,7 +1486,7 @@ dependencies = [
|
|
|
1475
1486
|
"prettyplease",
|
|
1476
1487
|
"proc-macro2",
|
|
1477
1488
|
"quote",
|
|
1478
|
-
"syn",
|
|
1489
|
+
"syn 2.0.117",
|
|
1479
1490
|
"wit-bindgen-core",
|
|
1480
1491
|
"wit-bindgen-rust",
|
|
1481
1492
|
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: diffctx
|
|
3
|
-
Version: 1.12.
|
|
3
|
+
Version: 1.12.2
|
|
4
4
|
Classifier: Development Status :: 5 - Production/Stable
|
|
5
5
|
Classifier: Environment :: Console
|
|
6
6
|
Classifier: Intended Audience :: Developers
|
|
@@ -62,30 +62,35 @@ Provides-Extra: full
|
|
|
62
62
|
Provides-Extra: mcp
|
|
63
63
|
Provides-Extra: tree-sitter
|
|
64
64
|
License-File: LICENSE
|
|
65
|
-
Summary:
|
|
65
|
+
Summary: Smart git diff context for LLMs and AI agents: budgeted fragment selection, deterministic, MCP server included
|
|
66
66
|
Keywords: ai,chatgpt,claude,code-analysis,code-context,code-review,code-to-prompt,codebase,context,context-selection,diff-context,directory-tree,export,git-diff,json,llm,llm-context,mcp,model-context-protocol,tree,yaml
|
|
67
|
+
Home-Page: https://github.com/nikolay-e/diffctx
|
|
67
68
|
Author-email: Nikolay Eremeev <nikolay.eremeev@outlook.com>
|
|
68
69
|
License-Expression: Apache-2.0
|
|
69
70
|
Requires-Python: >=3.10
|
|
70
71
|
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
71
|
-
Project-URL: Changelog, https://github.com/nikolay-e/diffctx/
|
|
72
|
-
Project-URL:
|
|
72
|
+
Project-URL: Changelog, https://github.com/nikolay-e/diffctx/blob/main/CHANGELOG.md
|
|
73
|
+
Project-URL: Documentation, https://nikolay-e.github.io/diffctx/
|
|
74
|
+
Project-URL: Homepage, https://nikolay-e.github.io/diffctx/
|
|
73
75
|
Project-URL: Issues, https://github.com/nikolay-e/diffctx/issues
|
|
76
|
+
Project-URL: Paper, https://doi.org/10.5281/zenodo.18824579
|
|
74
77
|
Project-URL: Repository, https://github.com/nikolay-e/diffctx
|
|
75
78
|
|
|
76
79
|
# diffctx — smart diff context for LLM code review
|
|
77
80
|
|
|
78
81
|
[](https://github.com/nikolay-e/diffctx/actions/workflows/ci.yml)
|
|
79
82
|
[](https://pypi.org/project/diffctx/)
|
|
83
|
+
[](https://crates.io/crates/diffctx)
|
|
84
|
+
[](https://www.npmjs.com/package/diffctx)
|
|
80
85
|
[](https://pypi.org/project/diffctx/)
|
|
81
86
|
|
|
82
87
|
**diffctx selects the minimum code an LLM needs to review a git diff.**
|
|
83
|
-
Instead of pasting whole files, it walks the dependency graph from the
|
|
84
|
-
lines
|
|
88
|
+
Instead of pasting whole files, it walks the dependency graph outward from the
|
|
89
|
+
changed lines and stops once more context stops paying for itself.
|
|
85
90
|
|
|
86
91
|
> Coming from [`treemapper`](https://pypi.org/project/treemapper/)? That name is
|
|
87
|
-
> deprecated
|
|
88
|
-
>
|
|
92
|
+
> deprecated. Every command, flag, and API call works unchanged:
|
|
93
|
+
> `treemapper` → `diffctx`, `treemapper-mcp` → `diffctx-mcp`.
|
|
89
94
|
|
|
90
95
|
## Why not just use `tree` or repomix?
|
|
91
96
|
|
|
@@ -109,10 +114,26 @@ pip install diffctx # or: into an active environment
|
|
|
109
114
|
pipx install 'diffctx[mcp]' # + MCP server for AI assistants
|
|
110
115
|
```
|
|
111
116
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
117
|
+
Without Python:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
cargo install diffctx # native CLI from crates.io
|
|
121
|
+
npx diffctx . --diff HEAD~1 # npm wrapper over the native binary
|
|
122
|
+
docker run --rm -v "$PWD:/repo" ghcr.io/nikolay-e/diffctx . --diff HEAD~1
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
The image runs as a non-root user (uid 10001) and writes to stdout — the
|
|
126
|
+
native binary has no `-o` flag, so redirect to capture: `... --diff HEAD~1 >
|
|
127
|
+
context.yaml`.
|
|
128
|
+
|
|
129
|
+
Prebuilt binaries for linux (x86_64/aarch64), macOS (arm64) and Windows (x64)
|
|
130
|
+
are attached to every [release](https://github.com/nikolay-e/diffctx/releases/latest).
|
|
131
|
+
The native binary covers diff mode with YAML/JSON output; tree mode, Markdown
|
|
132
|
+
output, the `graph` subcommand and the MCP server live in the Python package.
|
|
133
|
+
`cargo add diffctx` embeds the pipeline in a Rust project — the library is
|
|
134
|
+
imported as `_diffctx` (`use _diffctx::pipeline::build_diff_context`), since
|
|
135
|
+
the crate doubles as the Python extension module
|
|
136
|
+
([docs.rs](https://docs.rs/diffctx)).
|
|
116
137
|
|
|
117
138
|
## Quick start
|
|
118
139
|
|
|
@@ -121,19 +142,18 @@ diffctx . --diff HEAD~1 # smart context for last commit → paste into Cla
|
|
|
121
142
|
diffctx . -f md -c # full codebase export → clipboard in Markdown
|
|
122
143
|
```
|
|
123
144
|
|
|
124
|
-

|
|
145
|
+

|
|
125
146
|
|
|
126
|
-
*`diffctx . --diff HEAD~1` selects only the fragments
|
|
127
|
-
|
|
128
|
-
instead of dumping every changed file in full.*
|
|
147
|
+
*`diffctx . --diff HEAD~1` selects only the fragments an LLM needs to review the
|
|
148
|
+
last commit, instead of dumping every changed file in full.*
|
|
129
149
|
|
|
130
150
|
## Diff context mode
|
|
131
151
|
|
|
132
|
-
Finds the minimal set of
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
152
|
+
Finds the minimal set of fragments needed to understand a change — imports,
|
|
153
|
+
callers, type definitions, config dependencies — across 50+ file types. It
|
|
154
|
+
builds a code graph (imports, co-changes, type refs), propagates relevance
|
|
155
|
+
outward from the changed lines, and stops when relevance drops below `--tau` or
|
|
156
|
+
the `--budget` token cap is hit.
|
|
137
157
|
|
|
138
158
|
| Flag | Default | Description |
|
|
139
159
|
|-------------|---------|--------------------------------------------------------------------------|
|
|
@@ -145,8 +165,10 @@ relevance from the changed lines outward, and stops when relevance drops below
|
|
|
145
165
|
| `--timeout` | 300 | Wall-clock deadline in seconds; on expiry diffctx exits 124 instead of hanging |
|
|
146
166
|
|
|
147
167
|
Calibration of `--alpha`, `--tau`, and the edge-weight priors:
|
|
148
|
-
[`docs/parameter-strategy.md`](docs/parameter-strategy.md).
|
|
149
|
-
|
|
168
|
+
[`docs/engineering/parameter-strategy.md`](docs/engineering/parameter-strategy.md).
|
|
169
|
+
Theory:
|
|
170
|
+
[diffctx: Budgeted Typed-Graph Retrieval for Diff-Aware Code Context
|
|
171
|
+
Selection (Zenodo, 2026)](https://doi.org/10.5281/zenodo.18824579).
|
|
150
172
|
|
|
151
173
|
### `graph` subcommand
|
|
152
174
|
|
|
@@ -181,11 +203,11 @@ diffctx . --diff HEAD~1 -c # diff context to clipboard
|
|
|
181
203
|
<!-- END USAGE -->
|
|
182
204
|
|
|
183
205
|
Every run reports token count and size on stderr — `12,847 tokens
|
|
184
|
-
(o200k_base), 52.3 KB` (tiktoken, the GPT-4o tokenizer; `~`-prefixed
|
|
185
|
-
|
|
186
|
-
`
|
|
187
|
-
|
|
188
|
-
`<
|
|
206
|
+
(o200k_base), 52.3 KB` (tiktoken, the GPT-4o tokenizer; `~`-prefixed above
|
|
207
|
+
1 MB). `-c/--copy` copies output via `pbcopy` (macOS), `clip` (Windows), or
|
|
208
|
+
`wl-copy`/`xclip`/`xsel` (Linux). Unreadable files become placeholders like
|
|
209
|
+
`<binary file: N bytes>`, `<file too large: N bytes>`, or
|
|
210
|
+
`<unreadable content: not utf-8>`.
|
|
189
211
|
|
|
190
212
|
## Python API
|
|
191
213
|
|
|
@@ -221,32 +243,42 @@ print(to_yaml(tree))
|
|
|
221
243
|
|
|
222
244
|
diffctx includes an [MCP](https://modelcontextprotocol.io) server that lets AI
|
|
223
245
|
assistants (Claude Code, Cursor, Windsurf, etc.) call diff context analysis
|
|
224
|
-
automatically during code review. Install with `pip install 'diffctx[mcp]'
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
"mcpServers": {
|
|
230
|
-
"diffctx": {
|
|
231
|
-
"command": "diffctx-mcp"
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
246
|
+
automatically during code review. Install with `pip install 'diffctx[mcp]'`,
|
|
247
|
+
then register it — for Claude Code:
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
claude mcp add diffctx -- diffctx-mcp
|
|
235
251
|
```
|
|
236
252
|
|
|
237
|
-
The server exposes
|
|
238
|
-
|
|
253
|
+
The server exposes three tools — `get_diff_context`, `get_tree_map`, and
|
|
254
|
+
`get_file_context` — that assistants call when reviewing PRs, explaining
|
|
255
|
+
changes, or investigating broken tests. Tool reference and configs for
|
|
239
256
|
Cursor, Continue, Windsurf, and Zed:
|
|
240
257
|
[`src/diffctx/mcp/README.md`](src/diffctx/mcp/README.md).
|
|
241
258
|
|
|
242
259
|
## Ignore patterns
|
|
243
260
|
|
|
244
261
|
Respects `.gitignore` and `.diffctx/ignore` automatically — hierarchically at
|
|
245
|
-
every directory level, with gitignore semantics (negation `!important.log`,
|
|
246
|
-
anchored `/root_only.txt`). `.diffctx/whitelist` acts as an include-only
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
262
|
+
every directory level, with full gitignore semantics (negation `!important.log`,
|
|
263
|
+
anchored `/root_only.txt`). `.diffctx/whitelist` acts as an include-only filter,
|
|
264
|
+
and the output file is always auto-ignored. `--no-default-ignores` disables the
|
|
265
|
+
built-in patterns; `--no-ignores` disables all ignore rules (tree mode only).
|
|
266
|
+
|
|
267
|
+
## Token cache
|
|
268
|
+
|
|
269
|
+
Diff mode caches per-blob tokenization under
|
|
270
|
+
`~/Library/Caches/diffctx/token-cache` (macOS),
|
|
271
|
+
`$XDG_CACHE_HOME/diffctx/token-cache` (Linux) or
|
|
272
|
+
`%LOCALAPPDATA%\diffctx\token-cache` (Windows). It is a pure speedup: deleting
|
|
273
|
+
it only costs one cold run.
|
|
274
|
+
|
|
275
|
+
| Variable | Effect |
|
|
276
|
+
|----------|--------|
|
|
277
|
+
| `DIFFCTX_TOKEN_CACHE_DIR` | Relocate the cache |
|
|
278
|
+
| `DIFFCTX_TOKEN_CACHE_MAX_BYTES` | Size cap, default `536870912` (512 MB); `0` disables eviction |
|
|
279
|
+
|
|
280
|
+
Eviction is amortized: each run trims one of the cache's 256 shards back under
|
|
281
|
+
its share of the cap, oldest entries first.
|
|
250
282
|
|
|
251
283
|
## Exit codes
|
|
252
284
|
|
|
@@ -256,18 +288,33 @@ disables the built-in patterns; `--no-ignores` disables all ignore rules
|
|
|
256
288
|
| `1` | Runtime error (bad path, permission denied, etc.) |
|
|
257
289
|
| `2` | Usage error (invalid flags/arguments) |
|
|
258
290
|
| `3` | Environment error (`--diff` outside a git repo, git not installed, no commits yet) |
|
|
259
|
-
| `4` | `--diff` produced no semantic context (clean tree, binary-only, everything filtered); output is still emitted. Deletion/rename-only diffs list `deleted_files`/`renamed_files` and exit `0` |
|
|
291
|
+
| `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` |
|
|
292
|
+
| `124`| `--diff` exceeded the `--timeout` wall-clock deadline |
|
|
260
293
|
| `130`| Interrupted (Ctrl-C) |
|
|
261
294
|
| `141`| Broken pipe (e.g. piping into `head`) |
|
|
262
295
|
|
|
296
|
+
## Repository layout
|
|
297
|
+
|
|
298
|
+
Rust product code lives in `crates/diffctx-native/`; the Python CLI and MCP
|
|
299
|
+
package live in `src/diffctx/`. Evaluation code, immutable datasets, and paper
|
|
300
|
+
artifacts are intentionally separated under `eval/`, `datasets/`, and `paper/`.
|
|
301
|
+
See [repository ownership boundaries](docs/architecture/repository-layout.md)
|
|
302
|
+
for the lifecycle and entry point of each area.
|
|
303
|
+
|
|
263
304
|
## License
|
|
264
305
|
|
|
265
306
|
Apache 2.0
|
|
266
307
|
|
|
308
|
+
<!-- mcp-name: io.github.nikolay-e/diffctx -->
|
|
309
|
+
<!-- Ownership marker read from the PyPI description by the MCP registry. -->
|
|
310
|
+
<!-- Must survive edits verbatim: one space after the colon, case-sensitive. -->
|
|
311
|
+
|
|
267
312
|
---
|
|
268
313
|
|
|
314
|
+
- [Documentation site](https://nikolay-e.github.io/diffctx/) — the pipeline
|
|
315
|
+
end to end: diff → fragments → graph → relevance → selection
|
|
269
316
|
- [Changelog](CHANGELOG.md)
|
|
270
317
|
- [Security policy](SECURITY.md) — threat model and vulnerability reporting
|
|
271
|
-
- [Parameter strategy](docs/parameter-strategy.md) — how `--alpha`,
|
|
318
|
+
- [Parameter strategy](docs/engineering/parameter-strategy.md) — how `--alpha`,
|
|
272
319
|
`--tau`, and edge weights are calibrated
|
|
273
320
|
|