diffctx 1.12.1__tar.gz → 1.12.3__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 (162) hide show
  1. {diffctx-1.12.1 → diffctx-1.12.3}/CHANGELOG.md +161 -8
  2. {diffctx-1.12.1/diffctx → diffctx-1.12.3}/Cargo.lock +45 -34
  3. diffctx-1.12.3/Cargo.toml +20 -0
  4. {diffctx-1.12.1 → diffctx-1.12.3}/PKG-INFO +123 -60
  5. diffctx-1.12.3/README.md +271 -0
  6. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/Cargo.toml +4 -13
  7. diffctx-1.12.3/crates/diffctx-native/README.md +271 -0
  8. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/analytics.rs +53 -14
  9. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/candidate_files.rs +26 -2
  10. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/config/analytics.rs +0 -2
  11. diffctx-1.12.3/crates/diffctx-native/src/config/env_overrides.rs +273 -0
  12. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/config/limits.rs +0 -1
  13. diffctx-1.12.3/crates/diffctx-native/src/discovery.rs +579 -0
  14. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/mod.rs +5 -2
  15. diffctx-1.12.3/crates/diffctx-native/src/excerpt.rs +191 -0
  16. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/filtering.rs +93 -0
  17. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/git.rs +567 -9
  18. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/graph.rs +340 -30
  19. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/graph_export.rs +57 -0
  20. diffctx-1.12.3/crates/diffctx-native/src/interval.rs +210 -0
  21. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/languages.rs +69 -0
  22. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/lib.rs +1 -0
  23. diffctx-1.12.3/crates/diffctx-native/src/main.rs +244 -0
  24. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/memory_pipeline.rs +4 -0
  25. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/parsers/generic.rs +55 -0
  26. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/parsers/tree_sitter_strategy.rs +517 -1
  27. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/pipeline.rs +229 -21
  28. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/postpass.rs +110 -6
  29. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/ppr.rs +231 -0
  30. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/pybridge.rs +32 -189
  31. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/render.rs +121 -7
  32. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/select.rs +268 -4
  33. diffctx-1.12.3/crates/diffctx-native/src/signatures.rs +312 -0
  34. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/test_harness.rs +1 -1
  35. diffctx-1.12.3/crates/diffctx-native/src/token_corpus.rs +493 -0
  36. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/types.rs +10 -0
  37. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/utility/scoring.rs +2 -0
  38. {diffctx-1.12.1 → diffctx-1.12.3}/pyproject.toml +22 -15
  39. {diffctx-1.12.1 → diffctx-1.12.3}/src/diffctx/__init__.py +2 -2
  40. {diffctx-1.12.1 → diffctx-1.12.3}/src/diffctx/__main__.py +1 -1
  41. diffctx-1.12.1/src/diffctx/main.py → diffctx-1.12.3/src/diffctx/_app.py +55 -21
  42. {diffctx-1.12.1/src/diffctx/diffctx → diffctx-1.12.3/src/diffctx/_native}/pipeline.py +47 -4
  43. {diffctx-1.12.1 → diffctx-1.12.3}/src/diffctx/cli.py +77 -3
  44. {diffctx-1.12.1 → diffctx-1.12.3}/src/diffctx/mcp/README.md +18 -5
  45. diffctx-1.12.3/src/diffctx/mcp/__main__.py +4 -0
  46. {diffctx-1.12.1 → diffctx-1.12.3}/src/diffctx/mcp/security.py +23 -5
  47. {diffctx-1.12.1 → diffctx-1.12.3}/src/diffctx/mcp/server.py +168 -35
  48. diffctx-1.12.3/src/diffctx/version.py +1 -0
  49. {diffctx-1.12.1 → diffctx-1.12.3}/src/diffctx/writer.py +49 -7
  50. diffctx-1.12.1/README.md +0 -210
  51. diffctx-1.12.1/diffctx/README.md +0 -210
  52. diffctx-1.12.1/diffctx/src/config/env_overrides.rs +0 -125
  53. diffctx-1.12.1/diffctx/src/discovery.rs +0 -293
  54. diffctx-1.12.1/diffctx/src/interval.rs +0 -78
  55. diffctx-1.12.1/diffctx/src/main.rs +0 -126
  56. diffctx-1.12.1/diffctx/src/signatures.rs +0 -144
  57. diffctx-1.12.1/diffctx/src/token_corpus.rs +0 -228
  58. diffctx-1.12.1/src/diffctx/mcp/__main__.py +0 -26
  59. diffctx-1.12.1/src/diffctx/version.py +0 -1
  60. {diffctx-1.12.1 → diffctx-1.12.3}/LICENSE +0 -0
  61. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/config/bm25.rs +0 -0
  62. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/config/budget.rs +0 -0
  63. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/config/category_weights.rs +0 -0
  64. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/config/edge_weights.rs +0 -0
  65. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/config/extensions.rs +0 -0
  66. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/config/filtering.rs +0 -0
  67. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/config/fragmentation.rs +0 -0
  68. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/config/git.rs +0 -0
  69. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/config/graph_filtering.rs +0 -0
  70. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/config/importance.rs +0 -0
  71. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/config/mod.rs +0 -0
  72. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/config/mode.rs +0 -0
  73. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/config/needs.rs +0 -0
  74. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/config/parsers.rs +0 -0
  75. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/config/render.rs +0 -0
  76. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/config/scoring.rs +0 -0
  77. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/config/selection.rs +0 -0
  78. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/config/tokenization.rs +0 -0
  79. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/config/weights.rs +0 -0
  80. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/core.rs +0 -0
  81. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/base.rs +0 -0
  82. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/config_edges/build_system.rs +0 -0
  83. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/config_edges/cicd.rs +0 -0
  84. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/config_edges/docker.rs +0 -0
  85. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/config_edges/generic.rs +0 -0
  86. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/config_edges/helm.rs +0 -0
  87. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/config_edges/kubernetes.rs +0 -0
  88. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/config_edges/mod.rs +0 -0
  89. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/document/mod.rs +0 -0
  90. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/history/cochange.rs +0 -0
  91. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/history/mod.rs +0 -0
  92. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/ansible.rs +0 -0
  93. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/bazel.rs +0 -0
  94. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/c_family.rs +0 -0
  95. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/cargo_edges.rs +0 -0
  96. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/clojure.rs +0 -0
  97. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/css.rs +0 -0
  98. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/dart.rs +0 -0
  99. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/dbt.rs +0 -0
  100. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/dotnet.rs +0 -0
  101. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/elixir.rs +0 -0
  102. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/erlang.rs +0 -0
  103. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/go.rs +0 -0
  104. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/graphql.rs +0 -0
  105. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/haskell.rs +0 -0
  106. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/javascript.rs +0 -0
  107. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/julia.rs +0 -0
  108. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/jvm.rs +0 -0
  109. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/latex.rs +0 -0
  110. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/lua.rs +0 -0
  111. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/mod.rs +0 -0
  112. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/nim.rs +0 -0
  113. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/nix.rs +0 -0
  114. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/ocaml.rs +0 -0
  115. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/openapi.rs +0 -0
  116. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/perl.rs +0 -0
  117. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/php.rs +0 -0
  118. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/prisma.rs +0 -0
  119. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/protobuf.rs +0 -0
  120. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/python.rs +0 -0
  121. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/r_lang.rs +0 -0
  122. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/ruby.rs +0 -0
  123. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/rust_lang.rs +0 -0
  124. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/shell.rs +0 -0
  125. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/sql.rs +0 -0
  126. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/swift.rs +0 -0
  127. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/tags.rs +0 -0
  128. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/terraform.rs +0 -0
  129. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/semantic/zig.rs +0 -0
  130. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/similarity/lexical.rs +0 -0
  131. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/similarity/mod.rs +0 -0
  132. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/structural/containment.rs +0 -0
  133. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/structural/mod.rs +0 -0
  134. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/structural/sibling.rs +0 -0
  135. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/edges/structural/testing.rs +0 -0
  136. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/fragmentation.rs +0 -0
  137. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/mode.rs +0 -0
  138. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/parsers/config_parser.rs +0 -0
  139. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/parsers/markdown.rs +0 -0
  140. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/parsers/mod.rs +0 -0
  141. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/peak_rss.rs +0 -0
  142. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/project_graph.rs +0 -0
  143. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/scoring.rs +0 -0
  144. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/stopwords.rs +0 -0
  145. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/tokenizer.rs +0 -0
  146. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/utility/boltzmann.rs +0 -0
  147. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/utility/importance.rs +0 -0
  148. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/utility/mod.rs +0 -0
  149. {diffctx-1.12.1/diffctx → diffctx-1.12.3/crates/diffctx-native}/src/utility/needs.rs +0 -0
  150. {diffctx-1.12.1 → diffctx-1.12.3}/rust-toolchain.toml +0 -0
  151. {diffctx-1.12.1/src/diffctx/diffctx → diffctx-1.12.3/src/diffctx/_native}/__init__.py +0 -0
  152. {diffctx-1.12.1/src/diffctx/diffctx → diffctx-1.12.3/src/diffctx/_native}/graph_analytics.py +0 -0
  153. {diffctx-1.12.1/src/diffctx/diffctx → diffctx-1.12.3/src/diffctx/_native}/graph_export.py +0 -0
  154. {diffctx-1.12.1/src/diffctx/diffctx → diffctx-1.12.3/src/diffctx/_native}/project_graph.py +0 -0
  155. {diffctx-1.12.1 → diffctx-1.12.3}/src/diffctx/clipboard.py +0 -0
  156. {diffctx-1.12.1 → diffctx-1.12.3}/src/diffctx/ignore.py +0 -0
  157. {diffctx-1.12.1 → diffctx-1.12.3}/src/diffctx/logger.py +0 -0
  158. {diffctx-1.12.1 → diffctx-1.12.3}/src/diffctx/mcp/__init__.py +0 -0
  159. {diffctx-1.12.1 → diffctx-1.12.3}/src/diffctx/mcp/formatting.py +0 -0
  160. {diffctx-1.12.1 → diffctx-1.12.3}/src/diffctx/py.typed +0 -0
  161. {diffctx-1.12.1 → diffctx-1.12.3}/src/diffctx/tokens.py +0 -0
  162. {diffctx-1.12.1 → diffctx-1.12.3}/src/diffctx/tree.py +0 -0
@@ -9,6 +9,157 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ### Added
11
11
 
12
+ - **Scoop install on Windows.** This repository is now itself the bucket:
13
+ `scoop bucket add diffctx https://github.com/nikolay-e/diffctx` then
14
+ `scoop install diffctx/diffctx`. The manifest moved from `packaging/scoop/`
15
+ to `bucket/diffctx.json`, where `scoop bucket add` reads it, so CD pushing
16
+ the regenerated manifest to `main` is the publication step. Previously the
17
+ manifest was correct but unreachable — no bucket existed, so
18
+ `scoop search diffctx` found nothing.
19
+
20
+ - `--with-raw-diff` bundles git's raw unified diff ahead of the selected
21
+ context in every Python-CLI format (md/yaml/json/txt) and in the Python API
22
+ (`build_diff_context(..., with_raw_diff=True)`). Additive only — selection is
23
+ byte-identical with and without it — and not charged to `--budget`; the
24
+ stderr token summary reports the real output size and breaks out the patch's
25
+ share. Lock-file (#112), ignored and secret-like sections stay omitted. Not
26
+ in the native binary or the MCP server yet (#150).
27
+ - Token counting is documented explicitly: every count and `--budget` use
28
+ tiktoken `o200k_base` (GPT-4o family) and are approximate for other model
29
+ families — `docs/product/token-budget.md`, plus a `Token counting` section in
30
+ `--help` (#150).
31
+ - A GitHub Action (`action.yml`) runs diffctx as a CI step and exposes the
32
+ context file, its exact token count and an `empty` flag as step outputs, so a
33
+ downstream job can feed an LLM without re-tokenizing
34
+ (`docs/product/github-action.md`, #145).
35
+ - The Claude Code plugin declares its MCP server self-bootstrapping:
36
+ `uvx --from 'diffctx[mcp]' diffctx-mcp`. Installing the plugin no longer
37
+ assumes `pip install 'diffctx[mcp]'` has already happened — `uv` fetches it
38
+ on first use. (The entry point rather than the `diffctx mcp` subcommand: the
39
+ subcommand only exists from this release on, so on any older version the
40
+ bare command would try to map a directory named `mcp`.)
41
+ - `diffctx mcp` starts the MCP server, alongside the existing `diffctx-mcp`
42
+ console script. MCP registries publish a *package* name, and clients that
43
+ derive the executable from it run `diffctx` — which started a tree-mapping
44
+ run and wrote 31 MB of the working directory into the protocol transport
45
+ instead of speaking MCP. Both spellings now reach the same server.
46
+
47
+ ### Security
48
+
49
+ - **A diff range could re-enable repository-configured diff commands.** The
50
+ range validator allowed a leading `-`, so `--ext-diff` or `--textconv` passed
51
+ as a "range" landed in argv *after* the `--no-ext-diff --no-textconv` that
52
+ every diff invocation sets — undoing them and letting `.gitattributes` run
53
+ external commands. Neither side of a range may now begin with a dash, and
54
+ single revisions are additionally rejected for whitespace or control
55
+ characters (a newline split one `cat-file --batch` request into two). Refs
56
+ starting with a dash are unaddressable on a git command line, so nothing
57
+ legitimate is lost.
58
+ - MCP tool calls had no wall-clock deadline — the CLI's watchdog covered only
59
+ the CLI, leaving parse, fragmentation and scoring unbounded on the MCP path.
60
+ All three tools now share the CLI's 300 s default.
61
+ - `SECURITY.md` documents prompt injection via repository content honestly: it
62
+ is inherent to moving repository text into a model's context, diffctx does
63
+ not detect or neutralize it, and output must be treated as untrusted.
64
+
65
+ ### Fixed
66
+
67
+ - **A changed file could vanish from the diff entirely when `.gitignore`
68
+ excluded its parent directory.** `check-ignore --no-index` — required for
69
+ `.diffctx/ignore` to apply to tracked files — also revives git's rule that a
70
+ file cannot be re-included once a parent directory is excluded. pandoc
71
+ excludes every dotted root entry with `/*.*` and re-includes `!.github/**`;
72
+ git keeps the tracked workflow file, diffctx dropped it, and a one-file
73
+ commit rendered as an empty selection with exit 4. An exclusion inherited
74
+ from an excluded ancestor no longer counts; a pattern matching the path
75
+ itself still excludes it, so `.diffctx/ignore` and per-directory
76
+ `.gitignore` rules are unaffected (#153).
77
+
78
+ - **The container images ran as root.** The published 1.12.1 image had no
79
+ `USER` directive, so every `docker run` executed as uid 0 while the README
80
+ promised an unprivileged user. The image now runs as uid 10001.
81
+ - **The MCP server advertised the SDK's version as its own.** `FastMCP` takes
82
+ no version argument, so `initialize` reported the installed `mcp` package
83
+ version (e.g. `1.28.1`) as the diffctx server version — a number that drifts
84
+ with every SDK bump and never matched the shipped package. Clients now see
85
+ the real version.
86
+ - **The native binary hard-capped every run at 4096 tokens.** `--budget`
87
+ carried a fixed clap default instead of leaving the budget unset, so the
88
+ auto-sizing the Python CLI has always used never ran on the binary shipped
89
+ via crates.io, npm, the container images and the release archives — the
90
+ same command returned a truncated selection there (14-file
91
+ self-eat range: 15 fragments across 10 files, against 172 across 44). The
92
+ binary now defaults to auto sizing, and `--budget -1` means unlimited as it
93
+ does in Python.
94
+ - The token cache grew without bound (a machine that had analyzed a few dozen
95
+ repositories reached 817k files / 4.0 GB). It is now capped at 512 MB,
96
+ overridable with `DIFFCTX_TOKEN_CACHE_MAX_BYTES` (`0` disables eviction);
97
+ each run trims one of the 256 shards back under its share of the cap,
98
+ oldest entries first (#122).
99
+ - The native binary exited `0` on a diff that produced no semantic context
100
+ (clean tree, binary-only, everything over the size cap), so callers on the
101
+ binary channels could not tell an empty selection from a successful one. It
102
+ now mirrors the Python CLI: the `no semantic context` warning plus its hint
103
+ on stderr and exit code `4`.
104
+ - The native binary printed no token summary, although the README documents
105
+ one for every run. It now writes `N tokens (o200k_base), SIZE` to stderr,
106
+ silenced by the new `-q/--quiet`.
107
+ - `diffctx -v` was a usage error on the native binary while it printed the
108
+ version on the Python CLI. Both short forms (`-v`, `-V`) now work.
109
+ - **A diff could lose its change signal entirely.** When the only fragment
110
+ covering a hunk was a whole-file chunk — flat data files, languages without a
111
+ tree-sitter parser, parse degradation — that chunk had no cheap variant to
112
+ fall back on, so an oversized core was skipped and the output carried *zero*
113
+ `role: "changed"` fragments while rendering the unchanged rest of the file as
114
+ context. Such cores now fall back to an excerpt: the changed lines plus three
115
+ lines of context, cut from the chunk itself and rendered as `kind: excerpt`,
116
+ `role: "changed"`. On the elasticsearch `muted-tests.yml` repro the output
117
+ goes from 121 fragments and no change signal to 118 fragments carrying the
118
+ appended entry, 5,337 tokens instead of 5,970 (#103). The whole-file
119
+ fragmentation of flat lists that the same repro shows is the separate
120
+ granularity issue (#105) and is unchanged.
121
+ - A diff whose selection came back empty rendered as a bare `name`/`type` stub
122
+ in every format: the writer gated *all* diff metadata on there being
123
+ fragments, so the commit message and the list of changed files — the only
124
+ actionable facts about such a run — were dropped. They are now always
125
+ written.
126
+
127
+ ### Removed
128
+
129
+ - **AUR support.** `diffctx-bin` had never been submitted (the AUR RPC reported
130
+ `resultcount: 0`), the publishing job gated on a credential this repository
131
+ does not hold, and every release regenerated a PKGBUILD nobody could install.
132
+ Arch users are served by `pipx install diffctx` and `cargo install diffctx`.
133
+
134
+ ### Changed
135
+
136
+ - **Lock files no longer render their hunks in diff mode.** A dependency bump
137
+ is signal, the checksum churn carrying it is not: `diffctx . --diff` used to
138
+ spend 12 KB of a 48 KB output on a `Cargo.lock` chunk. The touched lock files
139
+ are now listed under `lockfile_changes:` (paths only, like `deleted_files:`),
140
+ mirroring the tree-mode ignore policy while keeping the fact that they
141
+ changed — on the reported range the output drops from 12,947 to 7,888 tokens
142
+ (#112). Recognized: `Cargo.lock`, `package-lock.json`, `npm-shrinkwrap.json`,
143
+ `yarn.lock`, `pnpm-lock.yaml`, `bun.lock(b)`, `deno.lock`, `Pipfile.lock`,
144
+ `poetry.lock`, `uv.lock`, `pdm.lock`, `composer.lock`, `Gemfile.lock`,
145
+ `flake.lock`, `go.sum`, `mix.lock`, `packages.lock.json`, `gradle.lockfile`,
146
+ `Package.resolved`, `cabal.project.freeze`. `--full` still renders their
147
+ content — it is the escape hatch that promises every fragment of the changed
148
+ files.
149
+ - Native binary CLI parity: `-f` is accepted as the short form of `--format`,
150
+ bare `--diff` means the working tree against `HEAD`, `--scoring` advertises
151
+ its accepted values, and every option carries help text.
152
+ - The standalone binary is covered by its own integration suite
153
+ (`crates/diffctx-native/tests/native_cli.rs`, run in CI): exit codes, both
154
+ version flags,
155
+ the token summary, `--quiet`, format validation and budget handling are now
156
+ contract-tested against real git repositories. Every parity defect above
157
+ shipped because nothing exercised the clap parser.
158
+
159
+ ## [1.12.1] - 2026-07-23
160
+
161
+ ### Added
162
+
12
163
  - Standalone native binaries (linux x86_64/aarch64, macOS arm64, Windows
13
164
  x64) are built and attached to every GitHub release. The README had
14
165
  promised them since 1.9.x; releases only ever carried wheels and an sdist.
@@ -19,10 +170,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
19
170
  - Container image `ghcr.io/nikolay-e/diffctx` (linux amd64/arm64), built from
20
171
  the release tag and smoke-tested against a real repository before the tag
21
172
  moves: `docker run --rm -v "$PWD:/repo" ghcr.io/nikolay-e/diffctx . --diff HEAD~1`.
22
- - Packaging manifests generated from the release checksums: Scoop
23
- (`packaging/scoop/diffctx.json`), AUR (`packaging/aur/`) and an npm wrapper
24
- (`packaging/npm/`) that downloads the platform binary and verifies its
25
- SHA-256 against the published checksum.
173
+ Mirrored to Docker Hub as `nikolajer/diffctx`.
174
+ - npm wrapper (`packaging/npm/`) that downloads the platform binary and
175
+ verifies its SHA-256 against the published checksum. Scoop
176
+ (`packaging/scoop/diffctx.json`) and AUR (`packaging/aur/`) manifests are
177
+ generated from the same checksums but are not published to any bucket or
178
+ to the AUR.
26
179
 
27
180
  ### Fixed
28
181
 
@@ -78,7 +231,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
78
231
  log instead of re-running the builders, so generation cost stays 1x.
79
232
  Verified: gitpod 8000s-hang -> 35.7s,
80
233
  pytorch 1848s-SIGKILL -> 7.4s, mui/material-ui OOM class recovered.
81
- Outputs are bit-identical (gated by `benchmarks/equivalence_gate.py`).
234
+ Outputs are bit-identical (gated by `eval/analysis/equivalence_gate.py`).
82
235
 
83
236
  ### Known limitations
84
237
 
@@ -117,7 +270,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
117
270
  - **The default `--format` is now `md` (Markdown), previously `yaml`.** Markdown
118
271
  is ~7% more token-efficient than YAML on real diffs and is preferred by
119
272
  reviewers for code-fragment-heavy output (evidence:
120
- `benchmarks/real_world_diff_bench/`). YAML remains available via `-f yaml`.
273
+ `datasets/real-world-diff/v1/`). YAML remains available via `-f yaml`.
121
274
  This also changes the default stdout format of `treemapper` (a passthrough
122
275
  wrapper over this engine); downstream scripts that parse the default output
123
276
  must now pass `-f yaml` explicitly. (#104)
@@ -252,7 +405,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
252
405
  optional `diffctx-mcp` server confines its filesystem reach via
253
406
  `DIFFCTX_ALLOWED_PATHS`.
254
407
  - Footer of `README.md` links `CHANGELOG.md`, `SECURITY.md`, and
255
- `docs/parameter-strategy.md` so they are no longer orphaned.
408
+ `docs/engineering/parameter-strategy.md` so they are no longer orphaned.
256
409
 
257
410
  ### Changed
258
411
 
@@ -311,5 +464,5 @@ Earlier releases shipped as `treemapper`; see
311
464
  <https://github.com/nikolay-e/diffctx/releases> for the corresponding GitHub
312
465
  release notes (`1.0.0` through `1.6.1`).
313
466
 
314
- [Unreleased]: https://github.com/nikolay-e/diffctx/compare/v1.7.0...HEAD
467
+ [Unreleased]: https://github.com/nikolay-e/diffctx/compare/v1.12.1...HEAD
315
468
  [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]]
@@ -179,9 +179,9 @@ dependencies = [
179
179
 
180
180
  [[package]]
181
181
  name = "crossbeam-epoch"
182
- version = "0.9.18"
182
+ version = "0.9.20"
183
183
  source = "registry+https://github.com/rust-lang/crates.io-index"
184
- checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
184
+ checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f"
185
185
  dependencies = [
186
186
  "crossbeam-utils",
187
187
  ]
@@ -194,7 +194,7 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
194
194
 
195
195
  [[package]]
196
196
  name = "diffctx"
197
- version = "1.12.1"
197
+ version = "1.12.3"
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"
@@ -399,9 +399,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
399
399
 
400
400
  [[package]]
401
401
  name = "libc"
402
- version = "0.2.186"
402
+ version = "0.2.189"
403
403
  source = "registry+https://github.com/rust-lang/crates.io-index"
404
- checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
404
+ checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
405
405
 
406
406
  [[package]]
407
407
  name = "libmimalloc-sys"
@@ -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]]
@@ -1094,9 +1105,9 @@ dependencies = [
1094
1105
 
1095
1106
  [[package]]
1096
1107
  name = "tree-sitter-javascript"
1097
- version = "0.23.1"
1108
+ version = "0.25.0"
1098
1109
  source = "registry+https://github.com/rust-lang/crates.io-index"
1099
- checksum = "bf40bf599e0416c16c125c3cec10ee5ddc7d1bb8b0c60fa5c4de249ad34dc1b1"
1110
+ checksum = "68204f2abc0627a90bdf06e605f5c470aa26fdcb2081ea553a04bdad756693f5"
1100
1111
  dependencies = [
1101
1112
  "cc",
1102
1113
  "tree-sitter-language",
@@ -1200,9 +1211,9 @@ dependencies = [
1200
1211
 
1201
1212
  [[package]]
1202
1213
  name = "tree-sitter-python"
1203
- version = "0.23.6"
1214
+ version = "0.25.0"
1204
1215
  source = "registry+https://github.com/rust-lang/crates.io-index"
1205
- checksum = "3d065aaa27f3aaceaf60c1f0e0ac09e1cb9eb8ed28e7bcdaa52129cffc7f4b04"
1216
+ checksum = "6bf85fd39652e740bf60f46f4cda9492c3a9ad75880575bf14960f775cb74a1c"
1206
1217
  dependencies = [
1207
1218
  "cc",
1208
1219
  "tree-sitter-language",
@@ -1260,9 +1271,9 @@ dependencies = [
1260
1271
 
1261
1272
  [[package]]
1262
1273
  name = "tree-sitter-swift"
1263
- version = "0.6.0"
1274
+ version = "0.7.3"
1264
1275
  source = "registry+https://github.com/rust-lang/crates.io-index"
1265
- checksum = "d65aeb41726119416567d0333ec17580ac4abfb96db1f67c4bd638c65f9992fe"
1276
+ checksum = "fe36052155b9dd69ca82b3b8f1b4ccfb2d867125ac1a4db1dd7331829242668c"
1266
1277
  dependencies = [
1267
1278
  "cc",
1268
1279
  "tree-sitter-language",
@@ -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,20 @@
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"
13
+
14
+ # `cargo test --release` with panic = "abort" compiles the whole dependency
15
+ # graph twice (abort for the cdylib lib artifact, unwind for the test
16
+ # universe); the hashless cdylib output names then collide (cargo#6313).
17
+ # Tests run through this profile instead: same optimizations, one universe.
18
+ [profile.release-unwind]
19
+ inherits = "release"
20
+ panic = "unwind"