patch-chat 0.4.0__tar.gz → 0.5.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 (321) hide show
  1. patch_chat-0.5.0/.github/dependabot.yml +30 -0
  2. {patch_chat-0.4.0 → patch_chat-0.5.0}/AGENTS.md +7 -1
  3. {patch_chat-0.4.0 → patch_chat-0.5.0}/CHANGELOG.md +72 -1
  4. {patch_chat-0.4.0 → patch_chat-0.5.0}/CONTRIBUTING.md +10 -0
  5. {patch_chat-0.4.0/patch_chat.egg-info → patch_chat-0.5.0}/PKG-INFO +20 -6
  6. {patch_chat-0.4.0 → patch_chat-0.5.0}/README.md +15 -1
  7. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/_version.py +3 -3
  8. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/args.py +27 -0
  9. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/base_coder.py +21 -6
  10. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/wholefile_coder.py +57 -3
  11. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/commands.py +1 -0
  12. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/main.py +12 -2
  13. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/repo.py +33 -13
  14. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/repo_trust.py +8 -0
  15. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/scrape.py +466 -332
  16. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/utils.py +23 -1
  17. {patch_chat-0.4.0 → patch_chat-0.5.0/patch_chat.egg-info}/PKG-INFO +20 -6
  18. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch_chat.egg-info/SOURCES.txt +1 -0
  19. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch_chat.egg-info/requires.txt +4 -4
  20. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch_chat.egg-info/scm_file_list.json +1 -0
  21. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch_chat.egg-info/scm_version.json +2 -2
  22. {patch_chat-0.4.0 → patch_chat-0.5.0}/requirements/common-constraints.txt +509 -506
  23. {patch_chat-0.4.0 → patch_chat-0.5.0}/requirements/requirements-browser.txt +169 -169
  24. {patch_chat-0.4.0 → patch_chat-0.5.0}/requirements/requirements-dev.txt +359 -359
  25. patch_chat-0.5.0/requirements/requirements.in +63 -0
  26. {patch_chat-0.4.0 → patch_chat-0.5.0}/requirements.txt +467 -465
  27. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_coder.py +79 -0
  28. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_repo.py +51 -0
  29. patch_chat-0.5.0/tests/basic/test_utils.py +88 -0
  30. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_wholefile.py +79 -0
  31. patch_chat-0.5.0/tests/browser/test_browser.py +69 -0
  32. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/scrape/test_scrape.py +163 -54
  33. patch_chat-0.4.0/requirements/requirements.in +0 -48
  34. patch_chat-0.4.0/tests/basic/test_utils.py +0 -43
  35. patch_chat-0.4.0/tests/browser/test_browser.py +0 -36
  36. {patch_chat-0.4.0 → patch_chat-0.5.0}/.agents/resume +0 -0
  37. {patch_chat-0.4.0 → patch_chat-0.5.0}/.agents/setup +0 -0
  38. {patch_chat-0.4.0 → patch_chat-0.5.0}/.claude/commands/upstream-issue.md +0 -0
  39. {patch_chat-0.4.0 → patch_chat-0.5.0}/.dockerignore +0 -0
  40. {patch_chat-0.4.0 → patch_chat-0.5.0}/.flake8 +0 -0
  41. {patch_chat-0.4.0 → patch_chat-0.5.0}/.github/ISSUE_TEMPLATE/issue.yml +0 -0
  42. {patch_chat-0.4.0 → patch_chat-0.5.0}/.github/workflows/packaging.yml +0 -0
  43. {patch_chat-0.4.0 → patch_chat-0.5.0}/.github/workflows/pre-commit.yml +0 -0
  44. {patch_chat-0.4.0 → patch_chat-0.5.0}/.github/workflows/ubuntu-tests.yml +0 -0
  45. {patch_chat-0.4.0 → patch_chat-0.5.0}/.github/workflows/windows-tests.yml +0 -0
  46. {patch_chat-0.4.0 → patch_chat-0.5.0}/.gitignore +0 -0
  47. {patch_chat-0.4.0 → patch_chat-0.5.0}/.pre-commit-config.yaml +0 -0
  48. {patch_chat-0.4.0 → patch_chat-0.5.0}/AUDIT_PROMPT.md +0 -0
  49. {patch_chat-0.4.0 → patch_chat-0.5.0}/AUDIT_REPORT.md +0 -0
  50. {patch_chat-0.4.0 → patch_chat-0.5.0}/LICENSE.txt +0 -0
  51. {patch_chat-0.4.0 → patch_chat-0.5.0}/MANIFEST.in +0 -0
  52. {patch_chat-0.4.0 → patch_chat-0.5.0}/benchmark/Dockerfile +0 -0
  53. {patch_chat-0.4.0 → patch_chat-0.5.0}/benchmark/README.md +0 -0
  54. {patch_chat-0.4.0 → patch_chat-0.5.0}/benchmark/__init__.py +0 -0
  55. {patch_chat-0.4.0 → patch_chat-0.5.0}/benchmark/benchmark.py +0 -0
  56. {patch_chat-0.4.0 → patch_chat-0.5.0}/benchmark/clone-exercism.sh +0 -0
  57. {patch_chat-0.4.0 → patch_chat-0.5.0}/benchmark/cpp-test.sh +0 -0
  58. {patch_chat-0.4.0 → patch_chat-0.5.0}/benchmark/docker.sh +0 -0
  59. {patch_chat-0.4.0 → patch_chat-0.5.0}/benchmark/docker_build.sh +0 -0
  60. {patch_chat-0.4.0 → patch_chat-0.5.0}/benchmark/install-docker-ubuntu.sh +0 -0
  61. {patch_chat-0.4.0 → patch_chat-0.5.0}/benchmark/npm-test.sh +0 -0
  62. {patch_chat-0.4.0 → patch_chat-0.5.0}/benchmark/plot.sh +0 -0
  63. {patch_chat-0.4.0 → patch_chat-0.5.0}/benchmark/plots.py +0 -0
  64. {patch_chat-0.4.0 → patch_chat-0.5.0}/benchmark/prompts.py +0 -0
  65. {patch_chat-0.4.0 → patch_chat-0.5.0}/benchmark/refactor_tools.py +0 -0
  66. {patch_chat-0.4.0 → patch_chat-0.5.0}/benchmark/rsync.sh +0 -0
  67. {patch_chat-0.4.0 → patch_chat-0.5.0}/benchmark/rungrid.py +0 -0
  68. {patch_chat-0.4.0 → patch_chat-0.5.0}/benchmark/swe-bench-lite.txt +0 -0
  69. {patch_chat-0.4.0 → patch_chat-0.5.0}/benchmark/swe-bench.txt +0 -0
  70. {patch_chat-0.4.0 → patch_chat-0.5.0}/benchmark/swe_bench.py +0 -0
  71. {patch_chat-0.4.0 → patch_chat-0.5.0}/benchmark/test_benchmark.py +0 -0
  72. {patch_chat-0.4.0 → patch_chat-0.5.0}/docker/Dockerfile +0 -0
  73. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/__init__.py +0 -0
  74. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/__main__.py +0 -0
  75. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/analytics.py +0 -0
  76. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/args_formatter.py +0 -0
  77. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/__init__.py +0 -0
  78. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/architect_coder.py +0 -0
  79. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/architect_prompts.py +0 -0
  80. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/ask_coder.py +0 -0
  81. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/ask_prompts.py +0 -0
  82. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/base_prompts.py +0 -0
  83. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/chat_chunks.py +0 -0
  84. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/chat_state.py +0 -0
  85. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/context_coder.py +0 -0
  86. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/context_prompts.py +0 -0
  87. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/edit_apply.py +0 -0
  88. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/editblock_coder.py +0 -0
  89. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/editblock_fenced_coder.py +0 -0
  90. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/editblock_fenced_prompts.py +0 -0
  91. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/editblock_func_coder.py +0 -0
  92. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/editblock_func_prompts.py +0 -0
  93. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/editblock_prompts.py +0 -0
  94. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/editor_diff_fenced_coder.py +0 -0
  95. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/editor_diff_fenced_prompts.py +0 -0
  96. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/editor_editblock_coder.py +0 -0
  97. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/editor_editblock_prompts.py +0 -0
  98. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/editor_whole_coder.py +0 -0
  99. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/editor_whole_prompts.py +0 -0
  100. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/patch_coder.py +0 -0
  101. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/patch_prompts.py +0 -0
  102. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/search_replace.py +0 -0
  103. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/shell.py +0 -0
  104. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/single_wholefile_func_coder.py +0 -0
  105. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/single_wholefile_func_prompts.py +0 -0
  106. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/udiff_coder.py +0 -0
  107. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/udiff_prompts.py +0 -0
  108. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/udiff_simple.py +0 -0
  109. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/udiff_simple_prompts.py +0 -0
  110. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/usage.py +0 -0
  111. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/wholefile_func_coder.py +0 -0
  112. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/wholefile_func_prompts.py +0 -0
  113. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/coders/wholefile_prompts.py +0 -0
  114. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/copypaste.py +0 -0
  115. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/deprecated.py +0 -0
  116. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/diffs.py +0 -0
  117. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/dump.py +0 -0
  118. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/editor.py +0 -0
  119. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/exceptions.py +0 -0
  120. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/format_settings.py +0 -0
  121. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/gui.py +0 -0
  122. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/history.py +0 -0
  123. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/io.py +0 -0
  124. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/linter.py +0 -0
  125. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/llm.py +0 -0
  126. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/mdstream.py +0 -0
  127. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/models.py +0 -0
  128. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/onboarding.py +0 -0
  129. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/openrouter.py +0 -0
  130. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/prompts.py +0 -0
  131. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/README.md +0 -0
  132. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/arduino-tags.scm +0 -0
  133. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/bash-tags.scm +0 -0
  134. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/c-tags.scm +0 -0
  135. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/chatito-tags.scm +0 -0
  136. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/clojure-tags.scm +0 -0
  137. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/commonlisp-tags.scm +0 -0
  138. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/cpp-tags.scm +0 -0
  139. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/csharp-tags.scm +0 -0
  140. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/d-tags.scm +0 -0
  141. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/dart-tags.scm +0 -0
  142. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/elisp-tags.scm +0 -0
  143. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/elixir-tags.scm +0 -0
  144. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/elm-tags.scm +0 -0
  145. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/gleam-tags.scm +0 -0
  146. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/go-tags.scm +0 -0
  147. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/java-tags.scm +0 -0
  148. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/javascript-tags.scm +0 -0
  149. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/lua-tags.scm +0 -0
  150. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/matlab-tags.scm +0 -0
  151. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/ocaml-tags.scm +0 -0
  152. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +0 -0
  153. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/pony-tags.scm +0 -0
  154. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/properties-tags.scm +0 -0
  155. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/python-tags.scm +0 -0
  156. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/r-tags.scm +0 -0
  157. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/racket-tags.scm +0 -0
  158. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/ruby-tags.scm +0 -0
  159. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/rust-tags.scm +0 -0
  160. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/solidity-tags.scm +0 -0
  161. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/swift-tags.scm +0 -0
  162. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-language-pack/udev-tags.scm +0 -0
  163. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/README.md +0 -0
  164. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/bash-tags.scm +0 -0
  165. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/c-tags.scm +0 -0
  166. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/c_sharp-tags.scm +0 -0
  167. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/cpp-tags.scm +0 -0
  168. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/dart-tags.scm +0 -0
  169. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/elisp-tags.scm +0 -0
  170. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/elixir-tags.scm +0 -0
  171. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/elm-tags.scm +0 -0
  172. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/fortran-tags.scm +0 -0
  173. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/go-tags.scm +0 -0
  174. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/haskell-tags.scm +0 -0
  175. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/hcl-tags.scm +0 -0
  176. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/java-tags.scm +0 -0
  177. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/javascript-tags.scm +0 -0
  178. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/julia-tags.scm +0 -0
  179. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/kotlin-tags.scm +0 -0
  180. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/matlab-tags.scm +0 -0
  181. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/ocaml-tags.scm +0 -0
  182. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/ocaml_interface-tags.scm +0 -0
  183. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/php-tags.scm +0 -0
  184. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/python-tags.scm +0 -0
  185. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/ql-tags.scm +0 -0
  186. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/ruby-tags.scm +0 -0
  187. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/rust-tags.scm +0 -0
  188. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/scala-tags.scm +0 -0
  189. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/typescript-tags.scm +0 -0
  190. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/queries/tree-sitter-languages/zig-tags.scm +0 -0
  191. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/reasoning_tags.py +0 -0
  192. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/repomap.py +0 -0
  193. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/report.py +0 -0
  194. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/resources/__init__.py +0 -0
  195. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/resources/model-metadata.json +0 -0
  196. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/resources/model-settings.yml +0 -0
  197. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/run_cmd.py +0 -0
  198. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/sendchat.py +0 -0
  199. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/special.py +0 -0
  200. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/urls.py +0 -0
  201. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/versioncheck.py +0 -0
  202. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/voice.py +0 -0
  203. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/waiting.py +0 -0
  204. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/watch.py +0 -0
  205. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch/watch_prompts.py +0 -0
  206. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch_chat.egg-info/dependency_links.txt +0 -0
  207. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch_chat.egg-info/entry_points.txt +0 -0
  208. {patch_chat-0.4.0 → patch_chat-0.5.0}/patch_chat.egg-info/top_level.txt +0 -0
  209. {patch_chat-0.4.0 → patch_chat-0.5.0}/pyproject.toml +0 -0
  210. {patch_chat-0.4.0 → patch_chat-0.5.0}/pytest.ini +0 -0
  211. {patch_chat-0.4.0 → patch_chat-0.5.0}/requirements/pydub.in +0 -0
  212. {patch_chat-0.4.0 → patch_chat-0.5.0}/requirements/python-compat.in +0 -0
  213. {patch_chat-0.4.0 → patch_chat-0.5.0}/requirements/requirements-browser.in +0 -0
  214. {patch_chat-0.4.0 → patch_chat-0.5.0}/requirements/requirements-dev.in +0 -0
  215. {patch_chat-0.4.0 → patch_chat-0.5.0}/requirements/requirements-playwright.in +0 -0
  216. {patch_chat-0.4.0 → patch_chat-0.5.0}/requirements/requirements-playwright.txt +0 -0
  217. {patch_chat-0.4.0 → patch_chat-0.5.0}/requirements/tree-sitter.in +0 -0
  218. {patch_chat-0.4.0 → patch_chat-0.5.0}/scripts/__init__.py +0 -0
  219. {patch_chat-0.4.0 → patch_chat-0.5.0}/scripts/blame.py +0 -0
  220. {patch_chat-0.4.0 → patch_chat-0.5.0}/scripts/clean_metadata.py +0 -0
  221. {patch_chat-0.4.0 → patch_chat-0.5.0}/scripts/issues.py +0 -0
  222. {patch_chat-0.4.0 → patch_chat-0.5.0}/scripts/my_models.py +0 -0
  223. {patch_chat-0.4.0 → patch_chat-0.5.0}/scripts/pip-compile.sh +0 -0
  224. {patch_chat-0.4.0 → patch_chat-0.5.0}/scripts/redact-cast.py +0 -0
  225. {patch_chat-0.4.0 → patch_chat-0.5.0}/scripts/tmux_record.sh +0 -0
  226. {patch_chat-0.4.0 → patch_chat-0.5.0}/scripts/tsl_pack_langs.py +0 -0
  227. {patch_chat-0.4.0 → patch_chat-0.5.0}/scripts/yank-old-versions.py +0 -0
  228. {patch_chat-0.4.0 → patch_chat-0.5.0}/setup.cfg +0 -0
  229. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/__init__.py +0 -0
  230. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/__init__.py +0 -0
  231. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_analytics.py +0 -0
  232. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_aws_credentials.py +0 -0
  233. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_commands.py +0 -0
  234. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_copypaste.py +0 -0
  235. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_dependency_security.py +0 -0
  236. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_deprecated.py +0 -0
  237. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_edit_apply.py +0 -0
  238. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_editblock.py +0 -0
  239. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_editor.py +0 -0
  240. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_exceptions.py +0 -0
  241. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_find_or_blocks.py +0 -0
  242. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_format_settings.py +0 -0
  243. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_history.py +0 -0
  244. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_io.py +0 -0
  245. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_linter.py +0 -0
  246. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_llm.py +0 -0
  247. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_main.py +0 -0
  248. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_mdstream.py +0 -0
  249. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_model_info_manager.py +0 -0
  250. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_models.py +0 -0
  251. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_onboarding.py +0 -0
  252. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_openrouter.py +0 -0
  253. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_patch_coder.py +0 -0
  254. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_path_containment.py +0 -0
  255. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_reasoning.py +0 -0
  256. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_repo_config_trust.py +0 -0
  257. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_repomap.py +0 -0
  258. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_report.py +0 -0
  259. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_run_cmd.py +0 -0
  260. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_sanity_check_repo.py +0 -0
  261. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_scripting.py +0 -0
  262. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_sendchat.py +0 -0
  263. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_special.py +0 -0
  264. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_ssl_verification.py +0 -0
  265. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_udiff.py +0 -0
  266. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_urls.py +0 -0
  267. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_versioncheck.py +0 -0
  268. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_voice.py +0 -0
  269. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_waiting.py +0 -0
  270. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/basic/test_watch.py +0 -0
  271. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/conftest.py +0 -0
  272. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/chat-history-search-replace-gold.txt +0 -0
  273. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/chat-history.md +0 -0
  274. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/arduino/test.ino +0 -0
  275. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/bash/test.sh +0 -0
  276. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/c/test.c +0 -0
  277. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/chatito/test.chatito +0 -0
  278. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/clojure/test.clj +0 -0
  279. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/commonlisp/test.lisp +0 -0
  280. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/cpp/test.cpp +0 -0
  281. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/csharp/test.cs +0 -0
  282. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/d/test.d +0 -0
  283. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/dart/test.dart +0 -0
  284. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/elisp/test.el +0 -0
  285. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/elixir/test.ex +0 -0
  286. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/elm/test.elm +0 -0
  287. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/gleam/test.gleam +0 -0
  288. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/go/test.go +0 -0
  289. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/haskell/test.hs +0 -0
  290. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/hcl/test.tf +0 -0
  291. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/java/test.java +0 -0
  292. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/javascript/test.js +0 -0
  293. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/kotlin/test.kt +0 -0
  294. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/lua/test.lua +0 -0
  295. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/matlab/test.m +0 -0
  296. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/ocaml/test.ml +0 -0
  297. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/ocaml_interface/test.mli +0 -0
  298. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/php/test.php +0 -0
  299. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/pony/test.pony +0 -0
  300. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/properties/test.properties +0 -0
  301. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/python/test.py +0 -0
  302. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/ql/test.ql +0 -0
  303. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/r/test.r +0 -0
  304. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/racket/test.rkt +0 -0
  305. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/ruby/test.rb +0 -0
  306. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/rust/test.rs +0 -0
  307. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/scala/test.scala +0 -0
  308. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/solidity/test.sol +0 -0
  309. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/swift/test.swift +0 -0
  310. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/tsx/test.tsx +0 -0
  311. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/typescript/test.ts +0 -0
  312. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/udev/test.rules +0 -0
  313. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/languages/zig/test.zig +0 -0
  314. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/sample-code-base/sample.js +0 -0
  315. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/sample-code-base/sample.py +0 -0
  316. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/sample-code-base-repo-map.txt +0 -0
  317. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/watch.js +0 -0
  318. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/watch.lisp +0 -0
  319. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/watch.py +0 -0
  320. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/fixtures/watch_question.js +0 -0
  321. {patch_chat-0.4.0 → patch_chat-0.5.0}/tests/scrape/test_playwright_disable.py +0 -0
@@ -0,0 +1,30 @@
1
+ version: 2
2
+
3
+ # Nothing was watching the pins, which is how five months of advisories
4
+ # accumulated in requirements.txt unnoticed. See issue #1472.
5
+ #
6
+ # requirements.txt and requirements/*.txt are generated by
7
+ # ./scripts/pip-compile.sh from the matching .in files, so a Dependabot PR that
8
+ # edits a .txt is a signal, not a patch to merge: raise the floor in the .in
9
+ # file, re-run the script, and close the PR. AGENTS.md records the same rule.
10
+ updates:
11
+ - package-ecosystem: pip
12
+ directory: /
13
+ schedule:
14
+ interval: weekly
15
+ open-pull-requests-limit: 10
16
+ labels:
17
+ - security
18
+ groups:
19
+ # One PR per batch rather than ten: these are lockfiles, and they are
20
+ # regenerated as a set anyway.
21
+ python-dependencies:
22
+ patterns:
23
+ - "*"
24
+
25
+ - package-ecosystem: github-actions
26
+ directory: /
27
+ schedule:
28
+ interval: weekly
29
+ labels:
30
+ - security
@@ -85,7 +85,13 @@ Two root-level documents cover the audit history:
85
85
  - `patch/_version.py` — generated by setuptools_scm, gitignored.
86
86
  - `requirements/*.txt` — compiled from the matching `.in` files by
87
87
  `./scripts/pip-compile.sh`. Edit the `.in` file and re-run the script; never
88
- hand-edit the `.txt`.
88
+ hand-edit the `.txt`. `requirements.txt` is also the distribution's install set
89
+ (`pyproject.toml` reads `dependencies` from it), so a pin here is what every
90
+ `pip install patch-chat` resolves to — a stale one ships to users, not just to
91
+ contributors. Security floors go in the `.in` file as `package>=fixed.version`,
92
+ next to a comment saying which advisory forced it. `.github/dependabot.yml`
93
+ watches these weekly; its PRs edit the generated `.txt` and are a signal to act
94
+ on, never a patch to merge.
89
95
 
90
96
  ## CI
91
97
 
@@ -7,6 +7,76 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.5.0] - 2026-09-01
11
+
12
+ ### Added
13
+
14
+ - `--no-detect-file-mentions` stops Patch offering to add files the model names in its reply, for
15
+ sessions where the context should stay exactly what you added yourself. The prompt is otherwise
16
+ unchanged, and `--detect-file-mentions` is still the default.
17
+
18
+ ### Fixed
19
+
20
+ - A reply that both edits files and asks for more files no longer loses the edits. Adding the
21
+ mentioned files returned before the edits were applied, so the edits in that reply were dropped
22
+ while the model was told its files had arrived -- it then carried on believing the edit had
23
+ landed, and its next edit failed to match a file that had never changed. The edits are applied
24
+ and committed first, and the request for more files is reflected back afterwards. An edit error
25
+ and a file request arriving together are now both reported, rather than only the file request.
26
+
27
+ - A whole-file listing of a markdown file no longer stops at the first fenced block inside it.
28
+ Any ```` ``` ```` line ended the listing, so a file containing a code block was truncated there
29
+ and everything after it was written to a second file named after whichever line preceded the
30
+ next fence. A fence carrying a language ("```json") is now read as opening a block within the
31
+ listing, and the listing ends at the fence that closes it. A ```` ```diff ```` block inside a
32
+ listing is likewise content, not a mis-selected edit format. An inner block opened with a bare
33
+ ```` ``` ```` and no language remains ambiguous and still ends the listing.
34
+
35
+ - A debug dump of the chat transcript no longer ends the session when the transcript holds text
36
+ the console cannot encode. Content reaches the transcript from files read with
37
+ `surrogateescape` and from model replies, so a lone surrogate can appear in it; printing one
38
+ raised an uncaught `UnicodeEncodeError` from inside `--verbose` output. Unprintable characters
39
+ are now replaced, and the dump is shown.
40
+
41
+ - `/commit` now respects `--subtree-only`. It committed through `git commit -a`, which stages
42
+ every dirty file in the repository regardless of the subtree the session was scoped to, so
43
+ unrelated work elsewhere in the tree was swept into the commit without appearing anywhere the
44
+ user could see it. The commit is now restricted to dirty files inside the subtree, and does
45
+ nothing at all when the only changes are outside it.
46
+
47
+ ### Security
48
+
49
+ - Web scraping no longer fetches private, loopback or link-local addresses. A URL that resolves
50
+ into one of those ranges is refused before the request is made, which closes the path from a
51
+ repository README naming `http://169.254.169.254/latest/meta-data/iam/security-credentials/` to
52
+ Patch fetching the cloud metadata service it happens to be running next to and putting IAM
53
+ credentials in the chat. The check runs after DNS resolution, so a hostname pointing at a private
54
+ address is refused too, and it runs again on every redirect hop, so a public URL cannot bounce
55
+ the scraper somewhere private. Under Playwright the same rule applies to what the page itself
56
+ loads, not only to the address in the URL bar. `--allow-private-urls` scrapes a local dev server
57
+ deliberately; a repository config cannot set it, since the repository is also what supplies the
58
+ URLs.
59
+
60
+ - The browser UI now listens on `127.0.0.1` instead of every interface. `--browser` handed
61
+ Streamlit no address, and its default is to accept connections from anywhere, so running the UI
62
+ on a shared network put an unauthenticated page that can edit the repository and spend the user's
63
+ API credits in reach of everyone on it. `--gui-address ADDR` binds somewhere else on purpose and
64
+ warns when that address is not loopback; a repository config cannot set it.
65
+
66
+ - litellm is now constrained to 1.84.0 or later, carrying the fixes for an authentication bypass
67
+ via Host header injection (CVE-2026-49468) along with CVE-2026-42208 and CVE-2026-35030. This is
68
+ the library every provider request passes through, so it is the code holding the user's API keys.
69
+ The pin is held below 1.85 deliberately: resolving the floor open-ended lands on 1.99, which adds
70
+ the AWS SDK to every install and downgrades `importlib-metadata`, and that is a change to make
71
+ and validate on its own terms rather than inside a security patch.
72
+
73
+ - soupsieve is now constrained to 2.8.4 or later.
74
+
75
+ - Dependency pins are now watched weekly by Dependabot, for both Python packages and GitHub
76
+ Actions. Nothing had been watching them, which is how a lockfile that is also the published
77
+ install set accumulated advisories unnoticed. `CONTRIBUTING.md` and `AGENTS.md` describe how to
78
+ act on what it reports, since the files it edits are generated.
79
+
10
80
  ## [0.4.0] - 2026-09-01
11
81
 
12
82
  ### Changed
@@ -1004,7 +1074,8 @@ than continuing the upstream `0.86.x` numbering.
1004
1074
  `.env`, including `OPENAI_API_KEY` and `PATCH_MODEL`, still load normally; and anything passed on
1005
1075
  the command line always wins.
1006
1076
 
1007
- [Unreleased]: https://github.com/PierrunoYT/patch/compare/v0.4.0...main
1077
+ [Unreleased]: https://github.com/PierrunoYT/patch/compare/v0.5.0...main
1078
+ [0.5.0]: https://github.com/PierrunoYT/patch/compare/v0.4.0...v0.5.0
1008
1079
  [0.4.0]: https://github.com/PierrunoYT/patch/compare/v0.3.0...v0.4.0
1009
1080
  [0.3.0]: https://github.com/PierrunoYT/patch/compare/v0.2.0...v0.3.0
1010
1081
  [0.2.0]: https://github.com/PierrunoYT/patch/compare/v0.1.0...v0.2.0
@@ -280,6 +280,16 @@ Any arguments you pass to `pip-compile.sh` flow through to `uv pip compile`. For
280
280
  Note that a plain re-run keeps the existing pins: `uv` treats the current lock as a preference, so
281
281
  moving a dependency needs one of the `--upgrade` flags above.
282
282
 
283
+ **Security updates.** `requirements.txt` is the distribution's install set — `pyproject.toml` reads
284
+ `dependencies` from it — so a vulnerable pin is what everyone who runs `pip install patch-chat`
285
+ receives. Raise the floor in the `.in` file (`package>=fixed.version`) with a comment naming the
286
+ advisory, then re-run the script. Prefer the narrowest range that carries the fix: resolving an
287
+ open-ended floor can jump many minor versions at once and bring new transitive dependencies with
288
+ it, which is a change to review on its own terms rather than inside a security patch.
289
+ `.github/dependabot.yml` watches the pins weekly, and its pull requests edit the generated `.txt`
290
+ files — treat them as notification that a floor needs raising, and close them in favour of a
291
+ regenerated lock.
292
+
283
293
  ### Pre-commit Hooks
284
294
 
285
295
  The project uses [pre-commit](https://pre-commit.com/) hooks to automatically format code, lint, and run other checks before committing changes. After cloning the repository, run the following command to set up the pre-commit hooks:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: patch-chat
3
- Version: 0.4.0
3
+ Version: 0.5.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
@@ -19,7 +19,7 @@ Requires-Python: <3.15,>=3.10
19
19
  Description-Content-Type: text/markdown
20
20
  License-File: LICENSE.txt
21
21
  Requires-Dist: aiohappyeyeballs==2.6.1
22
- Requires-Dist: aiohttp==3.13.3
22
+ Requires-Dist: aiohttp==3.14.3
23
23
  Requires-Dist: aiosignal==1.4.0
24
24
  Requires-Dist: annotated-doc==0.0.4
25
25
  Requires-Dist: annotated-types==0.7.0
@@ -53,14 +53,14 @@ Requires-Dist: httpcore==1.0.9
53
53
  Requires-Dist: httpx==0.28.1
54
54
  Requires-Dist: huggingface-hub==1.7.1
55
55
  Requires-Dist: idna==3.11
56
- Requires-Dist: importlib-metadata==9.0.1
56
+ Requires-Dist: importlib-metadata==8.9.0
57
57
  Requires-Dist: importlib-resources==6.5.2
58
58
  Requires-Dist: jinja2==3.1.6
59
59
  Requires-Dist: jiter==0.13.0
60
60
  Requires-Dist: json5==0.13.0
61
61
  Requires-Dist: jsonschema==4.26.0
62
62
  Requires-Dist: jsonschema-specifications==2025.9.1
63
- Requires-Dist: litellm==1.82.3
63
+ Requires-Dist: litellm==1.84.10
64
64
  Requires-Dist: markdown-it-py==4.0.0
65
65
  Requires-Dist: markupsafe==3.0.3
66
66
  Requires-Dist: mdurl==0.1.2
@@ -104,7 +104,7 @@ Requires-Dist: smmap==5.0.3
104
104
  Requires-Dist: sniffio==1.3.1
105
105
  Requires-Dist: sounddevice==0.5.5
106
106
  Requires-Dist: soundfile==0.13.1
107
- Requires-Dist: soupsieve==2.8.3
107
+ Requires-Dist: soupsieve==2.9.2
108
108
  Requires-Dist: tiktoken==0.12.0
109
109
  Requires-Dist: tokenizers==0.22.2
110
110
  Requires-Dist: tqdm==4.67.3
@@ -346,7 +346,8 @@ refused whether it sits inside the repository or outside it.
346
346
 
347
347
  **The repository's own config is not trusted.** A `.patch.conf.yml` or `.env` discovered inside
348
348
  the repository cannot set execution-capable options (`test`, `test-cmd`, `lint-cmd`, `load`,
349
- `notifications-command`, `editor`, `verify-ssl`, `git-commit-verify`) or environment variables
349
+ `notifications-command`, `editor`, `verify-ssl`, `git-commit-verify`, `allow-private-urls`,
350
+ `gui-address`) or environment variables
350
351
  that redirect API endpoints or proxies (`*_API_BASE`, `*_BASE_URL`, `HTTP(S)_PROXY`,
351
352
  `REQUESTS_CA_BUNDLE`, ...). Nor can it name another file for Patch to trust — `env-file`,
352
353
  `model-settings-file` and `model-metadata-file` are refused from repository config for that reason.
@@ -364,6 +365,19 @@ Pass `--trust-repo-config` to opt back in for a repository you trust. You can al
364
365
  `PATCH_TRUST_REPO_CONFIG` in your shell. The setting is ignored when it comes from the repository's
365
366
  own config or `.env`, so a repository cannot grant itself trust.
366
367
 
368
+ **Scraping stays on public addresses.** `/web`, and any URL Patch fetches on your behalf, is
369
+ refused when it resolves to a private, loopback or link-local address. Cloud metadata services
370
+ answer on `169.254.169.254` and hand out IAM credentials to whatever asks, and the URL usually
371
+ comes from the repository — a README is enough to aim it. Names are checked after resolution, so a
372
+ hostname with an A record pointing at a private address is refused too, as is a redirect that
373
+ arrives at one. Pass `--allow-private-urls` to scrape a local dev server deliberately; a repository
374
+ config cannot set it.
375
+
376
+ **The browser UI listens on this machine only.** `--browser` / `--gui` binds `127.0.0.1`. The UI
377
+ has no authentication and can edit your repository and spend your API credits, so it is not
378
+ something to put on a shared network by accident. `--gui-address ADDR` moves it deliberately —
379
+ `--gui-address 0.0.0.0` accepts connections from anywhere and says so at startup.
380
+
367
381
  **Running in a container.** The boundary above distinguishes your files from the repository's by
368
382
  comparing against your home directory, so do not set `HOME` to the directory you mount the project
369
383
  into — the two become indistinguishable and every filter above stops applying. The bundled
@@ -94,7 +94,8 @@ refused whether it sits inside the repository or outside it.
94
94
 
95
95
  **The repository's own config is not trusted.** A `.patch.conf.yml` or `.env` discovered inside
96
96
  the repository cannot set execution-capable options (`test`, `test-cmd`, `lint-cmd`, `load`,
97
- `notifications-command`, `editor`, `verify-ssl`, `git-commit-verify`) or environment variables
97
+ `notifications-command`, `editor`, `verify-ssl`, `git-commit-verify`, `allow-private-urls`,
98
+ `gui-address`) or environment variables
98
99
  that redirect API endpoints or proxies (`*_API_BASE`, `*_BASE_URL`, `HTTP(S)_PROXY`,
99
100
  `REQUESTS_CA_BUNDLE`, ...). Nor can it name another file for Patch to trust — `env-file`,
100
101
  `model-settings-file` and `model-metadata-file` are refused from repository config for that reason.
@@ -112,6 +113,19 @@ Pass `--trust-repo-config` to opt back in for a repository you trust. You can al
112
113
  `PATCH_TRUST_REPO_CONFIG` in your shell. The setting is ignored when it comes from the repository's
113
114
  own config or `.env`, so a repository cannot grant itself trust.
114
115
 
116
+ **Scraping stays on public addresses.** `/web`, and any URL Patch fetches on your behalf, is
117
+ refused when it resolves to a private, loopback or link-local address. Cloud metadata services
118
+ answer on `169.254.169.254` and hand out IAM credentials to whatever asks, and the URL usually
119
+ comes from the repository — a README is enough to aim it. Names are checked after resolution, so a
120
+ hostname with an A record pointing at a private address is refused too, as is a redirect that
121
+ arrives at one. Pass `--allow-private-urls` to scrape a local dev server deliberately; a repository
122
+ config cannot set it.
123
+
124
+ **The browser UI listens on this machine only.** `--browser` / `--gui` binds `127.0.0.1`. The UI
125
+ has no authentication and can edit your repository and spend your API credits, so it is not
126
+ something to put on a shared network by accident. `--gui-address ADDR` moves it deliberately —
127
+ `--gui-address 0.0.0.0` accepts connections from anywhere and says so at startup.
128
+
115
129
  **Running in a container.** The boundary above distinguishes your files from the repository's by
116
130
  comparing against your home directory, so do not set `HOME` to the directory you mount the project
117
131
  into — the two become indistinguishable and every filter above stops applying. The bundled
@@ -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.4.0'
22
- __version_tuple__ = version_tuple = (0, 4, 0)
21
+ __version__ = version = '0.5.0'
22
+ __version_tuple__ = version_tuple = (0, 5, 0)
23
23
 
24
- __commit_id__ = commit_id = 'g890bad605'
24
+ __commit_id__ = commit_id = 'g5ca14cb5c'
@@ -669,6 +669,15 @@ def get_parser(default_config_files, git_root):
669
669
  help="Run Patch in your browser (default: False)",
670
670
  default=False,
671
671
  )
672
+ group.add_argument(
673
+ "--gui-address",
674
+ metavar="ADDR",
675
+ default="127.0.0.1",
676
+ help=(
677
+ "Address the browser UI listens on (default: 127.0.0.1, this machine only)."
678
+ " Use 0.0.0.0 to expose it to your network -- it has no authentication."
679
+ ),
680
+ )
672
681
  group.add_argument(
673
682
  "--copy-paste",
674
683
  action=argparse.BooleanOptionalAction,
@@ -735,6 +744,15 @@ def get_parser(default_config_files, git_root):
735
744
  help="Never prompt for or attempt to install Playwright for web scraping (default: False).",
736
745
  default=False,
737
746
  )
747
+ group.add_argument(
748
+ "--allow-private-urls",
749
+ action="store_true",
750
+ help=(
751
+ "Scrape private, loopback and link-local addresses, such as a local dev server."
752
+ " Refused by default because cloud metadata services answer there (default: False)."
753
+ ),
754
+ default=False,
755
+ )
738
756
  group.add_argument(
739
757
  "--file",
740
758
  action="append",
@@ -865,6 +883,15 @@ def get_parser(default_config_files, git_root):
865
883
  default=True,
866
884
  help="Enable/disable detection and offering to add URLs to chat (default: True)",
867
885
  )
886
+ group.add_argument(
887
+ "--detect-file-mentions",
888
+ action=argparse.BooleanOptionalAction,
889
+ default=True,
890
+ help=(
891
+ "Enable/disable offering to add files the model mentions to the chat"
892
+ " (default: True). Turn it off to keep the context to what you add yourself."
893
+ ),
894
+ )
868
895
  group.add_argument(
869
896
  "--editor",
870
897
  help="Specify which editor to use for the /editor command",
@@ -137,6 +137,7 @@ class Coder:
137
137
  num_cache_warming_pings = 0
138
138
  suggest_shell_commands = True
139
139
  detect_urls = True
140
+ detect_file_mentions = True
140
141
  ignore_mentions = None
141
142
  chat_language = None
142
143
  commit_language = None
@@ -368,6 +369,7 @@ class Coder:
368
369
  chat_language=None,
369
370
  commit_language=None,
370
371
  detect_urls=True,
372
+ detect_file_mentions=True,
371
373
  ignore_mentions=None,
372
374
  total_tokens_sent=0,
373
375
  total_tokens_received=0,
@@ -400,6 +402,7 @@ class Coder:
400
402
 
401
403
  self.suggest_shell_commands = suggest_shell_commands
402
404
  self.detect_urls = detect_urls
405
+ self.detect_file_mentions = detect_file_mentions
403
406
 
404
407
  self.num_cache_warming_pings = num_cache_warming_pings
405
408
 
@@ -1794,15 +1797,17 @@ class Coder:
1794
1797
  else:
1795
1798
  content = ""
1796
1799
 
1800
+ add_rel_files_message = None
1801
+
1797
1802
  if not interrupted:
1798
1803
  add_rel_files_message = self.check_for_file_mentions(content)
1799
- if add_rel_files_message:
1800
- if self.reflected_message:
1801
- self.reflected_message += "\n\n" + add_rel_files_message
1802
- else:
1803
- self.reflected_message = add_rel_files_message
1804
- return
1805
1804
 
1805
+ # A reply that both edits files and asks for more used to return here,
1806
+ # above apply_updates, so its edits were dropped on the floor. The model
1807
+ # was then told the files were added and carried on believing its edit
1808
+ # had landed -- and the next one failed to match, because it never had.
1809
+ # The edits are applied below first; the reflection happens after.
1810
+ if not interrupted and not add_rel_files_message:
1806
1811
  try:
1807
1812
  if self.reply_completed():
1808
1813
  return
@@ -1830,6 +1835,13 @@ class Coder:
1830
1835
 
1831
1836
  self.move_back_cur_messages(saved_message)
1832
1837
 
1838
+ if add_rel_files_message:
1839
+ if self.reflected_message:
1840
+ self.reflected_message += "\n\n" + add_rel_files_message
1841
+ else:
1842
+ self.reflected_message = add_rel_files_message
1843
+ return
1844
+
1833
1845
  if self.reflected_message:
1834
1846
  return
1835
1847
 
@@ -1999,6 +2011,9 @@ class Coder:
1999
2011
  return mentioned_rel_fnames
2000
2012
 
2001
2013
  def check_for_file_mentions(self, content):
2014
+ if not self.detect_file_mentions:
2015
+ return
2016
+
2002
2017
  mentioned_rel_fnames = self.get_file_mentions(content)
2003
2018
 
2004
2019
  new_mentions = mentioned_rel_fnames - self.ignore_mentions
@@ -28,6 +28,33 @@ class WholeFileCoder(Coder):
28
28
  fname = fname.lstrip("#")
29
29
  return fname.strip()
30
30
 
31
+ def nested_fence_delta(self, line):
32
+ """+1 if `line` opens a block nested inside a listing, -1 if it closes one.
33
+
34
+ A markdown file legitimately contains fenced code blocks, and a model
35
+ writes the same three backticks for the inner block as for the listing
36
+ wrapped around it. When the opening and closing markers are identical,
37
+ an info string ("```json") is the only thing distinguishing one from the
38
+ other, so that is what this reads.
39
+
40
+ A language-less inner block ("```" alone opening a code sample) stays
41
+ ambiguous -- nothing in the text separates it from the fence that ends
42
+ the listing -- and is still read as the end of the listing.
43
+ """
44
+ open_fence, close_fence = self.fence
45
+
46
+ if open_fence != close_fence:
47
+ if line.startswith(open_fence):
48
+ return 1
49
+ if line.startswith(close_fence):
50
+ return -1
51
+ return 0
52
+
53
+ if not line.startswith(open_fence):
54
+ return 0
55
+
56
+ return 1 if line[len(open_fence) :].strip() else -1
57
+
31
58
  def fence_fname(self, lines, i, chat_files):
32
59
  """The file the fence at `lines[i]` is listing, or None if that line names none.
33
60
 
@@ -60,12 +87,21 @@ class WholeFileCoder(Coder):
60
87
 
61
88
  diff_fences = ("```diff", "```patch", f"{self.fence[0]}diff", f"{self.fence[0]}patch")
62
89
  in_listing = False
90
+ listing_depth = 0
63
91
  diff_fence = None
64
92
  diff_header = None
65
93
  for i, line in enumerate(lines[:-1]):
66
94
  if in_listing:
67
- if line.startswith(self.fence[1]):
68
- in_listing = False
95
+ # Fenced blocks inside the listing are content, not the end of
96
+ # it -- including a ```diff block in a file that documents one.
97
+ delta = self.nested_fence_delta(line)
98
+ if delta > 0:
99
+ listing_depth += 1
100
+ elif delta < 0:
101
+ if listing_depth:
102
+ listing_depth -= 1
103
+ else:
104
+ in_listing = False
69
105
  continue
70
106
 
71
107
  if line.startswith(diff_fences):
@@ -100,11 +136,29 @@ class WholeFileCoder(Coder):
100
136
  fname = None
101
137
  fname_source = None
102
138
  new_lines = []
139
+ depth = 0
103
140
  for i, line in enumerate(lines):
104
- if line.startswith(self.fence[0]) or line.startswith(self.fence[1]):
141
+ is_fence = line.startswith(self.fence[0]) or line.startswith(self.fence[1])
142
+
143
+ if is_fence and fname is not None:
144
+ # The listing ends at its own closing fence, not at the first
145
+ # fence inside it. Reading an inner block as the end truncated
146
+ # the file there and wrote everything after it to a file named
147
+ # after whatever line happened to precede the next fence.
148
+ if self.nested_fence_delta(line) > 0:
149
+ depth += 1
150
+ new_lines.append(line)
151
+ continue
152
+ if depth:
153
+ depth -= 1
154
+ new_lines.append(line)
155
+ continue
156
+
157
+ if is_fence:
105
158
  if fname is not None:
106
159
  # ending an existing block
107
160
  saw_fname = None
161
+ depth = 0
108
162
 
109
163
  full_path = self.abs_root_path(fname)
110
164
 
@@ -278,6 +278,7 @@ class Commands:
278
278
  playwright_available=res,
279
279
  verify_ssl=self.verify_ssl,
280
280
  confirm_ask=self.io.confirm_ask,
281
+ allow_private_urls=getattr(self.args, "allow_private_urls", False),
281
282
  )
282
283
 
283
284
  content = self.scraper.scrape(url) or ""
@@ -228,7 +228,7 @@ def write_streamlit_credentials():
228
228
  print("Streamlit credentials already exist.")
229
229
 
230
230
 
231
- def launch_gui(args):
231
+ def launch_gui(args, address="127.0.0.1"):
232
232
  from streamlit.web import cli
233
233
 
234
234
  from patch import gui
@@ -243,7 +243,16 @@ def launch_gui(args):
243
243
 
244
244
  st_args = ["run", target]
245
245
 
246
+ # Streamlit binds every interface when it is not told otherwise, which put
247
+ # an unauthenticated UI holding the user's repository and API keys on the
248
+ # local network for anyone who ran --browser on a shared wifi. Loopback is
249
+ # the default; --gui-address is how you opt out of it.
250
+ if address not in ("127.0.0.1", "localhost", "::1"):
251
+ print(f"Warning: the browser UI will accept connections on {address}.")
252
+ print("It has no authentication: anyone who can reach it can edit your repository.")
253
+
246
254
  st_args += [
255
+ f"--server.address={address}",
247
256
  "--browser.gatherUsageStats=false",
248
257
  "--runner.magicEnabled=false",
249
258
  "--server.runOnSave=false",
@@ -980,7 +989,7 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
980
989
  analytics.event("exit", reason="Streamlit not installed")
981
990
  return
982
991
  analytics.event("gui session")
983
- launch_gui(argv)
992
+ launch_gui(argv, args.gui_address)
984
993
  analytics.event("exit", reason="GUI session ended")
985
994
  return
986
995
 
@@ -1187,6 +1196,7 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F
1187
1196
  chat_language=args.chat_language,
1188
1197
  commit_language=args.commit_language,
1189
1198
  detect_urls=args.detect_urls,
1199
+ detect_file_mentions=args.detect_file_mentions,
1190
1200
  auto_copy_context=args.copy_paste,
1191
1201
  auto_accept_architect=args.auto_accept_architect,
1192
1202
  add_gitignore_files=args.add_gitignore_files,
@@ -171,6 +171,15 @@ class GitRepo:
171
171
  - User commit with explicit no-committer: patch_edits=False,
172
172
  --no-attribute-committer -> Author=You, Committer=You
173
173
  """
174
+ if not fnames and self.subtree_only:
175
+ # `git commit -a` commits every dirty file in the repository, which
176
+ # is not what --subtree-only asked for: the session is scoped to one
177
+ # directory, and sweeping in unrelated work elsewhere in the tree is
178
+ # a commit the user cannot see coming. Name the files instead.
179
+ fnames = [f for f in self.get_dirty_files() if self.path_in_subtree(f)]
180
+ if not fnames:
181
+ return
182
+
174
183
  if not fnames and not self.repo.is_dirty():
175
184
  return
176
185
 
@@ -527,20 +536,31 @@ class GitRepo:
527
536
  self.ignore_file_cache[fname] = result
528
537
  return result
529
538
 
530
- def ignored_file_raw(self, fname):
531
- if self.subtree_only:
532
- try:
533
- fname_path = Path(self.normalize_path(fname))
534
- cwd_path = Path.cwd().resolve().relative_to(Path(self.root).resolve())
535
- except ValueError:
536
- # Issue #1524
537
- # ValueError: 'C:\\dev\\squid-certbot' is not in the subpath of
538
- # 'C:\\dev\\squid-certbot'
539
- # Clearly, fname is not under cwd... so ignore it
540
- return True
539
+ def path_in_subtree(self, fname):
540
+ """Is `fname` inside the subtree this session is scoped to?
541
541
 
542
- if cwd_path not in fname_path.parents and fname_path != cwd_path:
543
- return True
542
+ Always True without `--subtree-only`, which is what scopes it. Every
543
+ question about that scope goes through here, so committing and ignoring
544
+ cannot drift apart on what "in the subtree" means.
545
+ """
546
+ if not self.subtree_only:
547
+ return True
548
+
549
+ try:
550
+ fname_path = Path(self.normalize_path(fname))
551
+ cwd_path = Path.cwd().resolve().relative_to(Path(self.root).resolve())
552
+ except ValueError:
553
+ # Issue #1524
554
+ # ValueError: 'C:\\dev\\squid-certbot' is not in the subpath of
555
+ # 'C:\\dev\\squid-certbot'
556
+ # Clearly, fname is not under cwd... so it is not in the subtree
557
+ return False
558
+
559
+ return cwd_path in fname_path.parents or fname_path == cwd_path
560
+
561
+ def ignored_file_raw(self, fname):
562
+ if not self.path_in_subtree(fname):
563
+ return True
544
564
 
545
565
  if not self.patch_ignore_file or not self.patch_ignore_file.is_file():
546
566
  return False
@@ -55,6 +55,13 @@ EXECUTION_KEYS = {
55
55
  "editor",
56
56
  "verify-ssl",
57
57
  "git-commit-verify",
58
+ # The repository is the one supplying the URLs -- a README pointing at
59
+ # 169.254.169.254 plus `allow-private-urls: true` in its own config is the
60
+ # whole attack, assembled from two files it controls.
61
+ "allow-private-urls",
62
+ # Moves an unauthenticated UI holding this repository and the user's keys
63
+ # off loopback and onto the network.
64
+ "gui-address",
58
65
  }
59
66
 
60
67
  # Substitute credentials or redirect where prompts and code are sent. The .env
@@ -151,6 +158,7 @@ REVIEWED_SAFE_KEYS = {
151
158
  "editor-edit-format": "an edit-format name",
152
159
  "encoding": "a codec name",
153
160
  "line-endings": "an enum",
161
+ "detect-file-mentions": "a boolean; whether to offer to add mentioned files, still confirmed",
154
162
  }
155
163
 
156
164