diffctx 1.18.3__tar.gz → 1.19.0__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.18.3 → diffctx-1.19.0}/CHANGELOG.md +399 -1
- {diffctx-1.18.3 → diffctx-1.19.0}/Cargo.lock +14 -3
- diffctx-1.19.0/PKG-INFO +135 -0
- diffctx-1.19.0/README.md +75 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/Cargo.toml +10 -1
- diffctx-1.19.0/crates/diffctx-native/README.md +75 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/bindings.rs +277 -89
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/candidate_files.rs +15 -7
- diffctx-1.19.0/crates/diffctx-native/src/change_class.rs +637 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/core.rs +32 -3
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/discovery.rs +7 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/base.rs +172 -1
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/config_edges/generic.rs +40 -7
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/config_edges/helm.rs +7 -1
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/history/cochange.rs +25 -25
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/mod.rs +194 -23
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/jvm.rs +123 -2
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/nim.rs +51 -12
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/nix.rs +14 -2
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/ocaml.rs +14 -1
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/ruby.rs +31 -3
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/structural/testing.rs +27 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/effective_config.rs +1 -0
- diffctx-1.19.0/crates/diffctx-native/src/facts/kotlin.rs +1285 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/facts/mod.rs +1 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/fragmentation.rs +2 -1
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/git.rs +295 -65
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/graph.rs +6 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/graph_export.rs +113 -9
- diffctx-1.19.0/crates/diffctx-native/src/hook.rs +2751 -0
- diffctx-1.19.0/crates/diffctx-native/src/impact.rs +3860 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/in_memory_harness.rs +3 -1
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/interval.rs +12 -0
- diffctx-1.19.0/crates/diffctx-native/src/kotlin_bindings.rs +1054 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/lib.rs +1 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/locate.rs +10 -16
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/main.rs +74 -19
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/parsers/mod.rs +41 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/parsers/tree_sitter_strategy.rs +524 -19
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/pipeline.rs +479 -136
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/postpass.rs +33 -24
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/pybridge.rs +60 -15
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/render.rs +26 -21
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/resource.rs +85 -5
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/select.rs +226 -31
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/source.rs +79 -28
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/token_corpus.rs +3 -1
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/utility/objective.rs +2 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/pyproject.toml +7 -8
- {diffctx-1.18.3 → diffctx-1.19.0}/src/diffctx/_app.py +81 -28
- {diffctx-1.18.3 → diffctx-1.19.0}/src/diffctx/_diffctx.pyi +4 -3
- {diffctx-1.18.3 → diffctx-1.19.0}/src/diffctx/_native/graph_export.py +6 -6
- {diffctx-1.18.3 → diffctx-1.19.0}/src/diffctx/_native/pipeline.py +2 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/src/diffctx/cli.py +21 -10
- {diffctx-1.18.3 → diffctx-1.19.0}/src/diffctx/mcp/legacy.py +7 -4
- {diffctx-1.18.3 → diffctx-1.19.0}/src/diffctx/mcp/security.py +11 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/src/diffctx/mcp/server.py +58 -24
- diffctx-1.19.0/src/diffctx/version.py +1 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/src/diffctx/writer.py +2 -1
- diffctx-1.18.3/PKG-INFO +0 -372
- diffctx-1.18.3/README.md +0 -312
- diffctx-1.18.3/crates/diffctx-native/README.md +0 -312
- diffctx-1.18.3/crates/diffctx-native/src/change_class.rs +0 -169
- diffctx-1.18.3/crates/diffctx-native/src/hook.rs +0 -1768
- diffctx-1.18.3/crates/diffctx-native/src/impact.rs +0 -1848
- diffctx-1.18.3/src/diffctx/version.py +0 -1
- {diffctx-1.18.3 → diffctx-1.19.0}/Cargo.toml +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/LICENSE +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/analytics.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/config/analytics.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/config/bm25.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/config/budget.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/config/category_weights.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/config/edge_weights.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/config/env_overrides.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/config/extensions.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/config/filtering.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/config/fragmentation.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/config/git.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/config/graph_filtering.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/config/importance.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/config/limits.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/config/mod.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/config/mode.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/config/needs.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/config/parsers.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/config/render.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/config/scoring.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/config/selection.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/config/tokenization.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/config/weights.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/container.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/config_edges/build_system.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/config_edges/cicd.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/config_edges/docker.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/config_edges/kubernetes.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/config_edges/mod.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/document/mod.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/history/mod.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/ansible.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/bazel.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/c_family.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/cargo_edges.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/clojure.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/css.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/dart.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/dbt.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/dotnet.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/elixir.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/erlang.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/go.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/graphql.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/haskell.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/javascript.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/julia.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/latex.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/lua.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/mod.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/openapi.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/perl.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/php.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/prisma.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/protobuf.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/python.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/r_lang.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/rust_lang.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/shell.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/sql.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/swift.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/tags.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/terraform.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/semantic/zig.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/similarity/lexical.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/similarity/mod.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/structural/containment.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/structural/mod.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/edges/structural/sibling.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/excerpt.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/facts/scala.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/filtering.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/languages.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/mode.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/parsers/config_parser.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/parsers/generic.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/parsers/markdown.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/paths.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/peak_rss.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/ppr.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/project_graph.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/provenance.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/run_provenance.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/sanitize.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/scoring.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/signatures.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/stopwords/code.txt +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/stopwords/docs.txt +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/stopwords.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/test_rng.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/testfiles.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/tokenizer.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/types.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/utility/boltzmann.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/utility/importance.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/utility/mod.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/crates/diffctx-native/src/utility/needs.rs +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/rust-toolchain.toml +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/src/diffctx/__init__.py +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/src/diffctx/__main__.py +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/src/diffctx/_native/__init__.py +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/src/diffctx/_native/graph_analytics.py +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/src/diffctx/_native/project_graph.py +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/src/diffctx/clipboard.py +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/src/diffctx/ignore.py +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/src/diffctx/logger.py +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/src/diffctx/mcp/__init__.py +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/src/diffctx/mcp/__main__.py +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/src/diffctx/mcp/fetch.py +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/src/diffctx/mcp/launch.py +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/src/diffctx/py.typed +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/src/diffctx/tokens.py +0 -0
- {diffctx-1.18.3 → diffctx-1.19.0}/src/diffctx/tree.py +0 -0
|
@@ -7,6 +7,402 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.19.0] - 2026-10-14
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Kotlin (`.kt`, `.kts`) parses with tree-sitter, through
|
|
15
|
+
`brokk-tree-sitter-kotlin` 0.4.6 — the fwcd grammar as its maintained fork
|
|
16
|
+
publishes it, since fwcd's own crate still pins tree-sitter < 0.23. On
|
|
17
|
+
1,036 real files it lost no declaration and broke on none;
|
|
18
|
+
`tree-sitter-kotlin-sg` 0.4.1 silently dropped seven classes (a bodyless
|
|
19
|
+
annotated class swallowed the next one) and `tree-sitter-kotlin-ng` broke
|
|
20
|
+
whole classes on a `get(...)` call. Classes, interfaces, objects,
|
|
21
|
+
companions (`Companion` when unnamed), functions, extension functions,
|
|
22
|
+
secondary constructors (named after their class), properties (a property
|
|
23
|
+
spans its getter and setter; a local `val` is not a member) and type
|
|
24
|
+
aliases become fragments (#303). This reverses the 1.7.0 removal, which
|
|
25
|
+
dropped `.kt` read through the Java grammar, not a Kotlin one.
|
|
26
|
+
- Kotlin has a binding model, read off shared syntax facts (one parse per
|
|
27
|
+
blob, shared by the fragmenter, `--symbol` and impact): a use binds through
|
|
28
|
+
its import or alias, the declared package (not the directory), the
|
|
29
|
+
receiver's declared type — a local, a parameter, a property of the class
|
|
30
|
+
or of a source-visible base class, a constructor or a function with a
|
|
31
|
+
written return type, called plainly or on a typed name
|
|
32
|
+
(`val x = repo.find(id)`) — an object or companion qualifier, or the
|
|
33
|
+
implicit `this`, which in an `inner class` reaches the outer instance too.
|
|
34
|
+
What the syntax cannot settle stays a candidate with its reason
|
|
35
|
+
(`receiver_unresolved`, `overloaded`, `wildcard_import`, `dispatch`,
|
|
36
|
+
`project_dependency_unknown`); `internal` stops at the project, which its
|
|
37
|
+
test source set shares. Callable references (`Obj::f`) are references,
|
|
38
|
+
implementations of a changed abstract member are their own relation, a
|
|
39
|
+
removed declaration is found by comparing the file's declarations at the
|
|
40
|
+
base and the head, and a public contract changes when the header does —
|
|
41
|
+
parameter defaults and all. In the context graph a Kotlin member use links
|
|
42
|
+
only to the definition it resolves to: a candidate suggests, it does not
|
|
43
|
+
link.
|
|
44
|
+
|
|
45
|
+
### Security
|
|
46
|
+
|
|
47
|
+
- Impact and `--symbol` no longer name a withheld file that refers to the
|
|
48
|
+
change. 1.18.4's fix covered the changed side only: a file under
|
|
49
|
+
`.diffctx/ignore` or with a secret name that called a removed definition or
|
|
50
|
+
named a deleted path still appeared as a caller or a stale reference. The
|
|
51
|
+
withhold policy now applies inside the `git grep` readers, so every surface
|
|
52
|
+
gets only admissible files, and the files a read withheld count toward
|
|
53
|
+
`withheld_files`. The header says "N file(s) withheld by ignore rules"
|
|
54
|
+
rather than "N more", since they need not be changed files.
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
|
|
58
|
+
- **`A..B` is what `B` changed since it forked from `A`**, in every mode and
|
|
59
|
+
at every entry point (CLI, MCP, the hook, the Action): the range resolves
|
|
60
|
+
once to the merge base and `B`'s commit. Read as two trees, a base that
|
|
61
|
+
moved on after the fork put its own later commits into the change as
|
|
62
|
+
reverts — a PR's target branch in the Action (#485), a remote-tracking
|
|
63
|
+
ref a `pull` on the same line had just moved in the hook (#462, which now
|
|
64
|
+
also measures a push from where the push itself found the ref). The
|
|
65
|
+
Action deepens a shallow checkout until the fork point is known, and says
|
|
66
|
+
so when it is not. Histories with no common commit keep git's two-tree
|
|
67
|
+
reading.
|
|
68
|
+
- A range is answered from its snapshot alone. The Helm chart root was
|
|
69
|
+
probed on the disk, so a working tree without the range's `Chart.yaml`
|
|
70
|
+
dropped the chart's templates from pack and locate (#408). A snapshot
|
|
71
|
+
read that failed — out of time, cancelled, a broken stream — read as an
|
|
72
|
+
absent file and now names its limit; a snapshot listing that failed no
|
|
73
|
+
longer hands discovery or the token cache the working tree's files in its
|
|
74
|
+
place (#443).
|
|
75
|
+
- The changed-file count of uncommitted work is what git would commit.
|
|
76
|
+
Untracked files are analysed as before but named apart (`untracked_files`,
|
|
77
|
+
"2 changed file(s), 1 untracked"), and an untracked binary — a local
|
|
78
|
+
database, a build artefact — is no source change at all; two `.db` files
|
|
79
|
+
and a notes file read as "5 changed file(s)" for a 2-file edit (#418,
|
|
80
|
+
#423). A search for a name defined only in an untracked file is answered
|
|
81
|
+
with its callers, as the inspection that follows already was (#448).
|
|
82
|
+
- `--full A..B` listed a file the range deleted, or renamed away, as
|
|
83
|
+
changed and showed its old body read from `A`. One rule now names what a
|
|
84
|
+
range's head no longer holds, for every mode; staged pack, `--full` and
|
|
85
|
+
locate are pinned to read the index alone, renames and mode changes
|
|
86
|
+
included (#409).
|
|
87
|
+
- A one-second impact run ends near its second: the loops over input-sized
|
|
88
|
+
lists (stale-reference lines, unanalysed and namer files, test edges)
|
|
89
|
+
iterate through the deadline instead of polling it by hand, and the
|
|
90
|
+
stale-reference loop, which had no poll, ran such a run to 4.5 s (now
|
|
91
|
+
1.04 s, #510). A deadline that loop hit is named in `limits`; it was read
|
|
92
|
+
before the loop ran, so a run that had dropped its stale references
|
|
93
|
+
looked complete. `DIFFCTX_TEST_DEADLINE_AFTER_POLLS=n` stops a run at its
|
|
94
|
+
`n`th poll, so tests stop it at every point without racing the clock,
|
|
95
|
+
and a corpus case its deadline stopped says so instead of failing on a
|
|
96
|
+
score (#390).
|
|
97
|
+
- A call inside an f-string replacement field (`f"{fence(text)}"`) or a
|
|
98
|
+
template-literal substitution (`` `${formatBytes(n)}` ``) is a caller. The
|
|
99
|
+
hand lexer that impact and the binding resolver read code through
|
|
100
|
+
blanked every quoted byte, interpolations included, so both read
|
|
101
|
+
"0 callers" (#460, #475). Comments and string literals now come off the
|
|
102
|
+
file's grammar, interpolations kept, in every language with one; the
|
|
103
|
+
lexer and its special cases (`//` as floor division, a Rust lifetime,
|
|
104
|
+
`*` block-comment lines) are gone (#506).
|
|
105
|
+
- A removed definition is one the file declared at the base and does not
|
|
106
|
+
declare at the head, both read off the grammar. Read off removed lines by
|
|
107
|
+
regex, a Rust tail expression `Err("zero")` became a removed member named
|
|
108
|
+
`Err` with every `Err(` in the crate as its callers, a bare `int(text)` in
|
|
109
|
+
a Python method a removed `int`, and a private function next to an
|
|
110
|
+
`impl` a removed API (#515, #458). A member is as public as the class or
|
|
111
|
+
interface it sits in, by nesting, not by the nearest class above it.
|
|
112
|
+
- A caller of a removed definition is code that can still reach it:
|
|
113
|
+
through an import the binding model resolves to the file it left (a
|
|
114
|
+
removed member through its imported class), else from the same runtime.
|
|
115
|
+
By name alone, `subprocess.run`, another file's own `run`, a shell `die`
|
|
116
|
+
and a TypeScript `test.describe` were callers of a deleted Python script's
|
|
117
|
+
functions, and the import line was a second caller (#470, #471, #458,
|
|
118
|
+
#450). Files the range deleted resolve as files an import still names.
|
|
119
|
+
- A dependency bump is a change: impact names the lock files a range
|
|
120
|
+
changes (`lockfile_changes`) and never answers "No changes to analyse" for
|
|
121
|
+
them (#495).
|
|
122
|
+
- The hook's search pre-filter in the plugin script read the session's
|
|
123
|
+
cwd, not the Grep's own `path`, and ignored untracked files, so a search
|
|
124
|
+
over a pending change elsewhere never reached the binary. `bash -lc
|
|
125
|
+
"git commit …"` and `$(git -C r commit)` are named as unread rather than
|
|
126
|
+
logged as no trigger; reminders and gate denials are logged as such. A
|
|
127
|
+
cache directory the hook cannot write falls back to the temporary
|
|
128
|
+
directory instead of silently rerunning every preview (#407).
|
|
129
|
+
- Before a commit or push the hook says what it did when it has no impact
|
|
130
|
+
to show: one line when the change reaches nothing outside the diff, and
|
|
131
|
+
one when it could not read the command (a git inside a subshell, a `cd`
|
|
132
|
+
it cannot resolve), with the command to run instead. Silence then means
|
|
133
|
+
the hook did not run, not a clean review; the hook log keeps tens of
|
|
134
|
+
thousands of runs so an audit reads a whole session (#464).
|
|
135
|
+
- The MCP tool timed out at 300 s on a monorepo range the CLI answered in
|
|
136
|
+
four seconds (#457), for two reasons. It widened a subdirectory
|
|
137
|
+
`repo_path` to the whole repository; it now analyses the directory asked
|
|
138
|
+
about, as the CLI does with the same path. And impact grepped every
|
|
139
|
+
removed name as a fixed string in every file, so a deleted `call` matched
|
|
140
|
+
each `callback` in the repository's data and the run never stopped
|
|
141
|
+
(#413): the grep now reads whole words in code files only, at most 20
|
|
142
|
+
lines a file, and a name on more than 500 lines is answered "too common to
|
|
143
|
+
trace by name" instead of listed.
|
|
144
|
+
- Impact is fitted to its cap on the text the reader receives: markdown was
|
|
145
|
+
shed against the size of its JSON, so a 148-token answer cited a
|
|
146
|
+
2000-token cap and lost its contract block; an omitted count printed
|
|
147
|
+
under the previous list's heading (#451). `--budget` (CLI) and
|
|
148
|
+
`budget_tokens` (MCP) set the cap, which stayed 2000 whatever was asked,
|
|
149
|
+
and a shed answer says which flag lists the rest (#466).
|
|
150
|
+
- The MCP tool's refusal of `symbol` together with `diff_ref` or
|
|
151
|
+
`fragment_ids` names the call that answers: drop them and ask in
|
|
152
|
+
`mode="impact"`, which looks the name up in the working tree (#499).
|
|
153
|
+
- Impact no longer reads "not examined" as "nothing there". A failed `git
|
|
154
|
+
grep` or file listing marks the run partial (`read_failed`) instead of
|
|
155
|
+
dropping the removed definition's callers and the stale references with
|
|
156
|
+
exit 0; a failed snapshot listing no longer falls back to the working
|
|
157
|
+
tree. A changed code file over the size cap or unreadable is named in
|
|
158
|
+
`unanalysed_files` (it answered `"empty": true` while pack said
|
|
159
|
+
`file_too_large`). Limits follow pack's rule, so the contribution and
|
|
160
|
+
total-byte caps, which drop callers, are named; the per-node edge cap
|
|
161
|
+
every repository trips still is not. A caller whose edge that cap cut
|
|
162
|
+
faces the same tests an edge would have and stays a caller (`--symbol`
|
|
163
|
+
answered "0 callers" for a function its route handler calls). A
|
|
164
|
+
definition changed only in comments, formatting or position is listed
|
|
165
|
+
in `layout_only` and the answer says its callers were not traced, where
|
|
166
|
+
it said "No resolved static callers"; the hook's search answer says the
|
|
167
|
+
same instead of claiming none. Under a limit, "nothing calls them" reads
|
|
168
|
+
"no caller found in this partial run". Unparsed files that name a
|
|
169
|
+
camelCase definition are disclosed: identifiers are stored lowercased,
|
|
170
|
+
and the lookup compared them as written.
|
|
171
|
+
- Impact no longer calls a definition whose only callers are inside the diff
|
|
172
|
+
one that "nothing calls": a helper extracted from a function the same
|
|
173
|
+
commit changes read as dead code, on 62–75% of that list across three
|
|
174
|
+
ranges of this repository. Such definitions are listed under "Changed,
|
|
175
|
+
called only from inside the diff, no static test link" and in the new
|
|
176
|
+
`untested_called_in_diff` field; an in-diff source counts when it passes
|
|
177
|
+
the tests any caller passes. 1.18.3 never printed the clause, so 1.18.4
|
|
178
|
+
added the false statement (#441).
|
|
179
|
+
- A control statement removed from a method body is no removed member:
|
|
180
|
+
`match (a, b) {` in a Rust `impl` read as a removed API `match`, and
|
|
181
|
+
every file spelling the keyword became its caller. `when`, `try`,
|
|
182
|
+
`synchronized`, `using`, `lock`, `fixed`, `foreach` and `elseif` are
|
|
183
|
+
filtered with it (#492).
|
|
184
|
+
- Impact looks a fragment id up as the fragment, not as the signature
|
|
185
|
+
variant that shares its id when the signature spans the whole fragment: a
|
|
186
|
+
Kotlin class whose header ends in `) {` answered `--symbol` with "0
|
|
187
|
+
definition(s)".
|
|
188
|
+
- The JVM builder links an owner-qualified short member (`UI.DP`) to its
|
|
189
|
+
declaration. Names under three characters never linked by name, so a
|
|
190
|
+
constant read through its object was missed.
|
|
191
|
+
- A JVM framework annotation registers what it annotates and nothing else:
|
|
192
|
+
a class-level `@Component` or `@Service` no longer marks every method a
|
|
193
|
+
registered entry point (which hid its missing test link); method-level
|
|
194
|
+
entry points (`@GetMapping`, `@Scheduled`, `@Bean`, …) count only when the
|
|
195
|
+
file imports them from a framework package.
|
|
196
|
+
- Impact names a caller by its whole definition, not by the signature
|
|
197
|
+
variant the graph linked: a use in the body — a constructor call after a
|
|
198
|
+
long parameter list — was dropped from the caller's sites.
|
|
199
|
+
- A name shared across runtimes no runtime crosses is neither a caller nor
|
|
200
|
+
a test link: a Kotlin test no longer reads as guarding a Swift function of
|
|
201
|
+
the same name, and a Kotlin or Swift spelling of a Python or TypeScript
|
|
202
|
+
name is not its caller. JVM languages still call each other, JavaScript
|
|
203
|
+
its HTML and components, Swift and Objective-C their C (#487, #458).
|
|
204
|
+
- A changed symbol whose name is spelled in a file in view that did not
|
|
205
|
+
parse cleanly lists that file (`missed_in`, "callers may be missing")
|
|
206
|
+
instead of reading as one nothing calls (#454).
|
|
207
|
+
- JVM tests pair with their subject the way JVM projects name them:
|
|
208
|
+
`FooTest`, `FooTests`, `FooSpec` and `FooIT` beside `Foo` are discovered
|
|
209
|
+
and linked in both directions, and `src/<set>/<java|kotlin|scala|groovy>/`
|
|
210
|
+
is a source root, so a test importing `com.acme.Foo` reaches
|
|
211
|
+
`src/main/kotlin/com/acme/Foo.kt`. A related test file is not a guard: a
|
|
212
|
+
method its tests never call keeps "no static test link".
|
|
213
|
+
- `--symbol Owner.name` reads Kotlin's owners and receivers from the syntax:
|
|
214
|
+
`String.pad` names the extension, `Worker.Companion.create` the companion
|
|
215
|
+
member; and the declaration search that keeps a capped query's definition
|
|
216
|
+
file knows `fun` and `typealias`.
|
|
217
|
+
- A Kotlin type the text declares at the start of a line but the parse did
|
|
218
|
+
not read — a grammar's silent misreading, with no error node — marks the
|
|
219
|
+
file analysed only in part (0 of yay-tsa's 1,036 files with the shipped
|
|
220
|
+
grammar; local classes count as read).
|
|
221
|
+
- The parse outcome is recorded when the fragmenter parses a file, not
|
|
222
|
+
inferred from the fragments that came out: a file that parsed with errors
|
|
223
|
+
is reported as analysed only in part even when a definition survived, and
|
|
224
|
+
a timed-out parse is named as such.
|
|
225
|
+
|
|
226
|
+
- Impact names the changed code files it could not read for definitions —
|
|
227
|
+
no grammar for the language in this build, or a parse that broke — in its
|
|
228
|
+
header and in `unanalysed_files`, and never calls such a change empty: a
|
|
229
|
+
Kotlin-only commit read "0 callers outside the diff" and the commit hook
|
|
230
|
+
stayed silent (#454). A script that parses cleanly and defines nothing is
|
|
231
|
+
not listed.
|
|
232
|
+
- `--symbol` no longer answers only "no definition found" when the files
|
|
233
|
+
naming the symbol were read as text: it names them and the reason, so an
|
|
234
|
+
unparsed definition is not mistaken for a missing one (#454).
|
|
235
|
+
- Locate's `unparsed_files` uses the same test: code with no grammar in this
|
|
236
|
+
build (Kotlin, F#, Fortran) or a broken parse is listed and lowers
|
|
237
|
+
confidence; a script that parses cleanly and defines nothing (a
|
|
238
|
+
`build.gradle.kts`, a Python script of top-level statements) no longer is.
|
|
239
|
+
Data files (`.sql`, `.toml`, `.txt`) stay unlisted (#330).
|
|
240
|
+
|
|
241
|
+
### Changed
|
|
242
|
+
|
|
243
|
+
- **Plugin hook: a commit, merge, cherry-pick, revert or push is reviewed
|
|
244
|
+
from what git recorded, once the line ran** — read off the reflog (`HEAD`'s
|
|
245
|
+
last entry, a remote-tracking ref's "update by push"), never predicted
|
|
246
|
+
from the command text. The prediction took fifteen parser commits in
|
|
247
|
+
nine days and still reviewed another session's staged file for
|
|
248
|
+
`git commit --pathspec-from-file` instead of the paths the commit took
|
|
249
|
+
(#508). A loop, `xargs`, `git apply --cached`, `git --git-dir … commit` or
|
|
250
|
+
an escaped quote now stage what they stage, and the review says what the
|
|
251
|
+
commit took (#405, #436). A commit of a change already shown gets a
|
|
252
|
+
one-line pointer instead of silence (#501); a push of several refs
|
|
253
|
+
reviews the first and counts the rest; a `--squash` merge is reviewed by
|
|
254
|
+
the commit that records it. `L=/abs; cd $L && git commit` follows the
|
|
255
|
+
variable the line sets (#422). The opt-in strict gate still reviews
|
|
256
|
+
before git runs, and only what needs no prediction: the index a plain
|
|
257
|
+
`git commit` records; a commit line that stages for itself is denied with
|
|
258
|
+
the instruction to stage as its own command. Without the gate the impact
|
|
259
|
+
now arrives with the commit's result, which is when Claude Code delivered
|
|
260
|
+
the old preview too (#429).
|
|
261
|
+
- Claude plugin: the MCP server and the hooks launch `uvx diffctx[mcp]==X`
|
|
262
|
+
with no other option, and `plugin/uv.lock` pins the release's whole
|
|
263
|
+
dependency tree with PyPI hashes, the shape the plugin directory reads as a
|
|
264
|
+
locked launch ("Launcher lock invalid" before). `constraints.txt` is gone:
|
|
265
|
+
uv resolves diffctx's dependencies within their declared ranges, as every
|
|
266
|
+
other uvx install already did.
|
|
267
|
+
- CI and the pre-commit hook run the `impact`, `hook`, `bindings`, `staged`,
|
|
268
|
+
`git_faults`, `in_memory` and `context_schema` test targets, which no gate
|
|
269
|
+
ran before.
|
|
270
|
+
|
|
271
|
+
## [1.18.4] - 2026-10-07
|
|
272
|
+
|
|
273
|
+
### Security
|
|
274
|
+
|
|
275
|
+
- Impact no longer names a file the run withholds (`.diffctx/ignore`, a
|
|
276
|
+
secret name): a definition removed from it, its deletion or its rename
|
|
277
|
+
reached the answer while pack reported only a withheld count.
|
|
278
|
+
|
|
279
|
+
### Changed
|
|
280
|
+
|
|
281
|
+
- The MCP server runs on the MCP Python SDK 2.3 (`mcp>=2.3,<3`): it answers
|
|
282
|
+
the `server/discover` probe Claude Code opens with, instead of rejecting it
|
|
283
|
+
with 31 validation errors on every connect, and still accepts every
|
|
284
|
+
`initialize` revision back to 2024-11-05 (#366).
|
|
285
|
+
- `ChangeClass` gains `layout`: a file whose diff changes only whitespace,
|
|
286
|
+
comments, quote style, trailing commas, semicolons or import order. Pack
|
|
287
|
+
ranks it with `mechanical` (#370).
|
|
288
|
+
- `diffctx graph --level file|directory` now applies to `-f json`, `-f
|
|
289
|
+
graphml` and the `--summary` counts; without `--level` they stay per
|
|
290
|
+
fragment (#352).
|
|
291
|
+
|
|
292
|
+
### Fixed
|
|
293
|
+
|
|
294
|
+
- Plugin hook: the commit is previewed as the index it will start from — the
|
|
295
|
+
line's own `git add`/`git rm` replayed on a copy of the index, `-a`, a
|
|
296
|
+
pathspec with `--only` semantics, `-i`, `--amend`, a first commit, a linked
|
|
297
|
+
worktree — instead of the whole working tree; a here-document message is no
|
|
298
|
+
pathspec; a multi-commit cherry-pick is reviewed whole; `cd ~/repo` is
|
|
299
|
+
followed; a diff written to a file is no inspection; an empty answer stays
|
|
300
|
+
empty after the commit lands (#384, #373, #380, #377). The session hint
|
|
301
|
+
names `--symbol` again (#379).
|
|
302
|
+
- Impact: the header counts every caller its lines call untested, says
|
|
303
|
+
"callers unresolved" instead of "0 caller(s)" when no binding model could
|
|
304
|
+
resolve them, says when the run stopped early, and counts changed
|
|
305
|
+
definitions no test reaches (#386, #387, #335). A caller registered by a
|
|
306
|
+
decorator (`@register_node`, `@app.route`) is no longer called untested
|
|
307
|
+
(#388). Adding fields or parameters with defaults collapses the call sites
|
|
308
|
+
into one signature-compatible line (#388); formatting- or comment-only edits
|
|
309
|
+
are no contract and list no callers (#370). An unchanged caller in a changed
|
|
310
|
+
file is listed (#362). `pub` inside a `pub(crate)` module is no public API
|
|
311
|
+
(#359); a script named for a schema is no schema contract (#374). A deleted
|
|
312
|
+
path is matched by its path, a relative path, its module name or a
|
|
313
|
+
basename no other file has, never by a bare word (#369, #383). A removed
|
|
314
|
+
member and the remaining `?.()` calls to it are reported (#367).
|
|
315
|
+
- Impact on a large changed module took 74 s on one core; per-file indexes and
|
|
316
|
+
memoized test links bring it to 4 s with byte-identical output (#385). The
|
|
317
|
+
Python CLI's watchdog waits the binary's 30 s grace past `--timeout`, so a
|
|
318
|
+
run that stops at its deadline answers partially instead of exiting 124
|
|
319
|
+
with nothing (#382).
|
|
320
|
+
- MCP: `diff_ref="HEAD"` is documented as the uncommitted work, and a clean
|
|
321
|
+
tree answers with the hint that `HEAD~1..HEAD` is the last commit (#368).
|
|
322
|
+
- Selection: changed code is placed by evidence tier (production, tests,
|
|
323
|
+
config and docs, mechanical or layout edits, generated files) and by how
|
|
324
|
+
densely each piece carries the change, not cheapest-first; a core the
|
|
325
|
+
first pass could only place as its signature stub gets its body when the
|
|
326
|
+
difference fits (#296, #327). A Django migration ("Generated by Django")
|
|
327
|
+
is no longer a generated file ranked last (#311).
|
|
328
|
+
- Graph: a range's own commits no longer vouch for its files in the
|
|
329
|
+
co-change channel, which now links two files once, representative to
|
|
330
|
+
representative (#340). The tags fallback no longer links files of a
|
|
331
|
+
language its own builder parsed (a stdlib module name, a keyword); a key
|
|
332
|
+
most config files carry (`workflow_dispatch` in every CI workflow) links
|
|
333
|
+
no source file to all of them (#286, #297); a setting spelled whole
|
|
334
|
+
(`database_url` in two of four environment files) still links its reader.
|
|
335
|
+
A bare `--diff X` or a time window reads history only up to its base.
|
|
336
|
+
- Imports that spell paths resolve through one rule — next to the importing
|
|
337
|
+
file, then from the root, `..` folded: Nim `import vendor/palette`, Nix
|
|
338
|
+
`import ../vendor/palette.nix` (and a directory's `default.nix`), Ruby
|
|
339
|
+
`require_relative`; OCaml `open Palette` links `palette.ml` (#285). A
|
|
340
|
+
TypeScript import resolves with the nearest `tsconfig*.json`, its relative
|
|
341
|
+
`extends` folded in, not only the root one (#372).
|
|
342
|
+
- Plugin hook: `merge --squash`, `merge -S` and `cherry-pick -S` keep their
|
|
343
|
+
target; a pathspec commit previews the paths the index knows, never an
|
|
344
|
+
untracked file under them; `git apply`, `update-index`, `read-tree` and
|
|
345
|
+
the like before a commit preview the working tree; a step in another
|
|
346
|
+
repository stages nothing; the preview reads a copy of the index, so a
|
|
347
|
+
held index lock neither blocks it nor is taken; a first commit is checked
|
|
348
|
+
once it lands; a manual run that found nothing leaves no reminder;
|
|
349
|
+
`git status && git diff --cached` reviews the index; the `impact_gate`
|
|
350
|
+
option no longer silences PostToolUse answers; merging the branch's own
|
|
351
|
+
upstream (`git merge --ff-only origin/main`) is silence.
|
|
352
|
+
- Impact: a definition moved to another module names the importers of the
|
|
353
|
+
old one; removed methods are found in classes split into fragments, and
|
|
354
|
+
`def self.x`, `func (r T) x`, `const x = () =>` name `x`; a deletion below
|
|
355
|
+
a definition does not change it; a lexical test or caller link stops at
|
|
356
|
+
a sibling project's manifest; a function nested in another has callers
|
|
357
|
+
only inside it, and an untyped `x.name` attribute read is no possible
|
|
358
|
+
caller. Formatting-only detection keeps a docstring
|
|
359
|
+
that is not one, and no longer calls a change layout when it moves a line
|
|
360
|
+
out of its Python or YAML block, drops a one-element tuple's comma, edits
|
|
361
|
+
a mid-word `#` (`${#a}`), or removes a `;` before a line starting with `(`
|
|
362
|
+
or `[` in JS/TS. Compatible extension requires the new field to be
|
|
363
|
+
appended, and a relative path to a moved file is a stale reference.
|
|
364
|
+
- `--symbol` accepts `Owner.name`, `Owner::name` and dotted names; a
|
|
365
|
+
malformed one exits 2 in both CLIs, and over MCP a malformed or unknown
|
|
366
|
+
symbol is a refusal the agent can read, not "Error executing tool"; a
|
|
367
|
+
name in more than 256 files keeps its definition past the cap.
|
|
368
|
+
- The Python budget fitter drops a `layout` witness with `mechanical` ones
|
|
369
|
+
and ranks `unknown` with content, as the native CLI does.
|
|
370
|
+
- Release: post-publish smokes wait until both pip and uv resolve the version
|
|
371
|
+
and fail with a named error after 10 minutes; dispatched runs are found by
|
|
372
|
+
dispatch time (#364).
|
|
373
|
+
- Plugin hook: a path git cannot hash (an untracked nested repository, a
|
|
374
|
+
moved submodule) no longer turns the answer into silence logged as
|
|
375
|
+
"clean"; a multi-commit cherry-pick touching a non-UTF-8 file is reviewed;
|
|
376
|
+
a `cd` that a search printed is not taken for where it ran. `--diff staged`
|
|
377
|
+
writes a copy of the index, so a held `index.lock` is no "unmerged entries?".
|
|
378
|
+
- Impact: a Rust type and its `impl` blocks are one definition with its
|
|
379
|
+
callers, a trait method is reached through its trait (`Display`, not
|
|
380
|
+
`std`), and an unresolved name counts definitions, not "files" (#403).
|
|
381
|
+
Python `//` hides no call; a root `"baseUrl": "."` resolves bare
|
|
382
|
+
specifiers and overlapping `paths` take the longest prefix (#414). A
|
|
383
|
+
non-ASCII path, a `diff.context` setting, a deleted call statement, a
|
|
384
|
+
deletion right under a caller, a column-0 line removed from a body and a
|
|
385
|
+
call still naming a moved Rust function's old module no longer hide or
|
|
386
|
+
invent callers; a body call gaining an argument and an added `__slots__`
|
|
387
|
+
are not signature-compatible; `pub mod r#type;` and an attributed
|
|
388
|
+
`pub mod` are public; a same-language sibling module importing the
|
|
389
|
+
qualified name keeps its test link. `--symbol Owner.name` answers only for
|
|
390
|
+
that owner, and `PATH:Owner::name` parses. A call spread into an object
|
|
391
|
+
(`...workboxPolicy({..})`) is a call, not a member access: four Vite
|
|
392
|
+
configs read as zero callers.
|
|
393
|
+
- Impact counts the files the run withholds instead of reading them as
|
|
394
|
+
nothing: the header says `N more withheld by ignore rules` for a range and
|
|
395
|
+
`N file(s) naming it withheld` for `--symbol`, and that their calls and
|
|
396
|
+
tests were not read (`withheld_files` in the JSON) (#392).
|
|
397
|
+
- Impact: a definition moved within its file with its text unchanged lists
|
|
398
|
+
no callers, as a formatting-only edit does; moved and edited, it still
|
|
399
|
+
does. `@value.setter`, `@attr.s`, `@define`, `@enum.unique` and Lombok
|
|
400
|
+
annotations shape a definition and register it nowhere, so an untested
|
|
401
|
+
setter reads untested instead of "registered by" (#414).
|
|
402
|
+
- The CLI and the MCP server report only the engine's own "nothing defines
|
|
403
|
+
it" as a refusal; a `KeyError` or `IndexError` is an internal error with its
|
|
404
|
+
traceback. Git output is decoded as UTF-8 on every platform.
|
|
405
|
+
|
|
10
406
|
## [1.18.3] - 2026-10-04
|
|
11
407
|
|
|
12
408
|
### Added
|
|
@@ -1903,7 +2299,9 @@ Earlier releases shipped as `treemapper`; see
|
|
|
1903
2299
|
<https://github.com/nikolay-e/diffctx/releases> for the corresponding GitHub
|
|
1904
2300
|
release notes (`1.0.0` through `1.6.1`).
|
|
1905
2301
|
|
|
1906
|
-
[Unreleased]: https://github.com/nikolay-e/diffctx/compare/v1.
|
|
2302
|
+
[Unreleased]: https://github.com/nikolay-e/diffctx/compare/v1.19.0...HEAD
|
|
2303
|
+
[1.19.0]: https://github.com/nikolay-e/diffctx/compare/v1.18.4...v1.19.0
|
|
2304
|
+
[1.18.4]: https://github.com/nikolay-e/diffctx/compare/v1.18.3...v1.18.4
|
|
1907
2305
|
[1.18.3]: https://github.com/nikolay-e/diffctx/compare/v1.18.2...v1.18.3
|
|
1908
2306
|
[1.18.2]: https://github.com/nikolay-e/diffctx/compare/v1.18.1...v1.18.2
|
|
1909
2307
|
[1.18.1]: https://github.com/nikolay-e/diffctx/compare/v1.18.0...v1.18.1
|
|
@@ -94,6 +94,16 @@ version = "2.11.1"
|
|
|
94
94
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
95
95
|
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
|
|
96
96
|
|
|
97
|
+
[[package]]
|
|
98
|
+
name = "brokk-tree-sitter-kotlin"
|
|
99
|
+
version = "0.4.6"
|
|
100
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
101
|
+
checksum = "11536d4605247354ad8992b7ae605e85508c07d64ed0710ff8494558660e40d0"
|
|
102
|
+
dependencies = [
|
|
103
|
+
"cc",
|
|
104
|
+
"tree-sitter-language",
|
|
105
|
+
]
|
|
106
|
+
|
|
97
107
|
[[package]]
|
|
98
108
|
name = "bstr"
|
|
99
109
|
version = "1.13.1"
|
|
@@ -107,9 +117,9 @@ dependencies = [
|
|
|
107
117
|
|
|
108
118
|
[[package]]
|
|
109
119
|
name = "cc"
|
|
110
|
-
version = "1.2.
|
|
120
|
+
version = "1.2.56"
|
|
111
121
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
112
|
-
checksum = "
|
|
122
|
+
checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2"
|
|
113
123
|
dependencies = [
|
|
114
124
|
"find-msvc-tools",
|
|
115
125
|
"shlex",
|
|
@@ -194,10 +204,11 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
|
|
194
204
|
|
|
195
205
|
[[package]]
|
|
196
206
|
name = "diffctx"
|
|
197
|
-
version = "1.
|
|
207
|
+
version = "1.19.0"
|
|
198
208
|
dependencies = [
|
|
199
209
|
"aho-corasick",
|
|
200
210
|
"anyhow",
|
|
211
|
+
"brokk-tree-sitter-kotlin",
|
|
201
212
|
"clap",
|
|
202
213
|
"dunce",
|
|
203
214
|
"glob",
|
diffctx-1.19.0/PKG-INFO
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: diffctx
|
|
3
|
+
Version: 1.19.0
|
|
4
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
5
|
+
Classifier: Environment :: Console
|
|
6
|
+
Classifier: Intended Audience :: Developers
|
|
7
|
+
Classifier: Operating System :: OS Independent
|
|
8
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Classifier: Topic :: Software Development
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
17
|
+
Classifier: Topic :: Utilities
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Dist: pathspec>=0.11,<2.0
|
|
20
|
+
Requires-Dist: black>=23.0.0,<27.0 ; extra == 'dev'
|
|
21
|
+
Requires-Dist: charset-normalizer>=3.0,<4.0 ; extra == 'dev'
|
|
22
|
+
Requires-Dist: coverage>=7.0,<8.0 ; extra == 'dev'
|
|
23
|
+
Requires-Dist: hypothesis>=6.0,<7.0 ; extra == 'dev'
|
|
24
|
+
Requires-Dist: import-linter>=2.0,<3.0 ; extra == 'dev'
|
|
25
|
+
Requires-Dist: mypy>=1.0,<3.0 ; extra == 'dev'
|
|
26
|
+
Requires-Dist: pre-commit>=3.0,<5.0 ; extra == 'dev'
|
|
27
|
+
Requires-Dist: pygit2>=1.12,<2.0 ; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=7.0,<10.0 ; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-asyncio>=0.23,<2.0 ; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest-cov>=3.0,<8.0 ; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-timeout>=2.1,<3.0 ; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest-xdist>=3.0,<4.0 ; extra == 'dev'
|
|
33
|
+
Requires-Dist: pyyaml>=6.0.2,<8.0 ; extra == 'dev'
|
|
34
|
+
Requires-Dist: radon>=6.0,<7.0 ; extra == 'dev'
|
|
35
|
+
Requires-Dist: ruff>=0.4,<1.0 ; extra == 'dev'
|
|
36
|
+
Requires-Dist: tiktoken>=0.9,<1.0 ; extra == 'dev'
|
|
37
|
+
Requires-Dist: types-pyyaml>=6.0,<7.0 ; extra == 'dev'
|
|
38
|
+
Requires-Dist: charset-normalizer>=3.0,<4.0 ; extra == 'full'
|
|
39
|
+
Requires-Dist: anyio>=4.5,<5.0 ; extra == 'mcp'
|
|
40
|
+
Requires-Dist: mcp>=2.3,<3 ; extra == 'mcp'
|
|
41
|
+
Provides-Extra: dev
|
|
42
|
+
Provides-Extra: full
|
|
43
|
+
Provides-Extra: mcp
|
|
44
|
+
License-File: LICENSE
|
|
45
|
+
Summary: Selects the minimum code an LLM needs to review a git diff
|
|
46
|
+
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
|
|
47
|
+
Home-Page: https://diffctx.com/
|
|
48
|
+
Author-email: Nikolay Eremeev <nikolay.eremeev@outlook.com>
|
|
49
|
+
License-Expression: Apache-2.0
|
|
50
|
+
Requires-Python: >=3.10
|
|
51
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
52
|
+
Project-URL: Author, https://nikolay-eremeev.com
|
|
53
|
+
Project-URL: Changelog, https://github.com/nikolay-e/diffctx/blob/main/CHANGELOG.md
|
|
54
|
+
Project-URL: Documentation, https://diffctx.com/
|
|
55
|
+
Project-URL: Homepage, https://diffctx.com/
|
|
56
|
+
Project-URL: Issues, https://github.com/nikolay-e/diffctx/issues
|
|
57
|
+
Project-URL: Paper, https://doi.org/10.5281/zenodo.18824579
|
|
58
|
+
Project-URL: Repository, https://github.com/nikolay-e/diffctx
|
|
59
|
+
|
|
60
|
+
# diffctx — what a git change reaches, and the code to review it
|
|
61
|
+
|
|
62
|
+
[](https://github.com/nikolay-e/diffctx/actions/workflows/ci.yml)
|
|
63
|
+
[](https://pypi.org/project/diffctx/)
|
|
64
|
+
[](https://crates.io/crates/diffctx)
|
|
65
|
+
[](https://www.npmjs.com/package/diffctx)
|
|
66
|
+
[](https://registry.modelcontextprotocol.io/v0/servers?search=io.github.nikolay-e/diffctx)
|
|
67
|
+
|
|
68
|
+
**diffctx tells an agent or a reviewer what a change reaches outside its
|
|
69
|
+
diff** — the callers, the tests that reach them, the contracts it crosses —
|
|
70
|
+
**and selects the code needed to understand it** under a token budget. Local,
|
|
71
|
+
deterministic, no index, no model calls. Caller resolution is static and per
|
|
72
|
+
language; where it cannot resolve, the answer says so instead of printing a
|
|
73
|
+
zero.
|
|
74
|
+
|
|
75
|
+
> Formerly published as `treemapper` — every command, flag, and API call works unchanged.
|
|
76
|
+
|
|
77
|
+
## Install
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# Claude Code: the MCP server, /diffctx:impact, and hooks that run it on each commit and push
|
|
81
|
+
claude plugin marketplace add nikolay-e/diffctx
|
|
82
|
+
claude plugin install diffctx@diffctx
|
|
83
|
+
|
|
84
|
+
# any MCP client
|
|
85
|
+
claude mcp add diffctx -- uvx --from 'diffctx[mcp]' diffctx-mcp
|
|
86
|
+
|
|
87
|
+
# CLI, zero-install
|
|
88
|
+
uvx diffctx . --diff HEAD~1
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
pipx, pip, cargo, npm, Docker, Scoop, other MCP clients and the Python API:
|
|
92
|
+
[integrations](docs/product/integrations.md).
|
|
93
|
+
|
|
94
|
+
## Usage
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
diffctx . --diff --mode impact # uncommitted work: callers outside the diff, their tests, contracts
|
|
98
|
+
diffctx . --symbol parse_config # the same for a name, no change needed
|
|
99
|
+
diffctx . --diff main...feature # the code to review a branch, packed under the auto budget
|
|
100
|
+
diffctx . --diff HEAD~1 --budget 12000 # the last commit, capped at 12k o200k tokens
|
|
101
|
+
diffctx . --diff 24h --mode locate # today's work as ranked JSON, no source bodies
|
|
102
|
+
diffctx . # whole-tree export, Markdown
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`--diff` takes a git range, `staged`, or a duration window ending now (`24h`,
|
|
106
|
+
`90min`, `2w`). Every flag, default and exit code:
|
|
107
|
+
[command-line reference](docs/product/cli.md).
|
|
108
|
+
|
|
109
|
+

|
|
110
|
+
|
|
111
|
+
## How it compares
|
|
112
|
+
|
|
113
|
+
Whole-repo packers export everything; code-graph servers answer queries
|
|
114
|
+
against a maintained index. diffctx is **diff-seeded**: the input is a change,
|
|
115
|
+
the output is what it touches and what explains it. Measured results, and when
|
|
116
|
+
the other two fit better: [COMPARISON.md](COMPARISON.md).
|
|
117
|
+
|
|
118
|
+
## More
|
|
119
|
+
|
|
120
|
+
- [Documentation site](https://diffctx.com/) — the pipeline end to end
|
|
121
|
+
- [Command-line reference](docs/product/cli.md) — rendered from `diffctx --help`
|
|
122
|
+
- [Integrations](docs/product/integrations.md) — install, MCP clients, Python API
|
|
123
|
+
- [FAQ](docs/product/faq.md) — heuristic or oracle, ignore rules, monorepos, secrets
|
|
124
|
+
- [Token counting](docs/product/token-budget.md) — `--budget` for non-GPT models
|
|
125
|
+
- [GitHub Action](docs/product/github-action.md) — diff context as a CI step
|
|
126
|
+
- [Benchmarks](BENCHMARKS.md) and the [paper](https://doi.org/10.5281/zenodo.18824579)
|
|
127
|
+
- [Changelog](CHANGELOG.md) · [Security](SECURITY.md)
|
|
128
|
+
- [Parameter strategy](docs/engineering/parameter-strategy.md)
|
|
129
|
+
|
|
130
|
+
Apache 2.0
|
|
131
|
+
|
|
132
|
+
<!-- mcp-name: io.github.nikolay-e/diffctx -->
|
|
133
|
+
<!-- Ownership marker read from the PyPI description by the MCP registry. -->
|
|
134
|
+
<!-- Must survive edits verbatim: one space after the colon, case-sensitive. -->
|
|
135
|
+
|