diffctx 1.7.1__tar.gz → 1.9.1__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 (160) hide show
  1. {diffctx-1.7.1 → diffctx-1.9.1}/CHANGELOG.md +44 -0
  2. {diffctx-1.7.1 → diffctx-1.9.1}/PKG-INFO +11 -5
  3. {diffctx-1.7.1 → diffctx-1.9.1}/README.md +9 -4
  4. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/Cargo.lock +38 -131
  5. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/Cargo.toml +9 -9
  6. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/config_edges/kubernetes.rs +39 -4
  7. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/filtering.rs +2 -3
  8. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/git.rs +18 -2
  9. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/interval.rs +6 -4
  10. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/languages.rs +0 -92
  11. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/memory_pipeline.rs +16 -1
  12. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/pipeline.rs +63 -2
  13. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/postpass.rs +7 -2
  14. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/pybridge.rs +29 -6
  15. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/render.rs +106 -11
  16. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/signatures.rs +5 -1
  17. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/tokenizer.rs +1 -0
  18. {diffctx-1.7.1 → diffctx-1.9.1}/pyproject.toml +3 -1
  19. {diffctx-1.7.1 → diffctx-1.9.1}/src/diffctx/__init__.py +2 -0
  20. {diffctx-1.7.1 → diffctx-1.9.1}/src/diffctx/cli.py +8 -8
  21. {diffctx-1.7.1 → diffctx-1.9.1}/src/diffctx/main.py +29 -10
  22. {diffctx-1.7.1 → diffctx-1.9.1}/src/diffctx/mcp/__main__.py +2 -2
  23. {diffctx-1.7.1 → diffctx-1.9.1}/src/diffctx/mcp/server.py +7 -0
  24. diffctx-1.9.1/src/diffctx/version.py +1 -0
  25. {diffctx-1.7.1 → diffctx-1.9.1}/src/diffctx/writer.py +38 -0
  26. diffctx-1.7.1/src/diffctx/version.py +0 -1
  27. {diffctx-1.7.1 → diffctx-1.9.1}/LICENSE +0 -0
  28. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/analytics.rs +0 -0
  29. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/candidate_files.rs +0 -0
  30. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/config/analytics.rs +0 -0
  31. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/config/bm25.rs +0 -0
  32. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/config/budget.rs +0 -0
  33. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/config/category_weights.rs +0 -0
  34. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/config/edge_weights.rs +0 -0
  35. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/config/env_overrides.rs +0 -0
  36. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/config/extensions.rs +0 -0
  37. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/config/filtering.rs +0 -0
  38. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/config/fragmentation.rs +0 -0
  39. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/config/git.rs +0 -0
  40. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/config/graph_filtering.rs +0 -0
  41. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/config/importance.rs +0 -0
  42. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/config/limits.rs +0 -0
  43. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/config/mod.rs +0 -0
  44. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/config/mode.rs +0 -0
  45. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/config/needs.rs +0 -0
  46. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/config/parsers.rs +0 -0
  47. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/config/render.rs +0 -0
  48. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/config/scoring.rs +0 -0
  49. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/config/selection.rs +0 -0
  50. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/config/tokenization.rs +0 -0
  51. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/config/weights.rs +0 -0
  52. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/core.rs +0 -0
  53. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/discovery.rs +0 -0
  54. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/base.rs +0 -0
  55. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/config_edges/build_system.rs +0 -0
  56. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/config_edges/cicd.rs +0 -0
  57. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/config_edges/docker.rs +0 -0
  58. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/config_edges/generic.rs +0 -0
  59. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/config_edges/helm.rs +0 -0
  60. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/config_edges/mod.rs +0 -0
  61. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/document/mod.rs +0 -0
  62. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/history/cochange.rs +0 -0
  63. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/history/mod.rs +0 -0
  64. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/mod.rs +0 -0
  65. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/ansible.rs +0 -0
  66. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/bazel.rs +0 -0
  67. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/c_family.rs +0 -0
  68. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/cargo_edges.rs +0 -0
  69. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/clojure.rs +0 -0
  70. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/css.rs +0 -0
  71. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/dart.rs +0 -0
  72. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/dbt.rs +0 -0
  73. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/dotnet.rs +0 -0
  74. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/elixir.rs +0 -0
  75. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/erlang.rs +0 -0
  76. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/go.rs +0 -0
  77. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/graphql.rs +0 -0
  78. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/haskell.rs +0 -0
  79. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/javascript.rs +0 -0
  80. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/julia.rs +0 -0
  81. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/jvm.rs +0 -0
  82. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/latex.rs +0 -0
  83. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/lua.rs +0 -0
  84. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/mod.rs +0 -0
  85. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/nim.rs +0 -0
  86. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/nix.rs +0 -0
  87. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/ocaml.rs +0 -0
  88. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/openapi.rs +0 -0
  89. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/perl.rs +0 -0
  90. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/php.rs +0 -0
  91. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/prisma.rs +0 -0
  92. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/protobuf.rs +0 -0
  93. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/python.rs +0 -0
  94. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/r_lang.rs +0 -0
  95. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/ruby.rs +0 -0
  96. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/rust_lang.rs +0 -0
  97. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/shell.rs +0 -0
  98. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/sql.rs +0 -0
  99. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/swift.rs +0 -0
  100. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/tags.rs +0 -0
  101. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/terraform.rs +0 -0
  102. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/semantic/zig.rs +0 -0
  103. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/similarity/lexical.rs +0 -0
  104. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/similarity/mod.rs +0 -0
  105. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/structural/containment.rs +0 -0
  106. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/structural/mod.rs +0 -0
  107. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/structural/sibling.rs +0 -0
  108. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/edges/structural/testing.rs +0 -0
  109. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/fragmentation.rs +0 -0
  110. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/graph.rs +0 -0
  111. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/graph_export.rs +0 -0
  112. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/lib.rs +0 -0
  113. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/main.rs +0 -0
  114. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/mode.rs +0 -0
  115. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/parsers/config_parser.rs +0 -0
  116. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/parsers/generic.rs +0 -0
  117. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/parsers/markdown.rs +0 -0
  118. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/parsers/mod.rs +0 -0
  119. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/parsers/tree_sitter_strategy.rs +0 -0
  120. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/ppr.rs +0 -0
  121. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/project_graph.rs +0 -0
  122. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/scoring.rs +0 -0
  123. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/select.rs +0 -0
  124. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/stopwords.rs +0 -0
  125. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/test_harness.rs +0 -0
  126. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/types.rs +0 -0
  127. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/utility/boltzmann.rs +0 -0
  128. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/utility/importance.rs +0 -0
  129. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/utility/mod.rs +0 -0
  130. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/utility/needs.rs +0 -0
  131. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/src/utility/scoring.rs +0 -0
  132. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/tests/common/mod.rs +0 -0
  133. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/tests/fixtures/garbage/garbage_api.py +0 -0
  134. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/tests/fixtures/garbage/garbage_constants.py +0 -0
  135. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/tests/fixtures/garbage/garbage_handlers.py +0 -0
  136. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/tests/fixtures/garbage/garbage_models.py +0 -0
  137. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/tests/fixtures/garbage/garbage_module.js +0 -0
  138. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/tests/fixtures/garbage/garbage_services.py +0 -0
  139. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/tests/fixtures/garbage/garbage_types.py +0 -0
  140. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/tests/fixtures/garbage/garbage_unrelated.yaml +0 -0
  141. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/tests/fixtures/garbage/garbage_utils.py +0 -0
  142. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/tests/fixtures/garbage/garbage_validators.py +0 -0
  143. {diffctx-1.7.1 → diffctx-1.9.1}/diffctx/tests/yaml_cases.rs +0 -0
  144. {diffctx-1.7.1 → diffctx-1.9.1}/rust-toolchain.toml +0 -0
  145. {diffctx-1.7.1 → diffctx-1.9.1}/src/diffctx/__main__.py +0 -0
  146. {diffctx-1.7.1 → diffctx-1.9.1}/src/diffctx/clipboard.py +0 -0
  147. {diffctx-1.7.1 → diffctx-1.9.1}/src/diffctx/diffctx/__init__.py +0 -0
  148. {diffctx-1.7.1 → diffctx-1.9.1}/src/diffctx/diffctx/graph_analytics.py +0 -0
  149. {diffctx-1.7.1 → diffctx-1.9.1}/src/diffctx/diffctx/graph_export.py +0 -0
  150. {diffctx-1.7.1 → diffctx-1.9.1}/src/diffctx/diffctx/pipeline.py +0 -0
  151. {diffctx-1.7.1 → diffctx-1.9.1}/src/diffctx/diffctx/project_graph.py +0 -0
  152. {diffctx-1.7.1 → diffctx-1.9.1}/src/diffctx/ignore.py +0 -0
  153. {diffctx-1.7.1 → diffctx-1.9.1}/src/diffctx/logger.py +0 -0
  154. {diffctx-1.7.1 → diffctx-1.9.1}/src/diffctx/mcp/README.md +0 -0
  155. {diffctx-1.7.1 → diffctx-1.9.1}/src/diffctx/mcp/__init__.py +0 -0
  156. {diffctx-1.7.1 → diffctx-1.9.1}/src/diffctx/mcp/formatting.py +0 -0
  157. {diffctx-1.7.1 → diffctx-1.9.1}/src/diffctx/mcp/security.py +0 -0
  158. {diffctx-1.7.1 → diffctx-1.9.1}/src/diffctx/py.typed +0 -0
  159. {diffctx-1.7.1 → diffctx-1.9.1}/src/diffctx/tokens.py +0 -0
  160. {diffctx-1.7.1 → diffctx-1.9.1}/src/diffctx/tree.py +0 -0
@@ -7,6 +7,50 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.9.0] - 2026-06-14
11
+
12
+ ### Added
13
+
14
+ - Diff-context output now leads with an orientation header — `commit_message`
15
+ and `changed_files` — in every format (YAML/JSON/Markdown/text), so a reader
16
+ sees *what* changed before reading any fragment.
17
+ - Each fragment carries a `role` of `changed` when it overlaps the diff hunks
18
+ (omitted for supporting context). Changed code is emitted first; context
19
+ follows, ordered by descending per-file relevance instead of alphabetically.
20
+
21
+ ### Changed
22
+
23
+ - Line-contiguous fragments of the same role within a file are merged into a
24
+ single entry, cutting the per-fragment scaffolding that dominated output made
25
+ up of one-line snippets (lossless on line coverage).
26
+
27
+ ### Fixed
28
+
29
+ - `get_changed_files` and `get_untracked_files` canonicalize paths consistently
30
+ with deleted/discovered files, preventing duplicate fragments when a tracked
31
+ path traverses a symlinked directory.
32
+ - Signature extraction no longer terminates at braces inside parameter defaults
33
+ or annotations (e.g. Python `def f(x={}):`), which previously truncated the
34
+ signature mid-parameter-list.
35
+ - The post-pass that rescues unrepresented changed files reuses the open
36
+ `git cat-file --batch` reader instead of spawning a `git show` per file.
37
+ - Degraded token-count fallback returns 0 for the empty string (was 1).
38
+
39
+ ## [1.8.0]
40
+
41
+ ### Added
42
+
43
+ - Public `diffctx.run(argv=None, *, prog=None, version=None)` engine entry. It
44
+ is the same execution path as the `diffctx` console script but accepts an
45
+ injected program name and version string, so a downstream wrapper (e.g. the
46
+ `treemapper` distribution) can present its own branding in `--help`,
47
+ `--version`, and error prefixes without duplicating the CLI. `main()` now
48
+ delegates to `run()` with the default `diffctx` identity — no behavior change
49
+ for existing callers.
50
+ - `diffctx.mcp.__main__.main(prog=..., extra=...)` accepts an injected program
51
+ name and install-extra string for the missing-dependency hint, so wrappers
52
+ can re-expose the MCP entry point with their own name.
53
+
10
54
  ## [1.7.0] - 2026-05-22
11
55
 
12
56
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: diffctx
3
- Version: 1.7.1
3
+ Version: 1.9.1
4
4
  Classifier: Development Status :: 5 - Production/Stable
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -27,6 +27,7 @@ Requires-Dist: mypy>=1.0,<3.0 ; extra == 'dev'
27
27
  Requires-Dist: pre-commit>=3.0,<5.0 ; extra == 'dev'
28
28
  Requires-Dist: pygit2>=1.12,<2.0 ; extra == 'dev'
29
29
  Requires-Dist: pytest>=7.0,<10.0 ; extra == 'dev'
30
+ Requires-Dist: pytest-asyncio>=0.23,<2.0 ; extra == 'dev'
30
31
  Requires-Dist: pytest-cov>=3.0,<8.0 ; extra == 'dev'
31
32
  Requires-Dist: pytest-timeout>=2.1,<3.0 ; extra == 'dev'
32
33
  Requires-Dist: pytest-xdist>=3.0,<4.0 ; extra == 'dev'
@@ -100,12 +101,17 @@ lines outward and stops as soon as additional context stops paying for itself.
100
101
  ## Install (30 seconds)
101
102
 
102
103
  ```bash
103
- pip install diffctx # canonical
104
- pipx install diffctx # or: isolated, no venv needed
105
- pip install 'diffctx[tree-sitter]' # + AST parsing for smarter diff context
106
- pip install 'diffctx[mcp]' # + MCP server for AI assistants
104
+ pipx install diffctx # recommended — isolated CLI, no venv needed
105
+ pip install diffctx # or: into an active environment
106
+ pipx install 'diffctx[tree-sitter]' # + AST parsing for smarter diff context
107
+ pipx install 'diffctx[mcp]' # + MCP server for AI assistants
107
108
  ```
108
109
 
110
+ > For everyday use, install once with `pipx` and call `diffctx` from any
111
+ > directory. Do **not** `source` the project's `.venv` to run `diffctx` from
112
+ > another repo — that runs a working-tree build and mutates the shell's
113
+ > `PATH`/`PYTHONHOME` for every subsequent command.
114
+
109
115
  ```bash
110
116
  diffctx . --diff HEAD~1 # smart context for last commit → paste into Claude/ChatGPT
111
117
  diffctx . -f md -c # full export → clipboard in Markdown
@@ -24,12 +24,17 @@ lines outward and stops as soon as additional context stops paying for itself.
24
24
  ## Install (30 seconds)
25
25
 
26
26
  ```bash
27
- pip install diffctx # canonical
28
- pipx install diffctx # or: isolated, no venv needed
29
- pip install 'diffctx[tree-sitter]' # + AST parsing for smarter diff context
30
- pip install 'diffctx[mcp]' # + MCP server for AI assistants
27
+ pipx install diffctx # recommended — isolated CLI, no venv needed
28
+ pip install diffctx # or: into an active environment
29
+ pipx install 'diffctx[tree-sitter]' # + AST parsing for smarter diff context
30
+ pipx install 'diffctx[mcp]' # + MCP server for AI assistants
31
31
  ```
32
32
 
33
+ > For everyday use, install once with `pipx` and call `diffctx` from any
34
+ > directory. Do **not** `source` the project's `.venv` to run `diffctx` from
35
+ > another repo — that runs a working-tree build and mutates the shell's
36
+ > `PATH`/`PYTHONHOME` for every subsequent command.
37
+
33
38
  ```bash
34
39
  diffctx . --diff HEAD~1 # smart context for last commit → paste into Claude/ChatGPT
35
40
  diffctx . -f md -c # full export → clipboard in Markdown
@@ -67,32 +67,26 @@ version = "1.0.102"
67
67
  source = "registry+https://github.com/rust-lang/crates.io-index"
68
68
  checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
69
69
 
70
- [[package]]
71
- name = "autocfg"
72
- version = "1.5.0"
73
- source = "registry+https://github.com/rust-lang/crates.io-index"
74
- checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
75
-
76
70
  [[package]]
77
71
  name = "base64"
78
- version = "0.21.7"
72
+ version = "0.22.1"
79
73
  source = "registry+https://github.com/rust-lang/crates.io-index"
80
- checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
74
+ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
81
75
 
82
76
  [[package]]
83
77
  name = "bit-set"
84
- version = "0.5.3"
78
+ version = "0.8.0"
85
79
  source = "registry+https://github.com/rust-lang/crates.io-index"
86
- checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
80
+ checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
87
81
  dependencies = [
88
82
  "bit-vec",
89
83
  ]
90
84
 
91
85
  [[package]]
92
86
  name = "bit-vec"
93
- version = "0.6.3"
87
+ version = "0.8.0"
94
88
  source = "registry+https://github.com/rust-lang/crates.io-index"
95
- checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
89
+ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
96
90
 
97
91
  [[package]]
98
92
  name = "bitflags"
@@ -200,7 +194,7 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
200
194
 
201
195
  [[package]]
202
196
  name = "diffctx"
203
- version = "1.7.1"
197
+ version = "1.8.0"
204
198
  dependencies = [
205
199
  "anyhow",
206
200
  "clap",
@@ -211,7 +205,7 @@ dependencies = [
211
205
  "pyo3",
212
206
  "rayon",
213
207
  "regex",
214
- "rustc-hash 2.1.2",
208
+ "rustc-hash",
215
209
  "serde",
216
210
  "serde_json",
217
211
  "serde_yaml",
@@ -294,9 +288,9 @@ checksum = "5692dd7b5a1978a5aeb0ce83b7655c58ca8efdcb79d21036ea249da95afec2c6"
294
288
 
295
289
  [[package]]
296
290
  name = "fancy-regex"
297
- version = "0.13.0"
291
+ version = "0.17.0"
298
292
  source = "registry+https://github.com/rust-lang/crates.io-index"
299
- checksum = "531e46835a22af56d1e3b66f04844bed63158bc094a628bec1d321d9b4c44bf2"
293
+ checksum = "72cf461f865c862bb7dc573f643dd6a2b6842f7c30b07882b56bd148cc2761b8"
300
294
  dependencies = [
301
295
  "bit-set",
302
296
  "regex-automata",
@@ -385,15 +379,6 @@ dependencies = [
385
379
  "serde_core",
386
380
  ]
387
381
 
388
- [[package]]
389
- name = "indoc"
390
- version = "2.0.7"
391
- source = "registry+https://github.com/rust-lang/crates.io-index"
392
- checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
393
- dependencies = [
394
- "rustversion",
395
- ]
396
-
397
382
  [[package]]
398
383
  name = "is_terminal_polyfill"
399
384
  version = "1.70.2"
@@ -451,15 +436,6 @@ version = "0.12.1"
451
436
  source = "registry+https://github.com/rust-lang/crates.io-index"
452
437
  checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
453
438
 
454
- [[package]]
455
- name = "lock_api"
456
- version = "0.4.14"
457
- source = "registry+https://github.com/rust-lang/crates.io-index"
458
- checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
459
- dependencies = [
460
- "scopeguard",
461
- ]
462
-
463
439
  [[package]]
464
440
  name = "log"
465
441
  version = "0.4.29"
@@ -481,15 +457,6 @@ version = "2.8.0"
481
457
  source = "registry+https://github.com/rust-lang/crates.io-index"
482
458
  checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
483
459
 
484
- [[package]]
485
- name = "memoffset"
486
- version = "0.9.1"
487
- source = "registry+https://github.com/rust-lang/crates.io-index"
488
- checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
489
- dependencies = [
490
- "autocfg",
491
- ]
492
-
493
460
  [[package]]
494
461
  name = "mimalloc"
495
462
  version = "0.1.50"
@@ -530,29 +497,6 @@ version = "1.70.2"
530
497
  source = "registry+https://github.com/rust-lang/crates.io-index"
531
498
  checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
532
499
 
533
- [[package]]
534
- name = "parking_lot"
535
- version = "0.12.5"
536
- source = "registry+https://github.com/rust-lang/crates.io-index"
537
- checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
538
- dependencies = [
539
- "lock_api",
540
- "parking_lot_core",
541
- ]
542
-
543
- [[package]]
544
- name = "parking_lot_core"
545
- version = "0.9.12"
546
- source = "registry+https://github.com/rust-lang/crates.io-index"
547
- checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
548
- dependencies = [
549
- "cfg-if",
550
- "libc",
551
- "redox_syscall",
552
- "smallvec",
553
- "windows-link",
554
- ]
555
-
556
500
  [[package]]
557
501
  name = "pin-project-lite"
558
502
  version = "0.2.17"
@@ -586,37 +530,32 @@ dependencies = [
586
530
 
587
531
  [[package]]
588
532
  name = "pyo3"
589
- version = "0.24.2"
533
+ version = "0.29.0"
590
534
  source = "registry+https://github.com/rust-lang/crates.io-index"
591
- checksum = "e5203598f366b11a02b13aa20cab591229ff0a89fd121a308a5df751d5fc9219"
535
+ checksum = "cd274650b21d4bfc26a0a47587962c1edb425f69287324355cd040c3ea66071c"
592
536
  dependencies = [
593
- "cfg-if",
594
- "indoc",
595
537
  "libc",
596
- "memoffset",
597
538
  "once_cell",
598
539
  "portable-atomic",
599
540
  "pyo3-build-config",
600
541
  "pyo3-ffi",
601
542
  "pyo3-macros",
602
- "unindent",
603
543
  ]
604
544
 
605
545
  [[package]]
606
546
  name = "pyo3-build-config"
607
- version = "0.24.2"
547
+ version = "0.29.0"
608
548
  source = "registry+https://github.com/rust-lang/crates.io-index"
609
- checksum = "99636d423fa2ca130fa5acde3059308006d46f98caac629418e53f7ebb1e9999"
549
+ checksum = "c5e2a7d2f0d013342f295c048ad19237add5154a55b1c5a254c0ec93d4109078"
610
550
  dependencies = [
611
- "once_cell",
612
551
  "target-lexicon",
613
552
  ]
614
553
 
615
554
  [[package]]
616
555
  name = "pyo3-ffi"
617
- version = "0.24.2"
556
+ version = "0.29.0"
618
557
  source = "registry+https://github.com/rust-lang/crates.io-index"
619
- checksum = "78f9cf92ba9c409279bc3305b5409d90db2d2c22392d443a87df3a1adad59e33"
558
+ checksum = "ca85c467da1bbc8d866eea5deff9cf29ea5f7785054a17da36e65bda9c05845b"
620
559
  dependencies = [
621
560
  "libc",
622
561
  "pyo3-build-config",
@@ -624,9 +563,9 @@ dependencies = [
624
563
 
625
564
  [[package]]
626
565
  name = "pyo3-macros"
627
- version = "0.24.2"
566
+ version = "0.29.0"
628
567
  source = "registry+https://github.com/rust-lang/crates.io-index"
629
- checksum = "0b999cb1a6ce21f9a6b147dcf1be9ffedf02e0043aec74dc390f3007047cecd9"
568
+ checksum = "9ac53762fd065daa3194dd09337a38bd793a188100fd1a9304c4ab312d901771"
630
569
  dependencies = [
631
570
  "proc-macro2",
632
571
  "pyo3-macros-backend",
@@ -636,13 +575,12 @@ dependencies = [
636
575
 
637
576
  [[package]]
638
577
  name = "pyo3-macros-backend"
639
- version = "0.24.2"
578
+ version = "0.29.0"
640
579
  source = "registry+https://github.com/rust-lang/crates.io-index"
641
- checksum = "822ece1c7e1012745607d5cf0bcb2874769f0f7cb34c4cde03b9358eb9ef911a"
580
+ checksum = "4ca3a1557399783172dc5bf39cfca835157732532cba56b71d2292161e53b362"
642
581
  dependencies = [
643
582
  "heck",
644
583
  "proc-macro2",
645
- "pyo3-build-config",
646
584
  "quote",
647
585
  "syn",
648
586
  ]
@@ -682,15 +620,6 @@ dependencies = [
682
620
  "crossbeam-utils",
683
621
  ]
684
622
 
685
- [[package]]
686
- name = "redox_syscall"
687
- version = "0.5.18"
688
- source = "registry+https://github.com/rust-lang/crates.io-index"
689
- checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
690
- dependencies = [
691
- "bitflags",
692
- ]
693
-
694
623
  [[package]]
695
624
  name = "regex"
696
625
  version = "1.12.3"
@@ -720,12 +649,6 @@ version = "0.8.10"
720
649
  source = "registry+https://github.com/rust-lang/crates.io-index"
721
650
  checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
722
651
 
723
- [[package]]
724
- name = "rustc-hash"
725
- version = "1.1.0"
726
- source = "registry+https://github.com/rust-lang/crates.io-index"
727
- checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
728
-
729
652
  [[package]]
730
653
  name = "rustc-hash"
731
654
  version = "2.1.2"
@@ -745,12 +668,6 @@ dependencies = [
745
668
  "windows-sys",
746
669
  ]
747
670
 
748
- [[package]]
749
- name = "rustversion"
750
- version = "1.0.22"
751
- source = "registry+https://github.com/rust-lang/crates.io-index"
752
- checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
753
-
754
671
  [[package]]
755
672
  name = "ryu"
756
673
  version = "1.0.23"
@@ -766,12 +683,6 @@ dependencies = [
766
683
  "winapi-util",
767
684
  ]
768
685
 
769
- [[package]]
770
- name = "scopeguard"
771
- version = "1.2.0"
772
- source = "registry+https://github.com/rust-lang/crates.io-index"
773
- checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
774
-
775
686
  [[package]]
776
687
  name = "semver"
777
688
  version = "1.0.28"
@@ -852,9 +763,12 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
852
763
 
853
764
  [[package]]
854
765
  name = "similar"
855
- version = "2.7.0"
766
+ version = "3.1.1"
856
767
  source = "registry+https://github.com/rust-lang/crates.io-index"
857
- checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
768
+ checksum = "e6505efef05804732ed8a3f2d4f279429eb485bd69d5b0cc6b19cc02005cda16"
769
+ dependencies = [
770
+ "bstr",
771
+ ]
858
772
 
859
773
  [[package]]
860
774
  name = "smallvec"
@@ -953,18 +867,17 @@ dependencies = [
953
867
 
954
868
  [[package]]
955
869
  name = "tiktoken-rs"
956
- version = "0.6.0"
870
+ version = "0.12.0"
957
871
  source = "registry+https://github.com/rust-lang/crates.io-index"
958
- checksum = "44075987ee2486402f0808505dd65692163d243a337fc54363d49afac41087f6"
872
+ checksum = "027853bbf8c7763b77c5c595f1c271c7d536ced7d6f83452911b944621e57fc2"
959
873
  dependencies = [
960
874
  "anyhow",
961
875
  "base64",
962
876
  "bstr",
963
877
  "fancy-regex",
964
878
  "lazy_static",
965
- "parking_lot",
966
879
  "regex",
967
- "rustc-hash 1.1.0",
880
+ "rustc-hash",
968
881
  ]
969
882
 
970
883
  [[package]]
@@ -1054,9 +967,9 @@ dependencies = [
1054
967
 
1055
968
  [[package]]
1056
969
  name = "tree-sitter-c"
1057
- version = "0.23.4"
970
+ version = "0.24.2"
1058
971
  source = "registry+https://github.com/rust-lang/crates.io-index"
1059
- checksum = "afd2b1bf1585dc2ef6d69e87d01db8adb059006649dd5f96f31aa789ee6e9c71"
972
+ checksum = "a9b2eb57a55fed6b00812912e730b7a275cf4fe98bfd6a5d76263d4438371728"
1060
973
  dependencies = [
1061
974
  "cc",
1062
975
  "tree-sitter-language",
@@ -1115,9 +1028,9 @@ dependencies = [
1115
1028
 
1116
1029
  [[package]]
1117
1030
  name = "tree-sitter-dart"
1118
- version = "0.1.0"
1031
+ version = "0.2.0"
1119
1032
  source = "registry+https://github.com/rust-lang/crates.io-index"
1120
- checksum = "bba6bf8675e6fe92ba6da371a5497ee5df2a04d2c503e3599c8ad771f6f1faec"
1033
+ checksum = "325dd1e24ee9ee21111e9c43680ae7d6010aaa9f282b048a99b9c7163c1cf553"
1121
1034
  dependencies = [
1122
1035
  "cc",
1123
1036
  "tree-sitter-language",
@@ -1135,9 +1048,9 @@ dependencies = [
1135
1048
 
1136
1049
  [[package]]
1137
1050
  name = "tree-sitter-erlang"
1138
- version = "0.16.0"
1051
+ version = "0.19.0"
1139
1052
  source = "registry+https://github.com/rust-lang/crates.io-index"
1140
- checksum = "ceb442e728225d601db0661f60d64e166bd9b9a55c587f71d4e4f27378717cce"
1053
+ checksum = "a5c42fa011acaeb36d0be0f3ebc87d7a4009cd92a6969cbc63320bcfd989df69"
1141
1054
  dependencies = [
1142
1055
  "cc",
1143
1056
  "tree-sitter-language",
@@ -1291,9 +1204,9 @@ dependencies = [
1291
1204
 
1292
1205
  [[package]]
1293
1206
  name = "tree-sitter-ocaml"
1294
- version = "0.24.2"
1207
+ version = "0.25.0"
1295
1208
  source = "registry+https://github.com/rust-lang/crates.io-index"
1296
- checksum = "7d19db582b3855f56b5f9ec484170fbfb9ee60b938ec7720d76d2ee788e8b640"
1209
+ checksum = "0b943275476bac8f73e08bc4050e21d89d61ff68e1751b789ba74917b9147a85"
1297
1210
  dependencies = [
1298
1211
  "cc",
1299
1212
  "tree-sitter-language",
@@ -1361,9 +1274,9 @@ dependencies = [
1361
1274
 
1362
1275
  [[package]]
1363
1276
  name = "tree-sitter-scala"
1364
- version = "0.23.4"
1277
+ version = "0.26.0"
1365
1278
  source = "registry+https://github.com/rust-lang/crates.io-index"
1366
- checksum = "efde5e68b4736e9eac17bfa296c6f104a26bffab363b365eb898c40a63c15d2f"
1279
+ checksum = "de5a4a7ff23a55474ce6a741d52aaeca7a82fe9421bb982b86e98c6ac8629397"
1367
1280
  dependencies = [
1368
1281
  "cc",
1369
1282
  "tree-sitter-language",
@@ -1431,12 +1344,6 @@ version = "0.2.6"
1431
1344
  source = "registry+https://github.com/rust-lang/crates.io-index"
1432
1345
  checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
1433
1346
 
1434
- [[package]]
1435
- name = "unindent"
1436
- version = "0.2.4"
1437
- source = "registry+https://github.com/rust-lang/crates.io-index"
1438
- checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
1439
-
1440
1347
  [[package]]
1441
1348
  name = "unsafe-libyaml"
1442
1349
  version = "0.2.11"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "diffctx"
3
- version = "1.7.1"
3
+ version = "1.8.0"
4
4
  edition = "2024"
5
5
  rust-version = "1.85"
6
6
 
@@ -73,12 +73,12 @@ tree-sitter-typescript = { version = "0.23", optional = true }
73
73
  tree-sitter-go = { version = "0.23", optional = true }
74
74
  tree-sitter-rust = { version = "0.23", optional = true }
75
75
  tree-sitter-java = { version = "0.23", optional = true }
76
- tree-sitter-c = { version = "0.23", optional = true }
76
+ tree-sitter-c = { version = "0.24", optional = true }
77
77
  tree-sitter-cpp = { version = "0.23", optional = true }
78
78
  tree-sitter-c-sharp = { version = "0.23", optional = true }
79
79
  tree-sitter-ruby = { version = "0.23", optional = true }
80
80
  tree-sitter-php = { version = "0.24", optional = true }
81
- tree-sitter-scala = { version = "0.23", optional = true }
81
+ tree-sitter-scala = { version = "0.26", optional = true }
82
82
  tree-sitter-swift = { version = "0.6", optional = true }
83
83
  tree-sitter-html = { version = "0.23", optional = true }
84
84
  tree-sitter-bash = { version = "0.25", optional = true }
@@ -86,13 +86,13 @@ tree-sitter-css = { version = "0.25", optional = true }
86
86
  tree-sitter-json = { version = "0.24", optional = true }
87
87
  tree-sitter-haskell = { version = "0.23", optional = true }
88
88
  tree-sitter-julia = { version = "0.23", optional = true }
89
- tree-sitter-ocaml = { version = "0.24", optional = true }
89
+ tree-sitter-ocaml = { version = "0.25", optional = true }
90
90
  tree-sitter-yaml = { version = "0.7", optional = true }
91
91
  tree-sitter-r = { version = "1.2", optional = true }
92
92
  tree-sitter-elixir = { version = "0.3", optional = true }
93
93
  tree-sitter-lua = { version = "0.5", optional = true }
94
94
  tree-sitter-zig = { version = "1.1", optional = true }
95
- tree-sitter-erlang = { version = "0.16", optional = true }
95
+ tree-sitter-erlang = { version = "0.19", optional = true }
96
96
  tree-sitter-nix = { version = "0.3", optional = true }
97
97
  tree-sitter-groovy = { version = "0.1", optional = true }
98
98
  tree-sitter-objc = { version = "3.0", optional = true }
@@ -101,10 +101,10 @@ tree-sitter-make = { version = "1.1", optional = true }
101
101
  tree-sitter-hcl = { version = "1.1", optional = true }
102
102
  tree-sitter-clojure = { version = "0.1", optional = true }
103
103
  tree-sitter-graphql = { version = "0.1", optional = true }
104
- tree-sitter-dart = { version = "0.1", optional = true }
104
+ tree-sitter-dart = { version = "0.2", optional = true }
105
105
  tree-sitter-prisma-io = { version = "1.6", optional = true }
106
106
  tree-sitter-svelte-ng = { version = "1.0", optional = true }
107
- tiktoken-rs = "=0.6.0"
107
+ tiktoken-rs = "=0.12.0"
108
108
  rayon = "1.10"
109
109
  serde = { version = "1", features = ["derive", "rc"] }
110
110
  serde_json = "1"
@@ -116,12 +116,12 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
116
116
  regex = "1"
117
117
  once_cell = "1"
118
118
  rustc-hash = "2"
119
- similar = "2"
119
+ similar = "3"
120
120
  glob = "0.3"
121
121
  smallvec = "1"
122
122
  walkdir = "2"
123
123
  wait-timeout = "0.2"
124
- pyo3 = { version = "0.24", features = ["extension-module", "abi3-py310"], optional = true }
124
+ pyo3 = { version = "0.29", features = ["extension-module", "abi3-py310"], optional = true }
125
125
 
126
126
  [dev-dependencies]
127
127
  tempfile = "3"
@@ -41,12 +41,11 @@ static IMAGE_RE: Lazy<Regex> =
41
41
  Lazy::new(|| Regex::new(r##"(?m)^\s{1,20}image:\s?['"]?([^'"#\n]{1,300})"##).unwrap());
42
42
 
43
43
  static SELECTOR_MATCH_LABELS_RE: Lazy<Regex> = Lazy::new(|| {
44
- Regex::new(r"(?m)selector:\s?\n\s{1,20}matchLabels:\s?\n((?:\s{1,20}[a-zA-Z0-9_./-]{1,100}:\s?[^\n:]{1,200}\n){1,50})")
44
+ Regex::new(r"(?m)selector:\s?\n\s{1,20}matchLabels:\s?\n((?:\s{1,20}[a-zA-Z0-9_./-]{1,100}:\s?[^\n:]+\n){1,50})")
45
45
  .unwrap()
46
46
  });
47
47
  static LABELS_RE: Lazy<Regex> = Lazy::new(|| {
48
- Regex::new(r"(?m)labels:\s?\n((?:\s{1,20}[a-zA-Z0-9_./-]{1,100}:\s?[^\n:]{1,200}\n){1,50})")
49
- .unwrap()
48
+ Regex::new(r"(?m)labels:\s?\n((?:\s{1,20}[a-zA-Z0-9_./-]{1,100}:\s?[^\n:]+\n){1,50})").unwrap()
50
49
  });
51
50
  static LABEL_PAIR_RE: Lazy<Regex> = Lazy::new(|| {
52
51
  Regex::new(
@@ -55,7 +54,7 @@ static LABEL_PAIR_RE: Lazy<Regex> = Lazy::new(|| {
55
54
  .unwrap()
56
55
  });
57
56
  static SIMPLE_SELECTOR_RE: Lazy<Regex> = Lazy::new(|| {
58
- Regex::new(r"(?m)selector:\s?\n((?:\s{1,20}[a-zA-Z0-9_./-]{1,100}:\s?[^\n:]{1,200}\n){1,50})")
57
+ Regex::new(r"(?m)selector:\s?\n((?:\s{1,20}[a-zA-Z0-9_./-]{1,100}:\s?[^\n:]+\n){1,50})")
59
58
  .unwrap()
60
59
  });
61
60
 
@@ -515,3 +514,39 @@ impl EdgeBuilder for KubernetesEdgeBuilder {
515
514
  discovered
516
515
  }
517
516
  }
517
+
518
+ #[cfg(test)]
519
+ mod tests {
520
+ use super::*;
521
+
522
+ // Regression for issue #58: these label/selector patterns nest a Unicode
523
+ // negated class under bounded repetition, which compiled past regex's
524
+ // default 10 MiB size limit and made `.unwrap()` abort the whole process
525
+ // the first time a workload manifest forced the Lazy static. Forcing every
526
+ // k8s regex here fails CI on any future reintroduction instead of a user.
527
+ #[test]
528
+ fn all_kubernetes_regexes_compile() {
529
+ for re in [
530
+ &*K8S_API_VERSION_RE,
531
+ &*K8S_KIND_RE,
532
+ &*K8S_METADATA_NAME_RE,
533
+ &*K8S_NAME_RE,
534
+ &*CONFIGMAP_REF_RE,
535
+ &*CONFIGMAP_NAME_RE,
536
+ &*SECRET_REF_RE,
537
+ &*SECRET_NAME_RE,
538
+ &*SERVICE_NAME_RE,
539
+ &*BACKEND_SERVICE_RE,
540
+ &*IMAGE_RE,
541
+ &*SELECTOR_MATCH_LABELS_RE,
542
+ &*LABELS_RE,
543
+ &*LABEL_PAIR_RE,
544
+ &*SIMPLE_SELECTOR_RE,
545
+ &*VOLUME_CONFIGMAP_RE,
546
+ &*VOLUME_SECRET_RE,
547
+ &*VOLUME_PVC_RE,
548
+ ] {
549
+ let _ = re.is_match("x");
550
+ }
551
+ }
552
+ }
@@ -143,9 +143,8 @@ fn find_hub_noise_paths(graph: &Graph, changed_paths: &FxHashSet<Arc<str>>) -> F
143
143
 
144
144
  noise_counts
145
145
  .into_iter()
146
- .filter(|(p, count)| {
147
- *count >= 1
148
- && !direct_edge_paths.contains(p)
146
+ .filter(|(p, _count)| {
147
+ !direct_edge_paths.contains(p)
149
148
  && !changed_dirs.contains(
150
149
  &Path::new(p.as_ref())
151
150
  .parent()
@@ -376,7 +376,15 @@ pub fn get_changed_files(repo_root: &Path, diff_range: Option<&str>) -> Result<V
376
376
  args.push(range);
377
377
  }
378
378
  let parts = run_git_z(repo_root, &args)?;
379
- Ok(parts.iter().map(|p| repo_root.join(p)).collect())
379
+ Ok(parts
380
+ .iter()
381
+ .map(|p| {
382
+ repo_root
383
+ .join(p)
384
+ .canonicalize()
385
+ .unwrap_or_else(|_| repo_root.join(p))
386
+ })
387
+ .collect())
380
388
  }
381
389
 
382
390
  pub fn get_deleted_files(repo_root: &Path, diff_range: Option<&str>) -> Result<FxHashSet<PathBuf>> {
@@ -481,7 +489,15 @@ pub fn get_untracked_files(repo_root: &Path) -> Result<Vec<PathBuf>> {
481
489
  repo_root,
482
490
  &["ls-files", "--others", "--exclude-standard", "-z"],
483
491
  )?;
484
- Ok(parts.iter().map(|p| repo_root.join(p)).collect())
492
+ Ok(parts
493
+ .iter()
494
+ .map(|p| {
495
+ repo_root
496
+ .join(p)
497
+ .canonicalize()
498
+ .unwrap_or_else(|_| repo_root.join(p))
499
+ })
500
+ .collect())
485
501
  }
486
502
 
487
503
  pub struct CatFileBatch {