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.
Files changed (167) hide show
  1. {diffctx-1.12.0 → diffctx-1.12.2}/CHANGELOG.md +106 -4
  2. {diffctx-1.12.0/diffctx → diffctx-1.12.2}/Cargo.lock +35 -24
  3. diffctx-1.12.2/Cargo.toml +12 -0
  4. {diffctx-1.12.0 → diffctx-1.12.2}/PKG-INFO +95 -48
  5. {diffctx-1.12.0 → diffctx-1.12.2}/README.md +88 -44
  6. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/Cargo.toml +10 -18
  7. diffctx-1.12.2/crates/diffctx-native/README.md +241 -0
  8. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/env_overrides.rs +1 -1
  9. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/limits.rs +0 -1
  10. diffctx-1.12.2/crates/diffctx-native/src/excerpt.rs +191 -0
  11. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/lib.rs +1 -0
  12. diffctx-1.12.2/crates/diffctx-native/src/main.rs +244 -0
  13. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/memory_pipeline.rs +4 -0
  14. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/pipeline.rs +91 -2
  15. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/postpass.rs +12 -6
  16. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/pybridge.rs +7 -0
  17. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/render.rs +6 -0
  18. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/select.rs +12 -1
  19. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/test_harness.rs +1 -1
  20. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/token_corpus.rs +154 -0
  21. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/types.rs +10 -0
  22. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/utility/scoring.rs +2 -0
  23. {diffctx-1.12.0 → diffctx-1.12.2}/pyproject.toml +19 -14
  24. {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/__init__.py +2 -2
  25. {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/__main__.py +1 -1
  26. diffctx-1.12.0/src/diffctx/main.py → diffctx-1.12.2/src/diffctx/_app.py +10 -10
  27. {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/cli.py +12 -1
  28. {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/mcp/README.md +8 -4
  29. diffctx-1.12.2/src/diffctx/mcp/__main__.py +4 -0
  30. {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/mcp/server.py +32 -6
  31. diffctx-1.12.2/src/diffctx/version.py +1 -0
  32. {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/writer.py +20 -1
  33. diffctx-1.12.0/diffctx/src/main.rs +0 -123
  34. diffctx-1.12.0/diffctx/tests/common/mod.rs +0 -274
  35. diffctx-1.12.0/diffctx/tests/fixtures/garbage/garbage_api.py +0 -31
  36. diffctx-1.12.0/diffctx/tests/fixtures/garbage/garbage_constants.py +0 -11
  37. diffctx-1.12.0/diffctx/tests/fixtures/garbage/garbage_handlers.py +0 -27
  38. diffctx-1.12.0/diffctx/tests/fixtures/garbage/garbage_models.py +0 -25
  39. diffctx-1.12.0/diffctx/tests/fixtures/garbage/garbage_module.js +0 -23
  40. diffctx-1.12.0/diffctx/tests/fixtures/garbage/garbage_services.py +0 -37
  41. diffctx-1.12.0/diffctx/tests/fixtures/garbage/garbage_types.py +0 -31
  42. diffctx-1.12.0/diffctx/tests/fixtures/garbage/garbage_unrelated.yaml +0 -8
  43. diffctx-1.12.0/diffctx/tests/fixtures/garbage/garbage_utils.py +0 -31
  44. diffctx-1.12.0/diffctx/tests/fixtures/garbage/garbage_validators.py +0 -23
  45. diffctx-1.12.0/diffctx/tests/yaml_cases.rs +0 -276
  46. diffctx-1.12.0/src/diffctx/mcp/__main__.py +0 -26
  47. diffctx-1.12.0/src/diffctx/version.py +0 -1
  48. {diffctx-1.12.0 → diffctx-1.12.2}/LICENSE +0 -0
  49. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/analytics.rs +0 -0
  50. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/candidate_files.rs +0 -0
  51. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/analytics.rs +0 -0
  52. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/bm25.rs +0 -0
  53. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/budget.rs +0 -0
  54. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/category_weights.rs +0 -0
  55. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/edge_weights.rs +0 -0
  56. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/extensions.rs +0 -0
  57. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/filtering.rs +0 -0
  58. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/fragmentation.rs +0 -0
  59. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/git.rs +0 -0
  60. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/graph_filtering.rs +0 -0
  61. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/importance.rs +0 -0
  62. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/mod.rs +0 -0
  63. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/mode.rs +0 -0
  64. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/needs.rs +0 -0
  65. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/parsers.rs +0 -0
  66. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/render.rs +0 -0
  67. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/scoring.rs +0 -0
  68. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/selection.rs +0 -0
  69. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/tokenization.rs +0 -0
  70. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/config/weights.rs +0 -0
  71. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/core.rs +0 -0
  72. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/discovery.rs +0 -0
  73. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/base.rs +0 -0
  74. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/config_edges/build_system.rs +0 -0
  75. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/config_edges/cicd.rs +0 -0
  76. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/config_edges/docker.rs +0 -0
  77. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/config_edges/generic.rs +0 -0
  78. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/config_edges/helm.rs +0 -0
  79. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/config_edges/kubernetes.rs +0 -0
  80. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/config_edges/mod.rs +0 -0
  81. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/document/mod.rs +0 -0
  82. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/history/cochange.rs +0 -0
  83. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/history/mod.rs +0 -0
  84. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/mod.rs +0 -0
  85. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/ansible.rs +0 -0
  86. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/bazel.rs +0 -0
  87. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/c_family.rs +0 -0
  88. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/cargo_edges.rs +0 -0
  89. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/clojure.rs +0 -0
  90. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/css.rs +0 -0
  91. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/dart.rs +0 -0
  92. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/dbt.rs +0 -0
  93. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/dotnet.rs +0 -0
  94. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/elixir.rs +0 -0
  95. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/erlang.rs +0 -0
  96. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/go.rs +0 -0
  97. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/graphql.rs +0 -0
  98. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/haskell.rs +0 -0
  99. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/javascript.rs +0 -0
  100. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/julia.rs +0 -0
  101. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/jvm.rs +0 -0
  102. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/latex.rs +0 -0
  103. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/lua.rs +0 -0
  104. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/mod.rs +0 -0
  105. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/nim.rs +0 -0
  106. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/nix.rs +0 -0
  107. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/ocaml.rs +0 -0
  108. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/openapi.rs +0 -0
  109. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/perl.rs +0 -0
  110. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/php.rs +0 -0
  111. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/prisma.rs +0 -0
  112. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/protobuf.rs +0 -0
  113. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/python.rs +0 -0
  114. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/r_lang.rs +0 -0
  115. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/ruby.rs +0 -0
  116. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/rust_lang.rs +0 -0
  117. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/shell.rs +0 -0
  118. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/sql.rs +0 -0
  119. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/swift.rs +0 -0
  120. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/tags.rs +0 -0
  121. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/terraform.rs +0 -0
  122. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/semantic/zig.rs +0 -0
  123. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/similarity/lexical.rs +0 -0
  124. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/similarity/mod.rs +0 -0
  125. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/structural/containment.rs +0 -0
  126. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/structural/mod.rs +0 -0
  127. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/structural/sibling.rs +0 -0
  128. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/edges/structural/testing.rs +0 -0
  129. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/filtering.rs +0 -0
  130. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/fragmentation.rs +0 -0
  131. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/git.rs +0 -0
  132. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/graph.rs +0 -0
  133. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/graph_export.rs +0 -0
  134. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/interval.rs +0 -0
  135. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/languages.rs +0 -0
  136. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/mode.rs +0 -0
  137. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/parsers/config_parser.rs +0 -0
  138. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/parsers/generic.rs +0 -0
  139. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/parsers/markdown.rs +0 -0
  140. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/parsers/mod.rs +0 -0
  141. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/parsers/tree_sitter_strategy.rs +0 -0
  142. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/peak_rss.rs +0 -0
  143. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/ppr.rs +0 -0
  144. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/project_graph.rs +0 -0
  145. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/scoring.rs +0 -0
  146. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/signatures.rs +0 -0
  147. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/stopwords.rs +0 -0
  148. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/tokenizer.rs +0 -0
  149. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/utility/boltzmann.rs +0 -0
  150. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/utility/importance.rs +0 -0
  151. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/utility/mod.rs +0 -0
  152. {diffctx-1.12.0/diffctx → diffctx-1.12.2/crates/diffctx-native}/src/utility/needs.rs +0 -0
  153. {diffctx-1.12.0 → diffctx-1.12.2}/rust-toolchain.toml +0 -0
  154. {diffctx-1.12.0/src/diffctx/diffctx → diffctx-1.12.2/src/diffctx/_native}/__init__.py +0 -0
  155. {diffctx-1.12.0/src/diffctx/diffctx → diffctx-1.12.2/src/diffctx/_native}/graph_analytics.py +0 -0
  156. {diffctx-1.12.0/src/diffctx/diffctx → diffctx-1.12.2/src/diffctx/_native}/graph_export.py +0 -0
  157. {diffctx-1.12.0/src/diffctx/diffctx → diffctx-1.12.2/src/diffctx/_native}/pipeline.py +0 -0
  158. {diffctx-1.12.0/src/diffctx/diffctx → diffctx-1.12.2/src/diffctx/_native}/project_graph.py +0 -0
  159. {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/clipboard.py +0 -0
  160. {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/ignore.py +0 -0
  161. {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/logger.py +0 -0
  162. {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/mcp/__init__.py +0 -0
  163. {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/mcp/formatting.py +0 -0
  164. {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/mcp/security.py +0 -0
  165. {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/py.typed +0 -0
  166. {diffctx-1.12.0 → diffctx-1.12.2}/src/diffctx/tokens.py +0 -0
  167. {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 `benchmarks/equivalence_gate.py`).
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
- `benchmarks/real_world_diff_bench/`). YAML remains available via `-f yaml`.
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.7.0...HEAD
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.2"
126
+ version = "4.6.4"
127
127
  source = "registry+https://github.com/rust-lang/crates.io-index"
128
- checksum = "dd059f9da4f5c36b3787f65d38ccaab1cc315f07b01f89abc8359ee6a8205011"
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.1"
148
+ version = "4.6.4"
149
149
  source = "registry+https://github.com/rust-lang/crates.io-index"
150
- checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
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.0"
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.3"
333
+ version = "0.3.4"
334
334
  source = "registry+https://github.com/rust-lang/crates.io-index"
335
- checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
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.228"
678
+ version = "1.0.229"
679
679
  source = "registry+https://github.com/rust-lang/crates.io-index"
680
- checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
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.228"
688
+ version = "1.0.229"
689
689
  source = "registry+https://github.com/rust-lang/crates.io-index"
690
- checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
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.228"
697
+ version = "1.0.229"
698
698
  source = "registry+https://github.com/rust-lang/crates.io-index"
699
- checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
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.150"
708
+ version = "1.0.151"
709
709
  source = "registry+https://github.com/rust-lang/crates.io-index"
710
- checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
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
  ]
@@ -0,0 +1,12 @@
1
+ [workspace]
2
+ members = ["crates/diffctx-native"]
3
+ resolver = "2"
4
+
5
+ [profile.release]
6
+ opt-level = 3
7
+ lto = "fat"
8
+ codegen-units = 1
9
+ strip = true
10
+ panic = "abort"
11
+ debug = "line-tables-only"
12
+ split-debuginfo = "packed"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: diffctx
3
- Version: 1.12.0
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: Export codebase structure and contents for AI/LLM context
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/releases
72
- Project-URL: Homepage, https://github.com/nikolay-e/diffctx
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
  [![CI](https://github.com/nikolay-e/diffctx/actions/workflows/ci.yml/badge.svg)](https://github.com/nikolay-e/diffctx/actions/workflows/ci.yml)
79
82
  [![PyPI](https://img.shields.io/pypi/v/diffctx)](https://pypi.org/project/diffctx/)
83
+ [![crates.io](https://img.shields.io/crates/v/diffctx)](https://crates.io/crates/diffctx)
84
+ [![npm](https://img.shields.io/npm/v/diffctx)](https://www.npmjs.com/package/diffctx)
80
85
  [![License](https://img.shields.io/pypi/l/diffctx)](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 changed
84
- lines outward and stops as soon as additional context stops paying for itself.
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 — it was a thin wrapper around diffctx. Every command, flag, and
88
- > API call works unchanged: `treemapper` → `diffctx`, `treemapper-mcp` → `diffctx-mcp`.
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
- A standalone binary (no Python required) is on the
113
- [releases page](https://github.com/nikolay-e/diffctx/releases/latest).
114
- The `[tree-sitter]` extra adds AST-level parsing for more accurate context
115
- selection across 30+ languages.
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
- ![diffctx demo](https://raw.githubusercontent.com/nikolay-e/diffctx/main/docs/demo.gif)
145
+ ![diffctx demo](https://raw.githubusercontent.com/nikolay-e/diffctx/main/docs/demo/demo.gif)
125
146
 
126
- *`diffctx . --diff HEAD~1` selects only the fragments functions, imports,
127
- type definitions that an LLM actually needs to review the last commit,
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 code fragments needed to understand a change —
133
- imports, callers, type definitions, config dependencies — across 50+ file
134
- types. It builds a code graph (imports, co-changes, type refs), propagates
135
- relevance from the changed lines outward, and stops when relevance drops below
136
- `--tau` or the `--budget` token cap is reached.
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). Theory:
149
- [Context-Selection for Git Diff (Zenodo, 2026)](https://doi.org/10.5281/zenodo.18824580).
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
- approximation above 1 MB). `-c/--copy` sends output to the clipboard via
186
- `pbcopy` (macOS), `clip` (Windows), or `wl-copy`/`xclip`/`xsel` (Linux).
187
- Unreadable files are replaced by placeholders such as `<binary file: N bytes>`,
188
- `<file too large: N bytes>`, or `<unreadable content: not utf-8>`.
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
- and add to your MCP client config (e.g. `~/.claude/mcp.json` for Claude Code):
226
-
227
- ```json
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 a `get_diff_context` tool that assistants call when
238
- reviewing PRs, explaining changes, or investigating broken tests. Configs for
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
- filter, and the output file is always auto-ignored. `--no-default-ignores`
248
- disables the built-in patterns; `--no-ignores` disables all ignore rules
249
- (tree mode only).
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