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.
- {patch_chat-0.3.0 → patch_chat-0.4.0}/.claude/commands/upstream-issue.md +16 -22
- {patch_chat-0.3.0 → patch_chat-0.4.0}/AGENTS.md +11 -4
- {patch_chat-0.3.0 → patch_chat-0.4.0}/AUDIT_REPORT.md +17 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/CHANGELOG.md +96 -1
- {patch_chat-0.3.0 → patch_chat-0.4.0}/CONTRIBUTING.md +6 -0
- patch_chat-0.4.0/MANIFEST.in +6 -0
- {patch_chat-0.3.0/patch_chat.egg-info → patch_chat-0.4.0}/PKG-INFO +9 -9
- {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/benchmark.py +8 -0
- patch_chat-0.4.0/benchmark/test_benchmark.py +104 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/_version.py +3 -3
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/base_coder.py +4 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/editblock_coder.py +5 -2
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/udiff_coder.py +15 -12
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/usage.py +182 -186
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/wholefile_coder.py +69 -6
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/commands.py +11 -9
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/main.py +10 -3
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/repo.py +9 -4
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/report.py +28 -0
- patch_chat-0.4.0/patch/urls.py +44 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/watch.py +6 -1
- {patch_chat-0.3.0 → patch_chat-0.4.0/patch_chat.egg-info}/PKG-INFO +9 -9
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch_chat.egg-info/SOURCES.txt +1 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch_chat.egg-info/requires.txt +8 -8
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch_chat.egg-info/scm_file_list.json +1 -0
- patch_chat-0.4.0/patch_chat.egg-info/scm_version.json +8 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/pyproject.toml +133 -133
- {patch_chat-0.3.0 → patch_chat-0.4.0}/requirements/common-constraints.txt +506 -504
- {patch_chat-0.3.0 → patch_chat-0.4.0}/requirements/requirements-browser.txt +169 -169
- {patch_chat-0.3.0 → patch_chat-0.4.0}/requirements/requirements-dev.txt +359 -359
- {patch_chat-0.3.0 → patch_chat-0.4.0}/requirements/requirements.in +48 -47
- {patch_chat-0.3.0 → patch_chat-0.4.0}/requirements.txt +465 -464
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_coder.py +1777 -1725
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_commands.py +2502 -2447
- patch_chat-0.4.0/tests/basic/test_dependency_security.py +35 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_editblock.py +18 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_main.py +36 -1
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_repo.py +779 -737
- patch_chat-0.4.0/tests/basic/test_report.py +43 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_sanity_check_repo.py +2 -5
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_udiff.py +260 -241
- patch_chat-0.4.0/tests/basic/test_urls.py +40 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_watch.py +21 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_wholefile.py +97 -0
- patch_chat-0.3.0/MANIFEST.in +0 -1
- patch_chat-0.3.0/benchmark/test_benchmark.py +0 -47
- patch_chat-0.3.0/patch/urls.py +0 -17
- patch_chat-0.3.0/patch_chat.egg-info/scm_version.json +0 -8
- patch_chat-0.3.0/tests/basic/test_report.py +0 -20
- patch_chat-0.3.0/tests/basic/test_urls.py +0 -18
- {patch_chat-0.3.0 → patch_chat-0.4.0}/.agents/resume +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/.agents/setup +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/.dockerignore +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/.flake8 +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/.github/ISSUE_TEMPLATE/issue.yml +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/.github/workflows/packaging.yml +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/.github/workflows/pre-commit.yml +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/.github/workflows/ubuntu-tests.yml +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/.github/workflows/windows-tests.yml +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/.gitignore +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/.pre-commit-config.yaml +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/AUDIT_PROMPT.md +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/LICENSE.txt +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/README.md +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/Dockerfile +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/README.md +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/__init__.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/clone-exercism.sh +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/cpp-test.sh +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/docker.sh +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/docker_build.sh +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/install-docker-ubuntu.sh +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/npm-test.sh +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/plot.sh +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/plots.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/prompts.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/refactor_tools.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/rsync.sh +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/rungrid.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/swe-bench-lite.txt +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/swe-bench.txt +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/benchmark/swe_bench.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/docker/Dockerfile +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/__init__.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/__main__.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/analytics.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/args.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/args_formatter.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/__init__.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/architect_coder.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/architect_prompts.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/ask_coder.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/ask_prompts.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/base_prompts.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/chat_chunks.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/chat_state.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/context_coder.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/context_prompts.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/edit_apply.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/editblock_fenced_coder.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/editblock_fenced_prompts.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/editblock_func_coder.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/editblock_func_prompts.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/editblock_prompts.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/editor_diff_fenced_coder.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/editor_diff_fenced_prompts.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/editor_editblock_coder.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/editor_editblock_prompts.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/editor_whole_coder.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/editor_whole_prompts.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/patch_coder.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/patch_prompts.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/search_replace.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/shell.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/single_wholefile_func_coder.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/single_wholefile_func_prompts.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/udiff_prompts.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/udiff_simple.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/udiff_simple_prompts.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/wholefile_func_coder.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/wholefile_func_prompts.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/coders/wholefile_prompts.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/copypaste.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/deprecated.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/diffs.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/dump.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/editor.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/exceptions.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/format_settings.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/gui.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/history.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/io.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/linter.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/llm.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/mdstream.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/models.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/onboarding.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/openrouter.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/prompts.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/README.md +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/arduino-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/bash-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/c-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/chatito-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/clojure-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/commonlisp-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/cpp-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/csharp-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/d-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/dart-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/elisp-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/elixir-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/elm-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/gleam-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/go-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/java-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/javascript-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/lua-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/matlab-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/ocaml-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/pony-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/properties-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/python-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/r-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/racket-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/ruby-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/rust-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/solidity-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/swift-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-language-pack/udev-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/README.md +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/bash-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/c-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/c_sharp-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/cpp-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/dart-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/elisp-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/elixir-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/elm-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/fortran-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/go-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/haskell-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/hcl-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/java-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/javascript-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/julia-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/kotlin-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/matlab-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/ocaml-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/ocaml_interface-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/php-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/python-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/ql-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/ruby-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/rust-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/scala-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/typescript-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/queries/tree-sitter-languages/zig-tags.scm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/reasoning_tags.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/repo_trust.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/repomap.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/resources/__init__.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/resources/model-metadata.json +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/resources/model-settings.yml +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/run_cmd.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/scrape.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/sendchat.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/special.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/utils.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/versioncheck.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/voice.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/waiting.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch/watch_prompts.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch_chat.egg-info/dependency_links.txt +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch_chat.egg-info/entry_points.txt +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/patch_chat.egg-info/top_level.txt +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/pytest.ini +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/requirements/pydub.in +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/requirements/python-compat.in +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/requirements/requirements-browser.in +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/requirements/requirements-dev.in +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/requirements/requirements-playwright.in +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/requirements/requirements-playwright.txt +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/requirements/tree-sitter.in +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/scripts/__init__.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/scripts/blame.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/scripts/clean_metadata.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/scripts/issues.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/scripts/my_models.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/scripts/pip-compile.sh +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/scripts/redact-cast.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/scripts/tmux_record.sh +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/scripts/tsl_pack_langs.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/scripts/yank-old-versions.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/setup.cfg +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/__init__.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/__init__.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_analytics.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_aws_credentials.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_copypaste.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_deprecated.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_edit_apply.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_editor.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_exceptions.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_find_or_blocks.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_format_settings.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_history.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_io.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_linter.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_llm.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_mdstream.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_model_info_manager.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_models.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_onboarding.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_openrouter.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_patch_coder.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_path_containment.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_reasoning.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_repo_config_trust.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_repomap.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_run_cmd.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_scripting.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_sendchat.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_special.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_ssl_verification.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_utils.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_versioncheck.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_voice.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/basic/test_waiting.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/browser/test_browser.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/conftest.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/chat-history-search-replace-gold.txt +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/chat-history.md +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/arduino/test.ino +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/bash/test.sh +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/c/test.c +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/chatito/test.chatito +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/clojure/test.clj +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/commonlisp/test.lisp +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/cpp/test.cpp +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/csharp/test.cs +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/d/test.d +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/dart/test.dart +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/elisp/test.el +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/elixir/test.ex +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/elm/test.elm +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/gleam/test.gleam +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/go/test.go +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/haskell/test.hs +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/hcl/test.tf +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/java/test.java +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/javascript/test.js +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/kotlin/test.kt +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/lua/test.lua +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/matlab/test.m +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/ocaml/test.ml +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/ocaml_interface/test.mli +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/php/test.php +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/pony/test.pony +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/properties/test.properties +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/python/test.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/ql/test.ql +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/r/test.r +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/racket/test.rkt +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/ruby/test.rb +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/rust/test.rs +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/scala/test.scala +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/solidity/test.sol +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/swift/test.swift +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/tsx/test.tsx +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/typescript/test.ts +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/udev/test.rules +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/languages/zig/test.zig +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/sample-code-base/sample.js +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/sample-code-base/sample.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/sample-code-base-repo-map.txt +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/watch.js +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/watch.lisp +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/watch.py +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/fixtures/watch_question.js +0 -0
- {patch_chat-0.3.0 → patch_chat-0.4.0}/tests/scrape/test_playwright_disable.py +0 -0
- {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
|
|
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.
|
|
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.
|
|
102
|
+
## 7. Nothing goes upstream
|
|
108
103
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
|
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
|
|
136
|
-
`patch/foo.py` here, and a broken user environment has no fix in this code —
|
|
137
|
-
regression test, update the docs above, commit with `Closes #N
|
|
138
|
-
|
|
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.
|
|
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
|
+
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
22
|
-
__version_tuple__ = version_tuple = (0,
|
|
21
|
+
__version__ = version = '0.4.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 4, 0)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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 = []
|