patch-chat 0.3.0__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) hide show
  1. {patch_chat-0.3.0 → patch_chat-0.4.0}/.claude/commands/upstream-issue.md +16 -22
  2. {patch_chat-0.3.0 → patch_chat-0.4.0}/AGENTS.md +11 -4
  3. {patch_chat-0.3.0 → patch_chat-0.4.0}/AUDIT_REPORT.md +17 -0
  4. {patch_chat-0.3.0 → patch_chat-0.4.0}/CHANGELOG.md +96 -1
  5. {patch_chat-0.3.0 → patch_chat-0.4.0}/CONTRIBUTING.md +6 -0
  6. patch_chat-0.4.0/MANIFEST.in +6 -0
  7. {patch_chat-0.3.0/patch_chat.egg-info → patch_chat-0.4.0}/PKG-INFO +9 -9
  8. {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/benchmark.py +8 -0
  9. patch_chat-0.4.0/benchmark/test_benchmark.py +104 -0
  10. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/_version.py +3 -3
  11. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/base_coder.py +4 -0
  12. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/editblock_coder.py +5 -2
  13. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/udiff_coder.py +15 -12
  14. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/usage.py +182 -186
  15. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/wholefile_coder.py +69 -6
  16. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/commands.py +11 -9
  17. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/main.py +10 -3
  18. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/repo.py +9 -4
  19. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/report.py +28 -0
  20. patch_chat-0.4.0/patch/urls.py +44 -0
  21. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/watch.py +6 -1
  22. {patch_chat-0.3.0 → patch_chat-0.4.0/patch_chat.egg-info}/PKG-INFO +9 -9
  23. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch_chat.egg-info/SOURCES.txt +1 -0
  24. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch_chat.egg-info/requires.txt +8 -8
  25. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch_chat.egg-info/scm_file_list.json +1 -0
  26. patch_chat-0.4.0/patch_chat.egg-info/scm_version.json +8 -0
  27. {patch_chat-0.3.0 → patch_chat-0.4.0}/pyproject.toml +133 -133
  28. {patch_chat-0.3.0 → patch_chat-0.4.0}/requirements/common-constraints.txt +506 -504
  29. {patch_chat-0.3.0 → patch_chat-0.4.0}/requirements/requirements-browser.txt +169 -169
  30. {patch_chat-0.3.0 → patch_chat-0.4.0}/requirements/requirements-dev.txt +359 -359
  31. {patch_chat-0.3.0 → patch_chat-0.4.0}/requirements/requirements.in +48 -47
  32. {patch_chat-0.3.0 → patch_chat-0.4.0}/requirements.txt +465 -464
  33. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_coder.py +1777 -1725
  34. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_commands.py +2502 -2447
  35. patch_chat-0.4.0/tests/basic/test_dependency_security.py +35 -0
  36. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_editblock.py +18 -0
  37. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_main.py +36 -1
  38. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_repo.py +779 -737
  39. patch_chat-0.4.0/tests/basic/test_report.py +43 -0
  40. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_sanity_check_repo.py +2 -5
  41. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_udiff.py +260 -241
  42. patch_chat-0.4.0/tests/basic/test_urls.py +40 -0
  43. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_watch.py +21 -0
  44. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_wholefile.py +97 -0
  45. patch_chat-0.3.0/MANIFEST.in +0 -1
  46. patch_chat-0.3.0/benchmark/test_benchmark.py +0 -47
  47. patch_chat-0.3.0/patch/urls.py +0 -17
  48. patch_chat-0.3.0/patch_chat.egg-info/scm_version.json +0 -8
  49. patch_chat-0.3.0/tests/basic/test_report.py +0 -20
  50. patch_chat-0.3.0/tests/basic/test_urls.py +0 -18
  51. {patch_chat-0.3.0 → patch_chat-0.4.0}/.agents/resume +0 -0
  52. {patch_chat-0.3.0 → patch_chat-0.4.0}/.agents/setup +0 -0
  53. {patch_chat-0.3.0 → patch_chat-0.4.0}/.dockerignore +0 -0
  54. {patch_chat-0.3.0 → patch_chat-0.4.0}/.flake8 +0 -0
  55. {patch_chat-0.3.0 → patch_chat-0.4.0}/.github/ISSUE_TEMPLATE/issue.yml +0 -0
  56. {patch_chat-0.3.0 → patch_chat-0.4.0}/.github/workflows/packaging.yml +0 -0
  57. {patch_chat-0.3.0 → patch_chat-0.4.0}/.github/workflows/pre-commit.yml +0 -0
  58. {patch_chat-0.3.0 → patch_chat-0.4.0}/.github/workflows/ubuntu-tests.yml +0 -0
  59. {patch_chat-0.3.0 → patch_chat-0.4.0}/.github/workflows/windows-tests.yml +0 -0
  60. {patch_chat-0.3.0 → patch_chat-0.4.0}/.gitignore +0 -0
  61. {patch_chat-0.3.0 → patch_chat-0.4.0}/.pre-commit-config.yaml +0 -0
  62. {patch_chat-0.3.0 → patch_chat-0.4.0}/AUDIT_PROMPT.md +0 -0
  63. {patch_chat-0.3.0 → patch_chat-0.4.0}/LICENSE.txt +0 -0
  64. {patch_chat-0.3.0 → patch_chat-0.4.0}/README.md +0 -0
  65. {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/Dockerfile +0 -0
  66. {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/README.md +0 -0
  67. {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/__init__.py +0 -0
  68. {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/clone-exercism.sh +0 -0
  69. {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/cpp-test.sh +0 -0
  70. {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/docker.sh +0 -0
  71. {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/docker_build.sh +0 -0
  72. {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/install-docker-ubuntu.sh +0 -0
  73. {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/npm-test.sh +0 -0
  74. {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/plot.sh +0 -0
  75. {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/plots.py +0 -0
  76. {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/prompts.py +0 -0
  77. {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/refactor_tools.py +0 -0
  78. {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/rsync.sh +0 -0
  79. {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/rungrid.py +0 -0
  80. {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/swe-bench-lite.txt +0 -0
  81. {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/swe-bench.txt +0 -0
  82. {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/swe_bench.py +0 -0
  83. {patch_chat-0.3.0 → patch_chat-0.4.0}/docker/Dockerfile +0 -0
  84. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/__init__.py +0 -0
  85. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/__main__.py +0 -0
  86. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/analytics.py +0 -0
  87. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/args.py +0 -0
  88. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/args_formatter.py +0 -0
  89. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/__init__.py +0 -0
  90. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/architect_coder.py +0 -0
  91. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/architect_prompts.py +0 -0
  92. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/ask_coder.py +0 -0
  93. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/ask_prompts.py +0 -0
  94. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/base_prompts.py +0 -0
  95. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/chat_chunks.py +0 -0
  96. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/chat_state.py +0 -0
  97. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/context_coder.py +0 -0
  98. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/context_prompts.py +0 -0
  99. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/edit_apply.py +0 -0
  100. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/editblock_fenced_coder.py +0 -0
  101. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/editblock_fenced_prompts.py +0 -0
  102. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/editblock_func_coder.py +0 -0
  103. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/editblock_func_prompts.py +0 -0
  104. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/editblock_prompts.py +0 -0
  105. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/editor_diff_fenced_coder.py +0 -0
  106. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/editor_diff_fenced_prompts.py +0 -0
  107. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/editor_editblock_coder.py +0 -0
  108. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/editor_editblock_prompts.py +0 -0
  109. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/editor_whole_coder.py +0 -0
  110. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/editor_whole_prompts.py +0 -0
  111. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/patch_coder.py +0 -0
  112. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/patch_prompts.py +0 -0
  113. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/search_replace.py +0 -0
  114. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/shell.py +0 -0
  115. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/single_wholefile_func_coder.py +0 -0
  116. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/single_wholefile_func_prompts.py +0 -0
  117. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/udiff_prompts.py +0 -0
  118. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/udiff_simple.py +0 -0
  119. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/udiff_simple_prompts.py +0 -0
  120. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/wholefile_func_coder.py +0 -0
  121. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/wholefile_func_prompts.py +0 -0
  122. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/wholefile_prompts.py +0 -0
  123. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/copypaste.py +0 -0
  124. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/deprecated.py +0 -0
  125. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/diffs.py +0 -0
  126. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/dump.py +0 -0
  127. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/editor.py +0 -0
  128. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/exceptions.py +0 -0
  129. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/format_settings.py +0 -0
  130. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/gui.py +0 -0
  131. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/history.py +0 -0
  132. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/io.py +0 -0
  133. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/linter.py +0 -0
  134. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/llm.py +0 -0
  135. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/mdstream.py +0 -0
  136. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/models.py +0 -0
  137. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/onboarding.py +0 -0
  138. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/openrouter.py +0 -0
  139. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/prompts.py +0 -0
  140. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/README.md +0 -0
  141. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/arduino-tags.scm +0 -0
  142. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/bash-tags.scm +0 -0
  143. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/c-tags.scm +0 -0
  144. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/chatito-tags.scm +0 -0
  145. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/clojure-tags.scm +0 -0
  146. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/commonlisp-tags.scm +0 -0
  147. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/cpp-tags.scm +0 -0
  148. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/csharp-tags.scm +0 -0
  149. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/d-tags.scm +0 -0
  150. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/dart-tags.scm +0 -0
  151. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/elisp-tags.scm +0 -0
  152. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/elixir-tags.scm +0 -0
  153. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/elm-tags.scm +0 -0
  154. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/gleam-tags.scm +0 -0
  155. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/go-tags.scm +0 -0
  156. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/java-tags.scm +0 -0
  157. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/javascript-tags.scm +0 -0
  158. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/lua-tags.scm +0 -0
  159. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/matlab-tags.scm +0 -0
  160. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/ocaml-tags.scm +0 -0
  161. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +0 -0
  162. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/pony-tags.scm +0 -0
  163. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/properties-tags.scm +0 -0
  164. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/python-tags.scm +0 -0
  165. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/r-tags.scm +0 -0
  166. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/racket-tags.scm +0 -0
  167. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/ruby-tags.scm +0 -0
  168. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/rust-tags.scm +0 -0
  169. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/solidity-tags.scm +0 -0
  170. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/swift-tags.scm +0 -0
  171. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/udev-tags.scm +0 -0
  172. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/README.md +0 -0
  173. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/bash-tags.scm +0 -0
  174. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/c-tags.scm +0 -0
  175. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/c_sharp-tags.scm +0 -0
  176. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/cpp-tags.scm +0 -0
  177. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/dart-tags.scm +0 -0
  178. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/elisp-tags.scm +0 -0
  179. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/elixir-tags.scm +0 -0
  180. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/elm-tags.scm +0 -0
  181. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/fortran-tags.scm +0 -0
  182. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/go-tags.scm +0 -0
  183. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/haskell-tags.scm +0 -0
  184. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/hcl-tags.scm +0 -0
  185. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/java-tags.scm +0 -0
  186. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/javascript-tags.scm +0 -0
  187. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/julia-tags.scm +0 -0
  188. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/kotlin-tags.scm +0 -0
  189. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/matlab-tags.scm +0 -0
  190. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/ocaml-tags.scm +0 -0
  191. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/ocaml_interface-tags.scm +0 -0
  192. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/php-tags.scm +0 -0
  193. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/python-tags.scm +0 -0
  194. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/ql-tags.scm +0 -0
  195. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/ruby-tags.scm +0 -0
  196. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/rust-tags.scm +0 -0
  197. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/scala-tags.scm +0 -0
  198. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/typescript-tags.scm +0 -0
  199. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/zig-tags.scm +0 -0
  200. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/reasoning_tags.py +0 -0
  201. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/repo_trust.py +0 -0
  202. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/repomap.py +0 -0
  203. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/resources/__init__.py +0 -0
  204. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/resources/model-metadata.json +0 -0
  205. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/resources/model-settings.yml +0 -0
  206. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/run_cmd.py +0 -0
  207. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/scrape.py +0 -0
  208. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/sendchat.py +0 -0
  209. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/special.py +0 -0
  210. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/utils.py +0 -0
  211. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/versioncheck.py +0 -0
  212. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/voice.py +0 -0
  213. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/waiting.py +0 -0
  214. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/watch_prompts.py +0 -0
  215. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch_chat.egg-info/dependency_links.txt +0 -0
  216. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch_chat.egg-info/entry_points.txt +0 -0
  217. {patch_chat-0.3.0 → patch_chat-0.4.0}/patch_chat.egg-info/top_level.txt +0 -0
  218. {patch_chat-0.3.0 → patch_chat-0.4.0}/pytest.ini +0 -0
  219. {patch_chat-0.3.0 → patch_chat-0.4.0}/requirements/pydub.in +0 -0
  220. {patch_chat-0.3.0 → patch_chat-0.4.0}/requirements/python-compat.in +0 -0
  221. {patch_chat-0.3.0 → patch_chat-0.4.0}/requirements/requirements-browser.in +0 -0
  222. {patch_chat-0.3.0 → patch_chat-0.4.0}/requirements/requirements-dev.in +0 -0
  223. {patch_chat-0.3.0 → patch_chat-0.4.0}/requirements/requirements-playwright.in +0 -0
  224. {patch_chat-0.3.0 → patch_chat-0.4.0}/requirements/requirements-playwright.txt +0 -0
  225. {patch_chat-0.3.0 → patch_chat-0.4.0}/requirements/tree-sitter.in +0 -0
  226. {patch_chat-0.3.0 → patch_chat-0.4.0}/scripts/__init__.py +0 -0
  227. {patch_chat-0.3.0 → patch_chat-0.4.0}/scripts/blame.py +0 -0
  228. {patch_chat-0.3.0 → patch_chat-0.4.0}/scripts/clean_metadata.py +0 -0
  229. {patch_chat-0.3.0 → patch_chat-0.4.0}/scripts/issues.py +0 -0
  230. {patch_chat-0.3.0 → patch_chat-0.4.0}/scripts/my_models.py +0 -0
  231. {patch_chat-0.3.0 → patch_chat-0.4.0}/scripts/pip-compile.sh +0 -0
  232. {patch_chat-0.3.0 → patch_chat-0.4.0}/scripts/redact-cast.py +0 -0
  233. {patch_chat-0.3.0 → patch_chat-0.4.0}/scripts/tmux_record.sh +0 -0
  234. {patch_chat-0.3.0 → patch_chat-0.4.0}/scripts/tsl_pack_langs.py +0 -0
  235. {patch_chat-0.3.0 → patch_chat-0.4.0}/scripts/yank-old-versions.py +0 -0
  236. {patch_chat-0.3.0 → patch_chat-0.4.0}/setup.cfg +0 -0
  237. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/__init__.py +0 -0
  238. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/__init__.py +0 -0
  239. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_analytics.py +0 -0
  240. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_aws_credentials.py +0 -0
  241. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_copypaste.py +0 -0
  242. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_deprecated.py +0 -0
  243. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_edit_apply.py +0 -0
  244. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_editor.py +0 -0
  245. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_exceptions.py +0 -0
  246. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_find_or_blocks.py +0 -0
  247. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_format_settings.py +0 -0
  248. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_history.py +0 -0
  249. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_io.py +0 -0
  250. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_linter.py +0 -0
  251. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_llm.py +0 -0
  252. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_mdstream.py +0 -0
  253. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_model_info_manager.py +0 -0
  254. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_models.py +0 -0
  255. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_onboarding.py +0 -0
  256. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_openrouter.py +0 -0
  257. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_patch_coder.py +0 -0
  258. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_path_containment.py +0 -0
  259. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_reasoning.py +0 -0
  260. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_repo_config_trust.py +0 -0
  261. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_repomap.py +0 -0
  262. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_run_cmd.py +0 -0
  263. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_scripting.py +0 -0
  264. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_sendchat.py +0 -0
  265. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_special.py +0 -0
  266. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_ssl_verification.py +0 -0
  267. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_utils.py +0 -0
  268. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_versioncheck.py +0 -0
  269. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_voice.py +0 -0
  270. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_waiting.py +0 -0
  271. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/browser/test_browser.py +0 -0
  272. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/conftest.py +0 -0
  273. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/chat-history-search-replace-gold.txt +0 -0
  274. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/chat-history.md +0 -0
  275. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/arduino/test.ino +0 -0
  276. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/bash/test.sh +0 -0
  277. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/c/test.c +0 -0
  278. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/chatito/test.chatito +0 -0
  279. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/clojure/test.clj +0 -0
  280. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/commonlisp/test.lisp +0 -0
  281. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/cpp/test.cpp +0 -0
  282. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/csharp/test.cs +0 -0
  283. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/d/test.d +0 -0
  284. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/dart/test.dart +0 -0
  285. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/elisp/test.el +0 -0
  286. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/elixir/test.ex +0 -0
  287. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/elm/test.elm +0 -0
  288. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/gleam/test.gleam +0 -0
  289. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/go/test.go +0 -0
  290. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/haskell/test.hs +0 -0
  291. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/hcl/test.tf +0 -0
  292. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/java/test.java +0 -0
  293. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/javascript/test.js +0 -0
  294. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/kotlin/test.kt +0 -0
  295. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/lua/test.lua +0 -0
  296. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/matlab/test.m +0 -0
  297. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/ocaml/test.ml +0 -0
  298. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/ocaml_interface/test.mli +0 -0
  299. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/php/test.php +0 -0
  300. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/pony/test.pony +0 -0
  301. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/properties/test.properties +0 -0
  302. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/python/test.py +0 -0
  303. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/ql/test.ql +0 -0
  304. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/r/test.r +0 -0
  305. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/racket/test.rkt +0 -0
  306. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/ruby/test.rb +0 -0
  307. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/rust/test.rs +0 -0
  308. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/scala/test.scala +0 -0
  309. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/solidity/test.sol +0 -0
  310. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/swift/test.swift +0 -0
  311. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/tsx/test.tsx +0 -0
  312. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/typescript/test.ts +0 -0
  313. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/udev/test.rules +0 -0
  314. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/zig/test.zig +0 -0
  315. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/sample-code-base/sample.js +0 -0
  316. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/sample-code-base/sample.py +0 -0
  317. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/sample-code-base-repo-map.txt +0 -0
  318. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/watch.js +0 -0
  319. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/watch.lisp +0 -0
  320. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/watch.py +0 -0
  321. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/watch_question.js +0 -0
  322. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/scrape/test_playwright_disable.py +0 -0
  323. {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/scrape/test_scrape.py +0 -0
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Copy an upstream aider issue into this fork, fix it, ship it, and comment upstream
2
+ description: Copy an upstream aider issue into this fork, fix it and ship it
3
3
  argument-hint: <upstream issue URL or number>
4
4
  allowed-tools: Bash(gh:*), Bash(git:*), Bash(python:*), Bash(pytest:*), Bash(pre-commit:*), Read, Edit, Write, Grep, Glob
5
5
  ---
@@ -33,17 +33,12 @@ If nothing here is worth changing, stop after step 3 and say why rather than clo
33
33
  ## 3. Copy it into the fork
34
34
 
35
35
  Create the issue in `PierrunoYT/patch`, preserving the upstream title unless it names something
36
- this fork calls differently, and the body verbatim. Append an attribution footer:
36
+ this fork calls differently, and the body verbatim. Do not add source links, reporter attribution,
37
+ or `@` mentions.
37
38
 
38
- ```
39
- ---
40
-
41
- _Copied from [Aider-AI/aider#<N>](<url>) (opened by @<author> on <YYYY-MM-DD>)._
42
- ```
43
-
44
- When your reading in step 2 differs from the reporter's — a different root cause, or the
45
- conclusion that the real defect is elsewhere — add a short paragraph after the footer saying so.
46
- That paragraph is your analysis, kept separate from their words.
39
+ When your reading in step 2 differs from the report — a different root cause, or the conclusion
40
+ that the real defect is elsewhere — add a short paragraph after the original body saying so. That
41
+ paragraph is your analysis, kept separate from the copied words.
47
42
 
48
43
  Label it from the repo's existing labels (`gh label list --repo PierrunoYT/patch`): always the
49
44
  kind (`bug`, `documentation`, `enhancement`, `security`), always a severity (`critical`, `high`,
@@ -104,19 +99,18 @@ Push to `main`. That closes the fork issue. Confirm it did:
104
99
  gh issue list --repo PierrunoYT/patch --state closed --limit 3
105
100
  ```
106
101
 
107
- ## 7. Comment upstream
102
+ ## 7. Nothing goes upstream
108
103
 
109
- Post it without asking invoking this command is the authorisation. Write the comment to a file
110
- and post it with `gh issue comment <N> --repo Aider-AI/aider --body-file <file>`. It is a public
111
- comment on someone else's repository under the user's account, so write it as carefully as if you
112
- were going to be asked to defend it: say only what step 2 actually established, and nothing the
113
- tests do not cover.
104
+ The pipeline ends at step 6. **This account is blocked from commenting on `Aider-AI/aider`**: on
105
+ 2026-08-31 a maintainer minimised twelve comments from it as `SPAM` and blocked it, so
106
+ `gh issue comment` there returns `GraphQL: User is blocked (addComment)` and reactions are refused
107
+ too. Reading still works, which is why steps 1–6 are unaffected.
114
108
 
115
- A good comment is short and factual: confirm or correct the diagnosis, show the actual fix, say
116
- what the tests cover, and disclose that Patch is a fork of aider. Offer a PR when the fix would
117
- apply upstream unchanged. No promotion.
109
+ Do not post, do not write a comment for later, and do not look for another route to the same
110
+ effect. A block is that project's decision, not an error to work around. Writing a reply that
111
+ cannot be sent is wasted effort — if the block is ever lifted, or the user wants something to
112
+ relay by hand, they will ask.
118
113
 
119
114
  ## Report back
120
115
 
121
- Finish with the fork issue number, the commit SHA, the test and hook results, and the upstream
122
- comment link.
116
+ Finish with the fork issue number, the commit SHA, and the test and hook results.
@@ -132,10 +132,17 @@ code works — it is done when the docs match and the work is committed.
132
132
  Patch is a fork of [aider](https://github.com/Aider-AI/aider), and reports filed there often
133
133
  apply here. `.claude/commands/upstream-issue.md` is a Claude Code slash command
134
134
  (`/upstream-issue <url>`) that carries one from upstream to shipped: copy it into this repo's
135
- tracker with attribution, judge whether the defect is really ours — upstream `aider/foo.py` is
136
- `patch/foo.py` here, and a broken user environment has no fix in this code — write the fix and a
137
- regression test, update the docs above, commit with `Closes #N`, and only then comment upstream.
138
- Editing that file is how the procedure changes; do not keep a second copy of it here.
135
+ tracker without source or reporter attribution, judge whether the defect is really ours — upstream
136
+ `aider/foo.py` is `patch/foo.py` here, and a broken user environment has no fix in this code —
137
+ write the fix and a regression test, update the docs above, and commit with `Closes #N`. Editing
138
+ that file is how the procedure changes; do not keep a second copy of it here.
139
+
140
+ The upstream half of that loop is closed. On 2026-08-31 an aider maintainer minimised twelve
141
+ comments from this account as `SPAM` and blocked it from the repository: `gh issue comment` there
142
+ returns `GraphQL: User is blocked (addComment)`, and reactions are refused too. Reading still
143
+ works, so copying issues over and fixing them is unaffected — the command now ends at the commit,
144
+ sends nothing upstream, and omits source and reporter references from copied issues. Do not try to
145
+ post from elsewhere.
139
146
 
140
147
  ## Releases
141
148
 
@@ -3618,3 +3618,20 @@ test the safety of each policy decision, not only the presence of one.
3618
3618
  The 9.0 post-remediation score reflects all nine findings closed with focused tests and the full
3619
3619
  gate green. It remains below 10 because the fourth audit's structural constraints are unchanged:
3620
3620
  the type gate is intentionally partial and the largest modules still carry broad responsibilities.
3621
+
3622
+ ---
3623
+
3624
+ ## Post-audit upstream issue remediation
3625
+
3626
+ These upstream reports were selected from the 2026-08-31 `aider-issues.json` snapshot after the
3627
+ fourth audit. They are not new audit findings and do not extend the F-1 – F-106 sequence. The
3628
+ snapshot has since been pruned of every report resolved in this fork, so the rows below are no
3629
+ longer found in it; its `removed_resolved` block lists the upstream numbers that were dropped.
3630
+
3631
+ | Upstream report | Severity | Fork issue | Fix | Verification |
3632
+ | --- | --- | --- | --- | --- |
3633
+ | GitPython CVE-2026-42215, CVE-2026-42284, CVE-2026-44243 and CVE-2026-44244 | High | [#137](https://github.com/PierrunoYT/patch/issues/137) | **Resolved in `bf16ec6db`** by requiring GitPython 3.1.49+ | Dependency regression test, full `pytest`, and all pre-commit hooks |
3634
+ | Pillow CVE-2026-42311 | High | [#138](https://github.com/PierrunoYT/patch/issues/138) | **Resolved in `a7ef1ca21`** by requiring Pillow 12.2.0+ | Dependency regression test, full `pytest`, and all pre-commit hooks |
3635
+ | urllib3 CVE-2026-44431 | High | [#139](https://github.com/PierrunoYT/patch/issues/139) | **Resolved in `23358a200`** by requiring urllib3 2.7.0+ | Dependency regression test, full `pytest`, and all pre-commit hooks |
3636
+ | Fatal provider errors return success in one-shot mode | High | [#140](https://github.com/PierrunoYT/patch/issues/140) | **Resolved in `0af8c6508`** by propagating per-message provider failure to the CLI status | API-error retry-exhaustion test, one-shot status test, full `pytest`, and all pre-commit hooks |
3637
+ | Whole-file mode mishandles unified-diff output | Medium | [#141](https://github.com/PierrunoYT/patch/issues/141) | **Resolved in `083edb34f`** by detecting the format mismatch before edit parsing, with fence-span tracking added in `81cba83b0` and headerless diff fences covered in `a682516ef` | Bare, fenced and headerless diff regressions, a listing of a `.patch` file, full `pytest`, and all pre-commit hooks |
@@ -7,6 +7,100 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.4.0] - 2026-09-01
11
+
12
+ ### Changed
13
+
14
+ - The `/upstream-issue` command no longer sends anything to aider or adds source and reporter
15
+ references to copied issues. This account was blocked from commenting on that repository on
16
+ 2026-08-31, after twelve comments in an afternoon were minimised as spam, so the command now
17
+ keeps the workflow entirely inside this fork and ends at the commit. Reading upstream reports,
18
+ copying applicable defects, and fixing them remain unaffected.
19
+
20
+ - `/report` now prefills the questions a report has to answer -- what happened, what was expected
21
+ instead, and how to reproduce it -- above the session details. The command previously sent only
22
+ the announcements, which describe how the session was configured and nothing about the problem,
23
+ so the prefilled form was routinely submitted as an issue that said nothing at all. Text passed
24
+ to the command now answers the first question as well as titling the issue, where before it was
25
+ the title alone. `CONTRIBUTING.md` describes what to fill in.
26
+
27
+ ### Removed
28
+
29
+ - The stale generated dump of upstream aider issues is no longer stored in the repository.
30
+
31
+ ### Fixed
32
+
33
+ - Git repositories now use GitPython's default command-backed object database instead of forcing
34
+ `GitDB`, which could report `BadObject` for objects that Git itself reads successfully and prevent
35
+ Patch from opening a valid repository.
36
+
37
+ - The Polyglot benchmark now gives models the files listed by Exercism under `files.editor` as
38
+ read-only context. Solution files remain editable, while tests and example implementations remain
39
+ hidden, so pass@1 no longer depends on guessing interfaces supplied by the exercise.
40
+
41
+ - Cached input tokens are no longer charged twice in Patch's displayed cost. Cache writes and reads
42
+ use provider metadata when available, only the uncached remainder uses the ordinary input rate,
43
+ and inconsistent provider counts can no longer produce a negative uncached charge.
44
+
45
+ - `/tokens` now measures editable and read-only text files with a real closing code fence instead of
46
+ counting the literal text `{fence}`.
47
+
48
+ - Unified-diff parsing now strips standard Git `a/` and `b/` prefixes from every file in a fenced
49
+ multi-file diff, rather than only from the first file.
50
+
51
+ - Whole-file mode now reports and reflects recognizable unified-diff output instead of silently
52
+ discarding a bare diff or misreading a fenced diff as a new whole-file listing. Recognition
53
+ follows active listing fences, so an earlier completed example block cannot hide a later diff
54
+ and be applied as an edit. A `diff`- or `patch`-tagged fence counts on its own, without the
55
+ `---` and `+++` headers a short hunk often omits: such a block was being read as a listing and
56
+ written to a file named after the sentence above it, so a reply opening "Here is the change:"
57
+ left a file called `Here is the change` in the repository. The line above a fence still names a
58
+ file when it looks like one, which is how a listing of a `.diff` or `.patch` file is written.
59
+ This can happen when an unlisted model ignores the automatically selected edit format; the model
60
+ now gets a precise instruction to retry with the entire updated file.
61
+
62
+ - A fatal model-provider error in `--message` and `--message-file` mode now returns a nonzero exit
63
+ status after retries are exhausted. Headless callers previously received status 0 even though no
64
+ response was produced, making a failed API request indistinguishable from a successful no-op.
65
+
66
+ - A commit-message model that answers with nothing but whitespace no longer stops the fallback.
67
+ The reply was judged before being trimmed, and a string of spaces is not empty as far as an
68
+ `if` is concerned -- so the first model ended the search without producing anything, and the
69
+ next one configured was never asked. The same untrimmed test guarded the "Failed to generate
70
+ commit message!" report, which therefore did not appear either: the commit was made as "(no
71
+ commit message provided)" with nothing said at the time. Each reply is now trimmed as it
72
+ arrives, so the fallback and the failure report both see what the model actually sent.
73
+
74
+ - Search/replace blocks whose fences are indented no longer lose their filename. The search back
75
+ through the preceding lines tested the raw line for a fence while the filename check stripped
76
+ the line first, so the two disagreed about what a fence was and the search stopped one line
77
+ short: a model that indented "```python" produced a valid edit that was rejected for naming no
78
+ file. The docstring's own indented example now parses.
79
+
80
+ - A `.gitignore` containing non-ASCII characters no longer crashes `--watch-files` at startup. The
81
+ file was decoded with the system's locale encoding rather than the UTF-8 Git itself uses, so on
82
+ Windows a single accented path in the ignore file ended the session with an uncaught
83
+ `UnicodeDecodeError` before the first prompt. Bytes that are not valid UTF-8 are now replaced,
84
+ and the remaining patterns still apply.
85
+
86
+ - The "what's new in this version" offer shown on the first run after an upgrade now opens that
87
+ version's release page rather than `CHANGELOG.md` on `main`. The link was always the newest file,
88
+ so it showed entries still sitting under `Unreleased`, and once a later version was cut it buried
89
+ the section it was meant to show under the newer one. `--show-release-notes` follows the same
90
+ link. A development build has no tag behind it and so still opens the changelog.
91
+
92
+ ### Security
93
+
94
+ - urllib3 is now constrained to 2.7.0 or later, preventing low-level proxied cross-origin redirects
95
+ from forwarding sensitive authorization and cookie headers under CVE-2026-44431.
96
+
97
+ - Pillow is now constrained to 12.2.0 or later so a malicious PSD cannot trigger the memory
98
+ corruption and potential arbitrary code execution identified as CVE-2026-42311.
99
+
100
+ - GitPython is now constrained to 3.1.49 or later, closing four High-severity vulnerabilities in
101
+ the previous 3.1.46 lock: configuration injection that could select an attacker-controlled Git
102
+ hooks directory, unsafe clone options, and incomplete hardening of those paths.
103
+
10
104
  ## [0.3.0] - 2026-08-31
11
105
 
12
106
  ### Added
@@ -910,7 +1004,8 @@ than continuing the upstream `0.86.x` numbering.
910
1004
  `.env`, including `OPENAI_API_KEY` and `PATCH_MODEL`, still load normally; and anything passed on
911
1005
  the command line always wins.
912
1006
 
913
- [Unreleased]: https://github.com/PierrunoYT/patch/compare/v0.3.0...main
1007
+ [Unreleased]: https://github.com/PierrunoYT/patch/compare/v0.4.0...main
1008
+ [0.4.0]: https://github.com/PierrunoYT/patch/compare/v0.3.0...v0.4.0
914
1009
  [0.3.0]: https://github.com/PierrunoYT/patch/compare/v0.2.0...v0.3.0
915
1010
  [0.2.0]: https://github.com/PierrunoYT/patch/compare/v0.1.0...v0.2.0
916
1011
  [0.1.0]: https://github.com/PierrunoYT/patch/releases/tag/v0.1.0
@@ -11,6 +11,12 @@ Please submit bug reports and feature requests as GitHub issues. This
11
11
  helps us to keep track of them and discuss potential solutions or
12
12
  enhancements.
13
13
 
14
+ From inside a session, `/report` opens a prefilled issue form in your browser:
15
+ the version and model settings of that session, plus headings for what
16
+ happened, what you expected instead, and how to reproduce it. Please answer
17
+ those three — the settings say how Patch was configured, never what went
18
+ wrong. `/report <summary>` titles the issue and answers the first one.
19
+
14
20
  ## LLM Benchmark Results
15
21
 
16
22
  Contributions of
@@ -0,0 +1,6 @@
1
+ global-exclude .DS_Store
2
+
3
+ # Working data, not part of the distribution. setuptools-scm's git file finder
4
+ # puts every tracked root file into the sdist, so without this the 2.7 MB
5
+ # snapshot of aider's issue tracker would ship inside patch-chat.
6
+ exclude aider-issues.json
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: patch-chat
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: Patch is AI pair programming in your terminal
5
5
  License-Expression: Apache-2.0
6
6
  Project-URL: Homepage, https://github.com/PierrunoYT/patch
@@ -45,7 +45,7 @@ Requires-Dist: filelock==3.25.2
45
45
  Requires-Dist: frozenlist==1.8.0
46
46
  Requires-Dist: fsspec==2026.2.0
47
47
  Requires-Dist: gitdb==4.0.12
48
- Requires-Dist: gitpython==3.1.46
48
+ Requires-Dist: gitpython==3.1.61
49
49
  Requires-Dist: grep-ast==0.9.0
50
50
  Requires-Dist: h11==0.16.0
51
51
  Requires-Dist: hf-xet==1.4.2; platform_machine == "AMD64" or platform_machine == "aarch64" or platform_machine == "amd64" or platform_machine == "arm64" or platform_machine == "x86_64"
@@ -74,7 +74,7 @@ Requires-Dist: oslex==0.1.3
74
74
  Requires-Dist: packaging==26.0
75
75
  Requires-Dist: pathspec==1.0.4
76
76
  Requires-Dist: pexpect==4.9.0
77
- Requires-Dist: pillow==12.1.1
77
+ Requires-Dist: pillow==12.3.0
78
78
  Requires-Dist: posthog==7.9.12
79
79
  Requires-Dist: prompt-toolkit==3.0.52
80
80
  Requires-Dist: propcache==0.4.1
@@ -116,7 +116,7 @@ Requires-Dist: tree-sitter-yaml==0.7.2
116
116
  Requires-Dist: typer==0.24.1
117
117
  Requires-Dist: typing-extensions==4.15.0
118
118
  Requires-Dist: typing-inspection==0.4.2
119
- Requires-Dist: urllib3==2.6.3
119
+ Requires-Dist: urllib3==2.7.0
120
120
  Requires-Dist: watchfiles==1.1.1
121
121
  Requires-Dist: wcwidth==0.6.0
122
122
  Requires-Dist: yarl==1.23.0
@@ -170,7 +170,7 @@ Requires-Dist: numpy==2.4.3; python_full_version >= "3.11" and extra == "dev"
170
170
  Requires-Dist: packaging==26.0; extra == "dev"
171
171
  Requires-Dist: pandas==2.3.3; extra == "dev"
172
172
  Requires-Dist: pathspec==1.0.4; extra == "dev"
173
- Requires-Dist: pillow==12.1.1; extra == "dev"
173
+ Requires-Dist: pillow==12.3.0; extra == "dev"
174
174
  Requires-Dist: platformdirs==4.9.4; extra == "dev"
175
175
  Requires-Dist: pluggy==1.6.0; extra == "dev"
176
176
  Requires-Dist: pre-commit==4.5.1; extra == "dev"
@@ -200,7 +200,7 @@ Requires-Dist: tomli==2.4.1; python_full_version <= "3.11" and extra == "dev"
200
200
  Requires-Dist: typer==0.24.1; extra == "dev"
201
201
  Requires-Dist: typing-extensions==4.15.0; extra == "dev"
202
202
  Requires-Dist: tzdata==2025.3; extra == "dev"
203
- Requires-Dist: urllib3==2.6.3; extra == "dev"
203
+ Requires-Dist: urllib3==2.7.0; extra == "dev"
204
204
  Requires-Dist: uv==0.10.11; extra == "dev"
205
205
  Requires-Dist: virtualenv==21.2.0; extra == "dev"
206
206
  Provides-Extra: browser
@@ -213,7 +213,7 @@ Requires-Dist: charset-normalizer==3.4.6; extra == "browser"
213
213
  Requires-Dist: click==8.3.1; extra == "browser"
214
214
  Requires-Dist: colorama==0.4.6; sys_platform == "win32" and extra == "browser"
215
215
  Requires-Dist: gitdb==4.0.12; extra == "browser"
216
- Requires-Dist: gitpython==3.1.46; extra == "browser"
216
+ Requires-Dist: gitpython==3.1.61; extra == "browser"
217
217
  Requires-Dist: idna==3.11; extra == "browser"
218
218
  Requires-Dist: jinja2==3.1.6; extra == "browser"
219
219
  Requires-Dist: jsonschema==4.26.0; extra == "browser"
@@ -224,7 +224,7 @@ Requires-Dist: numpy==1.26.4; python_full_version < "3.11" and extra == "browser
224
224
  Requires-Dist: numpy==2.4.3; python_full_version >= "3.11" and extra == "browser"
225
225
  Requires-Dist: packaging==26.0; extra == "browser"
226
226
  Requires-Dist: pandas==2.3.3; extra == "browser"
227
- Requires-Dist: pillow==12.1.1; extra == "browser"
227
+ Requires-Dist: pillow==12.3.0; extra == "browser"
228
228
  Requires-Dist: protobuf==6.33.5; extra == "browser"
229
229
  Requires-Dist: pyarrow==23.0.1; extra == "browser"
230
230
  Requires-Dist: pydeck==0.9.1; extra == "browser"
@@ -241,7 +241,7 @@ Requires-Dist: toml==0.10.2; extra == "browser"
241
241
  Requires-Dist: tornado==6.5.5; extra == "browser"
242
242
  Requires-Dist: typing-extensions==4.15.0; extra == "browser"
243
243
  Requires-Dist: tzdata==2025.3; extra == "browser"
244
- Requires-Dist: urllib3==2.6.3; extra == "browser"
244
+ Requires-Dist: urllib3==2.7.0; extra == "browser"
245
245
  Requires-Dist: watchdog==6.0.0; sys_platform != "darwin" and extra == "browser"
246
246
  Provides-Extra: playwright
247
247
  Requires-Dist: greenlet==3.3.2; extra == "playwright"
@@ -727,6 +727,7 @@ def run_test_real(
727
727
  test_files = config.get("files", {}).get("test", [])
728
728
  example_files = config.get("files", {}).get("example", [])
729
729
  solution_files = set(config.get("files", {}).get("solution", []))
730
+ editor_files = set(config.get("files", {}).get("editor", []))
730
731
 
731
732
  # Forcibly ignore certain files not covered by test_files and example_files
732
733
  ignore_files = set(
@@ -747,6 +748,12 @@ def run_test_real(
747
748
  # Remove any ignore files from the solution set that LLM will edit
748
749
  solution_files.difference_update(ignore_files)
749
750
 
751
+ # Editor files provide context, but tests and examples must remain hidden.
752
+ editor_files.difference_update(test_files)
753
+ editor_files.difference_update(example_files)
754
+ editor_files.difference_update(solution_files)
755
+ read_only_fnames = [testdir / file_path for file_path in sorted(editor_files)]
756
+
750
757
  # Copy all solution files
751
758
  for file_path in solution_files:
752
759
  src = testdir / Path(file_path)
@@ -824,6 +831,7 @@ def run_test_real(
824
831
  edit_format,
825
832
  io,
826
833
  fnames=fnames,
834
+ read_only_fnames=read_only_fnames,
827
835
  use_git=False,
828
836
  stream=False,
829
837
  verbose=verbose,
@@ -0,0 +1,104 @@
1
+ # flake8: noqa: E501
2
+
3
+ import json
4
+ import unittest
5
+ from pathlib import Path
6
+ from tempfile import TemporaryDirectory
7
+ from unittest.mock import Mock, patch
8
+
9
+ from benchmark import cleanup_test_output, run_test_real
10
+
11
+
12
+ class TestCleanupTestOutput(unittest.TestCase):
13
+ def test_cleanup_test_output(self):
14
+ # Test case with timing info
15
+ output = "Ran 5 tests in 0.003s\nOK"
16
+ expected = "\nOK"
17
+ self.assertEqual(cleanup_test_output(output), expected)
18
+
19
+ # Test case without timing info
20
+ output = "OK"
21
+ expected = "OK"
22
+ self.assertEqual(cleanup_test_output(output), expected)
23
+
24
+ def test_cleanup_test_output_lines(self):
25
+ # Test case with timing info
26
+ output = """F
27
+ ======================================================================
28
+ FAIL: test_cleanup_test_output (test_benchmark.TestCleanupTestOutput.test_cleanup_test_output)
29
+ ----------------------------------------------------------------------
30
+ Traceback (most recent call last):
31
+ File "/Users/gauthier/Projects/patch/benchmark/test_benchmark.py", line 14, in test_cleanup_test_output
32
+ self.assertEqual(cleanup_test_output(output), expected)
33
+ AssertionError: 'OK' != 'OKx'
34
+ - OK
35
+ + OKx
36
+ ? +
37
+ """
38
+
39
+ expected = """F
40
+ ====
41
+ FAIL: test_cleanup_test_output (test_benchmark.TestCleanupTestOutput.test_cleanup_test_output)
42
+ ----
43
+ Traceback (most recent call last):
44
+ File "/Users/gauthier/Projects/patch/benchmark/test_benchmark.py", line 14, in test_cleanup_test_output
45
+ self.assertEqual(cleanup_test_output(output), expected)
46
+ AssertionError: 'OK' != 'OKx'
47
+ - OK
48
+ + OKx
49
+ ? +
50
+ """
51
+ self.assertEqual(cleanup_test_output(output), expected)
52
+
53
+
54
+ class TestExercismFiles(unittest.TestCase):
55
+ def test_editor_files_are_read_only_and_tests_and_examples_are_hidden(self):
56
+ class CoderCreated(Exception):
57
+ pass
58
+
59
+ with TemporaryDirectory() as temp_dir:
60
+ testdir = Path(temp_dir) / "exercise"
61
+ (testdir / ".meta").mkdir(parents=True)
62
+ (testdir / ".docs").mkdir()
63
+ (testdir / ".docs/instructions.md").write_text("Implement the exercise")
64
+
65
+ files = {
66
+ "solution": ["solution.py", "test.py", "example.py"],
67
+ "editor": ["context.txt", "solution.py", "test.py", "example.py"],
68
+ "test": ["test.py"],
69
+ "example": ["example.py"],
70
+ }
71
+ (testdir / ".meta/config.json").write_text(json.dumps({"files": files}))
72
+ for name in ("solution.py", "context.txt", "test.py", "example.py"):
73
+ (testdir / name).write_text(name)
74
+
75
+ model = Mock(
76
+ max_chat_history_tokens=0,
77
+ extra_params={},
78
+ edit_format="diff",
79
+ )
80
+ with (
81
+ patch("benchmark.models.Model", return_value=model),
82
+ patch("benchmark.Coder.create", side_effect=CoderCreated) as create,
83
+ self.assertRaises(CoderCreated),
84
+ ):
85
+ run_test_real(
86
+ Path(temp_dir),
87
+ testdir,
88
+ "model",
89
+ None,
90
+ 1,
91
+ True,
92
+ True,
93
+ False,
94
+ "hash",
95
+ None,
96
+ None,
97
+ None,
98
+ )
99
+
100
+ kwargs = create.call_args.kwargs
101
+ self.assertEqual(kwargs["fnames"], [testdir / "solution.py"])
102
+ self.assertEqual(kwargs["read_only_fnames"], [testdir / "context.txt"])
103
+ self.assertIn("test.py", kwargs["ignore_mentions"])
104
+ self.assertIn("example.py", kwargs["ignore_mentions"])
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.3.0'
22
- __version_tuple__ = version_tuple = (0, 3, 0)
21
+ __version__ = version = '0.4.0'
22
+ __version_tuple__ = version_tuple = (0, 4, 0)
23
23
 
24
- __commit_id__ = commit_id = 'g4d43d99f3'
24
+ __commit_id__ = commit_id = 'g890bad605'
@@ -1094,6 +1094,7 @@ class Coder:
1094
1094
  self.patch_edited_files = set()
1095
1095
  self.reflected_message = None
1096
1096
  self.num_reflections = 0
1097
+ self.message_send_failed = False
1097
1098
  self.lint_outcome = None
1098
1099
  self.test_outcome = None
1099
1100
  self.shell_commands = []
@@ -1675,6 +1676,7 @@ class Coder:
1675
1676
  ex_info = litellm_ex.get_ex_info(err)
1676
1677
 
1677
1678
  if ex_info.name == "ContextWindowExceededError":
1679
+ self.message_send_failed = True
1678
1680
  exhausted = True
1679
1681
  break
1680
1682
 
@@ -1685,6 +1687,7 @@ class Coder:
1685
1687
  should_retry = False
1686
1688
 
1687
1689
  if not should_retry:
1690
+ self.message_send_failed = True
1688
1691
  self.mdstream = None
1689
1692
  self.check_and_open_urls(err, ex_info.description)
1690
1693
  break
@@ -1733,6 +1736,7 @@ class Coder:
1733
1736
  dict(role="assistant", content=self.multi_response_content, prefix=True)
1734
1737
  )
1735
1738
  except Exception as err:
1739
+ self.message_send_failed = True
1736
1740
  self.mdstream = None
1737
1741
  lines = traceback.format_exception(type(err), err, err.__traceback__)
1738
1742
  self.io.tool_warning("".join(lines))
@@ -533,8 +533,11 @@ def find_filename(lines, fence, valid_fnames):
533
533
  if filename:
534
534
  filenames.append(filename)
535
535
 
536
- # Only continue as long as we keep seeing fences
537
- if not line.startswith(fence[0]) and not line.startswith(triple_backticks):
536
+ # Only continue as long as we keep seeing fences. Match strip_filename,
537
+ # which strips first: an indented fence is still a fence, and testing the
538
+ # raw line here stopped the search one line short of the filename.
539
+ stripped = line.strip()
540
+ if not stripped.startswith(fence[0]) and not stripped.startswith(triple_backticks):
538
541
  break
539
542
 
540
543
  if not filenames:
@@ -357,17 +357,7 @@ def process_fenced_block(lines, start_line_num):
357
357
  block.append("@@ @@")
358
358
 
359
359
  if block[0].startswith("--- ") and block[1].startswith("+++ "):
360
- # Extract the file path, considering that it might contain spaces
361
- a_fname = block[0][4:].strip()
362
- b_fname = block[1][4:].strip()
363
-
364
- # Check if standard git diff prefixes are present (or /dev/null) and strip them
365
- if (a_fname.startswith("a/") or a_fname == "/dev/null") and b_fname.startswith("b/"):
366
- fname = b_fname[2:]
367
- else:
368
- # Otherwise, assume the path is as intended
369
- fname = b_fname
370
-
360
+ fname = filename_from_headers(block[0], block[1])
371
361
  block = block[2:]
372
362
  else:
373
363
  fname = None
@@ -383,6 +373,7 @@ def process_fenced_block(lines, start_line_num):
383
373
  continue
384
374
 
385
375
  if line.startswith("+++ ") and hunk[-2].startswith("--- "):
376
+ next_fname = filename_from_headers(hunk[-2], line)
386
377
  if hunk[-3] == "\n":
387
378
  hunk = hunk[:-3]
388
379
  else:
@@ -392,7 +383,7 @@ def process_fenced_block(lines, start_line_num):
392
383
  hunk = []
393
384
  keeper = False
394
385
 
395
- fname = line[4:].strip()
386
+ fname = next_fname
396
387
  continue
397
388
 
398
389
  op = line[0]
@@ -413,6 +404,18 @@ def process_fenced_block(lines, start_line_num):
413
404
  return line_num + 1, edits
414
405
 
415
406
 
407
+ def filename_from_headers(a_header, b_header):
408
+ """Extract a path from a unified diff header pair."""
409
+ a_fname = a_header[4:].strip()
410
+ b_fname = b_header[4:].strip()
411
+
412
+ if (a_fname.startswith("a/") or a_fname == "/dev/null") and b_fname.startswith("b/"):
413
+ return b_fname[2:]
414
+
415
+ # Otherwise, preserve the existing behavior and treat the new path as intended.
416
+ return b_fname
417
+
418
+
416
419
  def hunk_to_before_after(hunk, lines=False):
417
420
  before = []
418
421
  after = []