patch-code 0.1.0__py3-none-any.whl
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/__init__.py +3 -0
- patch/__main__.py +4 -0
- patch/analytics.py +268 -0
- patch/args.py +949 -0
- patch/args_formatter.py +227 -0
- patch/coders/__init__.py +34 -0
- patch/coders/architect_coder.py +48 -0
- patch/coders/architect_prompts.py +40 -0
- patch/coders/ask_coder.py +9 -0
- patch/coders/ask_prompts.py +41 -0
- patch/coders/base_coder.py +2485 -0
- patch/coders/base_prompts.py +60 -0
- patch/coders/chat_chunks.py +64 -0
- patch/coders/context_coder.py +53 -0
- patch/coders/context_prompts.py +75 -0
- patch/coders/editblock_coder.py +657 -0
- patch/coders/editblock_fenced_coder.py +10 -0
- patch/coders/editblock_fenced_prompts.py +143 -0
- patch/coders/editblock_func_coder.py +141 -0
- patch/coders/editblock_func_prompts.py +27 -0
- patch/coders/editblock_prompts.py +172 -0
- patch/coders/editor_diff_fenced_coder.py +9 -0
- patch/coders/editor_diff_fenced_prompts.py +11 -0
- patch/coders/editor_editblock_coder.py +8 -0
- patch/coders/editor_editblock_prompts.py +18 -0
- patch/coders/editor_whole_coder.py +8 -0
- patch/coders/editor_whole_prompts.py +10 -0
- patch/coders/help_coder.py +16 -0
- patch/coders/help_prompts.py +46 -0
- patch/coders/patch_coder.py +706 -0
- patch/coders/patch_prompts.py +159 -0
- patch/coders/search_replace.py +757 -0
- patch/coders/shell.py +37 -0
- patch/coders/single_wholefile_func_coder.py +102 -0
- patch/coders/single_wholefile_func_prompts.py +27 -0
- patch/coders/udiff_coder.py +429 -0
- patch/coders/udiff_prompts.py +113 -0
- patch/coders/udiff_simple.py +14 -0
- patch/coders/udiff_simple_prompts.py +25 -0
- patch/coders/wholefile_coder.py +144 -0
- patch/coders/wholefile_func_coder.py +134 -0
- patch/coders/wholefile_func_prompts.py +27 -0
- patch/coders/wholefile_prompts.py +64 -0
- patch/commands.py +1712 -0
- patch/copypaste.py +72 -0
- patch/deprecated.py +126 -0
- patch/diffs.py +128 -0
- patch/docs/__init__.py +1 -0
- patch/docs/analytics.md +28 -0
- patch/docs/config.md +43 -0
- patch/docs/git.md +22 -0
- patch/docs/install.md +54 -0
- patch/docs/models.md +59 -0
- patch/docs/troubleshooting.md +23 -0
- patch/docs/usage.md +47 -0
- patch/dump.py +29 -0
- patch/editor.py +147 -0
- patch/exceptions.py +113 -0
- patch/format_settings.py +26 -0
- patch/gui.py +545 -0
- patch/help.py +118 -0
- patch/history.py +143 -0
- patch/io.py +1191 -0
- patch/linter.py +304 -0
- patch/llm.py +47 -0
- patch/main.py +1274 -0
- patch/mdstream.py +243 -0
- patch/models.py +1338 -0
- patch/onboarding.py +428 -0
- patch/openrouter.py +128 -0
- patch/prompts.py +61 -0
- patch/queries/tree-sitter-language-pack/arduino-tags.scm +5 -0
- patch/queries/tree-sitter-language-pack/bash-tags.scm +8 -0
- patch/queries/tree-sitter-language-pack/c-tags.scm +9 -0
- patch/queries/tree-sitter-language-pack/chatito-tags.scm +16 -0
- patch/queries/tree-sitter-language-pack/clojure-tags.scm +7 -0
- patch/queries/tree-sitter-language-pack/commonlisp-tags.scm +122 -0
- patch/queries/tree-sitter-language-pack/cpp-tags.scm +15 -0
- patch/queries/tree-sitter-language-pack/csharp-tags.scm +26 -0
- patch/queries/tree-sitter-language-pack/d-tags.scm +26 -0
- patch/queries/tree-sitter-language-pack/dart-tags.scm +92 -0
- patch/queries/tree-sitter-language-pack/elisp-tags.scm +5 -0
- patch/queries/tree-sitter-language-pack/elixir-tags.scm +54 -0
- patch/queries/tree-sitter-language-pack/elm-tags.scm +19 -0
- patch/queries/tree-sitter-language-pack/gleam-tags.scm +41 -0
- patch/queries/tree-sitter-language-pack/go-tags.scm +42 -0
- patch/queries/tree-sitter-language-pack/java-tags.scm +20 -0
- patch/queries/tree-sitter-language-pack/javascript-tags.scm +88 -0
- patch/queries/tree-sitter-language-pack/lua-tags.scm +34 -0
- patch/queries/tree-sitter-language-pack/matlab-tags.scm +10 -0
- patch/queries/tree-sitter-language-pack/ocaml-tags.scm +115 -0
- patch/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +98 -0
- patch/queries/tree-sitter-language-pack/pony-tags.scm +39 -0
- patch/queries/tree-sitter-language-pack/properties-tags.scm +5 -0
- patch/queries/tree-sitter-language-pack/python-tags.scm +14 -0
- patch/queries/tree-sitter-language-pack/r-tags.scm +21 -0
- patch/queries/tree-sitter-language-pack/racket-tags.scm +12 -0
- patch/queries/tree-sitter-language-pack/ruby-tags.scm +64 -0
- patch/queries/tree-sitter-language-pack/rust-tags.scm +60 -0
- patch/queries/tree-sitter-language-pack/solidity-tags.scm +43 -0
- patch/queries/tree-sitter-language-pack/swift-tags.scm +51 -0
- patch/queries/tree-sitter-language-pack/udev-tags.scm +20 -0
- patch/queries/tree-sitter-languages/bash-tags.scm +8 -0
- patch/queries/tree-sitter-languages/c-tags.scm +9 -0
- patch/queries/tree-sitter-languages/c_sharp-tags.scm +46 -0
- patch/queries/tree-sitter-languages/cpp-tags.scm +15 -0
- patch/queries/tree-sitter-languages/dart-tags.scm +91 -0
- patch/queries/tree-sitter-languages/elisp-tags.scm +8 -0
- patch/queries/tree-sitter-languages/elixir-tags.scm +54 -0
- patch/queries/tree-sitter-languages/elm-tags.scm +19 -0
- patch/queries/tree-sitter-languages/fortran-tags.scm +15 -0
- patch/queries/tree-sitter-languages/go-tags.scm +30 -0
- patch/queries/tree-sitter-languages/haskell-tags.scm +3 -0
- patch/queries/tree-sitter-languages/hcl-tags.scm +77 -0
- patch/queries/tree-sitter-languages/java-tags.scm +20 -0
- patch/queries/tree-sitter-languages/javascript-tags.scm +88 -0
- patch/queries/tree-sitter-languages/julia-tags.scm +60 -0
- patch/queries/tree-sitter-languages/kotlin-tags.scm +27 -0
- patch/queries/tree-sitter-languages/matlab-tags.scm +10 -0
- patch/queries/tree-sitter-languages/ocaml-tags.scm +115 -0
- patch/queries/tree-sitter-languages/ocaml_interface-tags.scm +98 -0
- patch/queries/tree-sitter-languages/php-tags.scm +26 -0
- patch/queries/tree-sitter-languages/python-tags.scm +12 -0
- patch/queries/tree-sitter-languages/ql-tags.scm +26 -0
- patch/queries/tree-sitter-languages/ruby-tags.scm +64 -0
- patch/queries/tree-sitter-languages/rust-tags.scm +60 -0
- patch/queries/tree-sitter-languages/scala-tags.scm +65 -0
- patch/queries/tree-sitter-languages/typescript-tags.scm +41 -0
- patch/queries/tree-sitter-languages/zig-tags.scm +3 -0
- patch/reasoning_tags.py +82 -0
- patch/repo.py +622 -0
- patch/repomap.py +867 -0
- patch/report.py +200 -0
- patch/resources/__init__.py +3 -0
- patch/resources/model-metadata.json +715 -0
- patch/resources/model-settings.yml +3128 -0
- patch/run_cmd.py +132 -0
- patch/scrape.py +284 -0
- patch/sendchat.py +61 -0
- patch/special.py +203 -0
- patch/urls.py +17 -0
- patch/utils.py +348 -0
- patch/versioncheck.py +130 -0
- patch/voice.py +187 -0
- patch/waiting.py +221 -0
- patch/watch.py +318 -0
- patch/watch_prompts.py +12 -0
- patch_code-0.1.0.dist-info/METADATA +467 -0
- patch_code-0.1.0.dist-info/RECORD +153 -0
- patch_code-0.1.0.dist-info/WHEEL +5 -0
- patch_code-0.1.0.dist-info/entry_points.txt +2 -0
- patch_code-0.1.0.dist-info/licenses/LICENSE.txt +202 -0
- patch_code-0.1.0.dist-info/top_level.txt +1 -0
patch/copypaste.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
import pyperclip
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ClipboardWatcher:
|
|
8
|
+
"""Watches clipboard for changes and updates IO placeholder"""
|
|
9
|
+
|
|
10
|
+
def __init__(self, io, verbose=False):
|
|
11
|
+
self.io = io
|
|
12
|
+
self.verbose = verbose
|
|
13
|
+
self.stop_event = None
|
|
14
|
+
self.watcher_thread = None
|
|
15
|
+
self.last_clipboard = None
|
|
16
|
+
self.io.clipboard_watcher = self
|
|
17
|
+
|
|
18
|
+
def start(self):
|
|
19
|
+
"""Start watching clipboard for changes"""
|
|
20
|
+
self.stop_event = threading.Event()
|
|
21
|
+
self.last_clipboard = pyperclip.paste()
|
|
22
|
+
|
|
23
|
+
def watch_clipboard():
|
|
24
|
+
while not self.stop_event.is_set():
|
|
25
|
+
try:
|
|
26
|
+
current = pyperclip.paste()
|
|
27
|
+
if current != self.last_clipboard:
|
|
28
|
+
self.last_clipboard = current
|
|
29
|
+
self.io.interrupt_input()
|
|
30
|
+
self.io.placeholder = current
|
|
31
|
+
if len(current.splitlines()) > 1:
|
|
32
|
+
self.io.placeholder = "\n" + self.io.placeholder + "\n"
|
|
33
|
+
|
|
34
|
+
time.sleep(0.5)
|
|
35
|
+
except Exception as e:
|
|
36
|
+
if self.verbose:
|
|
37
|
+
from patch.dump import dump
|
|
38
|
+
|
|
39
|
+
dump(f"Clipboard watcher error: {e}")
|
|
40
|
+
continue
|
|
41
|
+
|
|
42
|
+
self.watcher_thread = threading.Thread(target=watch_clipboard, daemon=True)
|
|
43
|
+
self.watcher_thread.start()
|
|
44
|
+
|
|
45
|
+
def stop(self):
|
|
46
|
+
"""Stop watching clipboard for changes"""
|
|
47
|
+
if self.stop_event:
|
|
48
|
+
self.stop_event.set()
|
|
49
|
+
if self.watcher_thread:
|
|
50
|
+
self.watcher_thread.join()
|
|
51
|
+
self.watcher_thread = None
|
|
52
|
+
self.stop_event = None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def main():
|
|
56
|
+
"""Example usage of the clipboard watcher"""
|
|
57
|
+
from patch.io import InputOutput
|
|
58
|
+
|
|
59
|
+
io = InputOutput()
|
|
60
|
+
watcher = ClipboardWatcher(io, verbose=True)
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
watcher.start()
|
|
64
|
+
while True:
|
|
65
|
+
time.sleep(1)
|
|
66
|
+
except KeyboardInterrupt:
|
|
67
|
+
print("\nStopped watching clipboard")
|
|
68
|
+
watcher.stop()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
if __name__ == "__main__":
|
|
72
|
+
main()
|
patch/deprecated.py
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
def add_deprecated_model_args(parser, group):
|
|
2
|
+
"""Add deprecated model shortcut arguments to the argparse parser."""
|
|
3
|
+
opus_model = "claude-3-opus-20240229"
|
|
4
|
+
group.add_argument(
|
|
5
|
+
"--opus",
|
|
6
|
+
action="store_true",
|
|
7
|
+
help=f"Use {opus_model} model for the main chat (deprecated, use --model)",
|
|
8
|
+
default=False,
|
|
9
|
+
)
|
|
10
|
+
sonnet_model = "anthropic/claude-3-7-sonnet-20250219"
|
|
11
|
+
group.add_argument(
|
|
12
|
+
"--sonnet",
|
|
13
|
+
action="store_true",
|
|
14
|
+
help=f"Use {sonnet_model} model for the main chat (deprecated, use --model)",
|
|
15
|
+
default=False,
|
|
16
|
+
)
|
|
17
|
+
haiku_model = "claude-3-5-haiku-20241022"
|
|
18
|
+
group.add_argument(
|
|
19
|
+
"--haiku",
|
|
20
|
+
action="store_true",
|
|
21
|
+
help=f"Use {haiku_model} model for the main chat (deprecated, use --model)",
|
|
22
|
+
default=False,
|
|
23
|
+
)
|
|
24
|
+
gpt_4_model = "gpt-4-0613"
|
|
25
|
+
group.add_argument(
|
|
26
|
+
"--4",
|
|
27
|
+
"-4",
|
|
28
|
+
action="store_true",
|
|
29
|
+
help=f"Use {gpt_4_model} model for the main chat (deprecated, use --model)",
|
|
30
|
+
default=False,
|
|
31
|
+
)
|
|
32
|
+
gpt_4o_model = "gpt-4o"
|
|
33
|
+
group.add_argument(
|
|
34
|
+
"--4o",
|
|
35
|
+
action="store_true",
|
|
36
|
+
help=f"Use {gpt_4o_model} model for the main chat (deprecated, use --model)",
|
|
37
|
+
default=False,
|
|
38
|
+
)
|
|
39
|
+
gpt_4o_mini_model = "gpt-4o-mini"
|
|
40
|
+
group.add_argument(
|
|
41
|
+
"--mini",
|
|
42
|
+
action="store_true",
|
|
43
|
+
help=f"Use {gpt_4o_mini_model} model for the main chat (deprecated, use --model)",
|
|
44
|
+
default=False,
|
|
45
|
+
)
|
|
46
|
+
gpt_4_turbo_model = "gpt-4-1106-preview"
|
|
47
|
+
group.add_argument(
|
|
48
|
+
"--4-turbo",
|
|
49
|
+
action="store_true",
|
|
50
|
+
help=f"Use {gpt_4_turbo_model} model for the main chat (deprecated, use --model)",
|
|
51
|
+
default=False,
|
|
52
|
+
)
|
|
53
|
+
gpt_3_model_name = "gpt-3.5-turbo"
|
|
54
|
+
group.add_argument(
|
|
55
|
+
"--35turbo",
|
|
56
|
+
"--35-turbo",
|
|
57
|
+
"--3",
|
|
58
|
+
"-3",
|
|
59
|
+
action="store_true",
|
|
60
|
+
help=f"Use {gpt_3_model_name} model for the main chat (deprecated, use --model)",
|
|
61
|
+
default=False,
|
|
62
|
+
)
|
|
63
|
+
deepseek_model = "deepseek/deepseek-chat"
|
|
64
|
+
group.add_argument(
|
|
65
|
+
"--deepseek",
|
|
66
|
+
action="store_true",
|
|
67
|
+
help=f"Use {deepseek_model} model for the main chat (deprecated, use --model)",
|
|
68
|
+
default=False,
|
|
69
|
+
)
|
|
70
|
+
o1_mini_model = "o1-mini"
|
|
71
|
+
group.add_argument(
|
|
72
|
+
"--o1-mini",
|
|
73
|
+
action="store_true",
|
|
74
|
+
help=f"Use {o1_mini_model} model for the main chat (deprecated, use --model)",
|
|
75
|
+
default=False,
|
|
76
|
+
)
|
|
77
|
+
o1_preview_model = "o1-preview"
|
|
78
|
+
group.add_argument(
|
|
79
|
+
"--o1-preview",
|
|
80
|
+
action="store_true",
|
|
81
|
+
help=f"Use {o1_preview_model} model for the main chat (deprecated, use --model)",
|
|
82
|
+
default=False,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def handle_deprecated_model_args(args, io):
|
|
87
|
+
"""Handle deprecated model shortcut arguments and provide appropriate warnings."""
|
|
88
|
+
# Define model mapping
|
|
89
|
+
model_map = {
|
|
90
|
+
"opus": "claude-3-opus-20240229",
|
|
91
|
+
"sonnet": "anthropic/claude-3-7-sonnet-20250219",
|
|
92
|
+
"haiku": "claude-3-5-haiku-20241022",
|
|
93
|
+
"4": "gpt-4-0613",
|
|
94
|
+
"4o": "gpt-4o",
|
|
95
|
+
"mini": "gpt-4o-mini",
|
|
96
|
+
"4_turbo": "gpt-4-1106-preview",
|
|
97
|
+
"35turbo": "gpt-3.5-turbo",
|
|
98
|
+
"deepseek": "deepseek/deepseek-chat",
|
|
99
|
+
"o1_mini": "o1-mini",
|
|
100
|
+
"o1_preview": "o1-preview",
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# Check if any deprecated args are used
|
|
104
|
+
for arg_name, model_name in model_map.items():
|
|
105
|
+
arg_name_clean = arg_name.replace("-", "_")
|
|
106
|
+
if hasattr(args, arg_name_clean) and getattr(args, arg_name_clean):
|
|
107
|
+
# Find preferred name to display in warning
|
|
108
|
+
from patch.models import MODEL_ALIASES
|
|
109
|
+
|
|
110
|
+
display_name = model_name
|
|
111
|
+
# Check if there's a shorter alias for this model
|
|
112
|
+
for alias, full_name in MODEL_ALIASES.items():
|
|
113
|
+
if full_name == model_name:
|
|
114
|
+
display_name = alias
|
|
115
|
+
break
|
|
116
|
+
|
|
117
|
+
# Show the warning
|
|
118
|
+
io.tool_warning(
|
|
119
|
+
f"The --{arg_name.replace('_', '-')} flag is deprecated and will be removed in a"
|
|
120
|
+
f" future version. Please use --model {display_name} instead."
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# Set the model
|
|
124
|
+
if not args.model:
|
|
125
|
+
args.model = model_name
|
|
126
|
+
break
|
patch/diffs.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import difflib
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from .dump import dump # noqa: F401
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def main():
|
|
8
|
+
if len(sys.argv) != 3:
|
|
9
|
+
print("Usage: python diffs.py file1 file")
|
|
10
|
+
sys.exit(1)
|
|
11
|
+
|
|
12
|
+
file_orig, file_updated = sys.argv[1], sys.argv[2]
|
|
13
|
+
|
|
14
|
+
with open(file_orig, "r", encoding="utf-8") as f:
|
|
15
|
+
lines_orig = f.readlines()
|
|
16
|
+
|
|
17
|
+
with open(file_updated, "r", encoding="utf-8") as f:
|
|
18
|
+
lines_updated = f.readlines()
|
|
19
|
+
|
|
20
|
+
for i in range(len(file_updated)):
|
|
21
|
+
res = diff_partial_update(lines_orig, lines_updated[:i])
|
|
22
|
+
print(res)
|
|
23
|
+
input()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def create_progress_bar(percentage):
|
|
27
|
+
block = "█"
|
|
28
|
+
empty = "░"
|
|
29
|
+
total_blocks = 30
|
|
30
|
+
filled_blocks = int(total_blocks * percentage // 100)
|
|
31
|
+
empty_blocks = total_blocks - filled_blocks
|
|
32
|
+
bar = block * filled_blocks + empty * empty_blocks
|
|
33
|
+
return bar
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def assert_newlines(lines):
|
|
37
|
+
if not lines:
|
|
38
|
+
return
|
|
39
|
+
for line in lines[:-1]:
|
|
40
|
+
assert line and line[-1] == "\n", line
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def diff_partial_update(lines_orig, lines_updated, final=False, fname=None):
|
|
44
|
+
"""
|
|
45
|
+
Given only the first part of an updated file, show the diff while
|
|
46
|
+
ignoring the block of "deleted" lines that are past the end of the
|
|
47
|
+
partially complete update.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
# dump(lines_orig)
|
|
51
|
+
# dump(lines_updated)
|
|
52
|
+
|
|
53
|
+
assert_newlines(lines_orig)
|
|
54
|
+
|
|
55
|
+
num_orig_lines = len(lines_orig)
|
|
56
|
+
|
|
57
|
+
if final:
|
|
58
|
+
last_non_deleted = num_orig_lines
|
|
59
|
+
else:
|
|
60
|
+
last_non_deleted = find_last_non_deleted(lines_orig, lines_updated)
|
|
61
|
+
|
|
62
|
+
# dump(last_non_deleted)
|
|
63
|
+
if last_non_deleted is None:
|
|
64
|
+
return ""
|
|
65
|
+
|
|
66
|
+
if num_orig_lines:
|
|
67
|
+
pct = last_non_deleted * 100 / num_orig_lines
|
|
68
|
+
else:
|
|
69
|
+
pct = 50
|
|
70
|
+
bar = create_progress_bar(pct)
|
|
71
|
+
bar = f" {last_non_deleted:3d} / {num_orig_lines:3d} lines [{bar}] {pct:3.0f}%\n"
|
|
72
|
+
|
|
73
|
+
lines_orig = lines_orig[:last_non_deleted]
|
|
74
|
+
|
|
75
|
+
if not final:
|
|
76
|
+
lines_updated = lines_updated[:-1] + [bar]
|
|
77
|
+
|
|
78
|
+
diff = difflib.unified_diff(lines_orig, lines_updated, n=5)
|
|
79
|
+
|
|
80
|
+
diff = list(diff)[2:]
|
|
81
|
+
|
|
82
|
+
diff = "".join(diff)
|
|
83
|
+
if not diff.endswith("\n"):
|
|
84
|
+
diff += "\n"
|
|
85
|
+
|
|
86
|
+
for i in range(3, 10):
|
|
87
|
+
backticks = "`" * i
|
|
88
|
+
if backticks not in diff:
|
|
89
|
+
break
|
|
90
|
+
|
|
91
|
+
show = f"{backticks}diff\n"
|
|
92
|
+
if fname:
|
|
93
|
+
show += f"--- {fname} original\n"
|
|
94
|
+
show += f"+++ {fname} updated\n"
|
|
95
|
+
|
|
96
|
+
show += diff
|
|
97
|
+
|
|
98
|
+
show += f"{backticks}\n\n"
|
|
99
|
+
|
|
100
|
+
# print(diff)
|
|
101
|
+
|
|
102
|
+
return show
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def find_last_non_deleted(lines_orig, lines_updated):
|
|
106
|
+
diff = list(difflib.ndiff(lines_orig, lines_updated))
|
|
107
|
+
|
|
108
|
+
num_orig = 0
|
|
109
|
+
last_non_deleted_orig = None
|
|
110
|
+
|
|
111
|
+
for line in diff:
|
|
112
|
+
# print(f"{num_orig:2d} {num_updated:2d} {line}", end="")
|
|
113
|
+
code = line[0]
|
|
114
|
+
if code == " ":
|
|
115
|
+
num_orig += 1
|
|
116
|
+
last_non_deleted_orig = num_orig
|
|
117
|
+
elif code == "-":
|
|
118
|
+
# line only in orig
|
|
119
|
+
num_orig += 1
|
|
120
|
+
elif code == "+":
|
|
121
|
+
# line only in updated
|
|
122
|
+
pass
|
|
123
|
+
|
|
124
|
+
return last_non_deleted_orig
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
if __name__ == "__main__":
|
|
128
|
+
main()
|
patch/docs/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Packaged Markdown documentation for Patch's interactive help."""
|
patch/docs/analytics.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Analytics
|
|
2
|
+
|
|
3
|
+
Patch ships no analytics destination. No analytics events are sent and no opt-in
|
|
4
|
+
prompt appears unless you configure your own PostHog project. Upstream Aider's
|
|
5
|
+
project keys have been removed. Patch operates no analytics service.
|
|
6
|
+
|
|
7
|
+
## Your own telemetry
|
|
8
|
+
|
|
9
|
+
The inherited instrumentation can be enabled with both
|
|
10
|
+
`--analytics-posthog-project-api-key KEY` and `--analytics`. The first time,
|
|
11
|
+
Patch asks you to confirm. `--analytics-posthog-host HOST` selects a custom host.
|
|
12
|
+
`--no-analytics` disables collection for the session;
|
|
13
|
+
`python -m patch --analytics-disable` records a permanent opt-out.
|
|
14
|
+
|
|
15
|
+
Events include feature usage, public model names, token counts, error indicators,
|
|
16
|
+
system information, and a random UUID. Unknown model names are redacted.
|
|
17
|
+
Automatic exception capture is disabled. Review [the source](../analytics.py)
|
|
18
|
+
before enabling instrumentation for your own deployment.
|
|
19
|
+
|
|
20
|
+
## Local logs
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
python -m patch --analytics-log events.jsonl --no-analytics
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
This writes events locally without uploading them. Review logs before sharing
|
|
27
|
+
them and keep them out of version control. User-level analytics preferences and
|
|
28
|
+
the UUID are stored in `~/.patch/analytics.json`.
|
patch/docs/config.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
Run `python -m patch --help` for the complete, current CLI option reference.
|
|
4
|
+
Most options also accept `.patch.conf.yml` entries or `PATCH_*` environment variables.
|
|
5
|
+
For example, `--dark-mode`, `dark-mode: true` in YAML, and `PATCH_DARK_MODE=true`
|
|
6
|
+
are equivalent. Command-line values override environment and configuration values.
|
|
7
|
+
|
|
8
|
+
Patch searches for `.patch.conf.yml` in your home directory, Git repository root,
|
|
9
|
+
and current directory, with nearer configuration taking precedence. Use
|
|
10
|
+
`--config FILE` for an explicit configuration file.
|
|
11
|
+
|
|
12
|
+
```yaml
|
|
13
|
+
model: sonnet
|
|
14
|
+
dark-mode: true
|
|
15
|
+
auto-commits: false
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Environment and credentials
|
|
19
|
+
|
|
20
|
+
Provider credentials retain their own names, such as `ANTHROPIC_API_KEY` and
|
|
21
|
+
`OPENAI_API_KEY`. Store them in your shell environment or an untracked `.env` file.
|
|
22
|
+
Use `--env-file FILE` to select an environment file. YAML `api-key` entries and
|
|
23
|
+
`--api-key provider=KEY` also support providers beyond OpenAI and Anthropic.
|
|
24
|
+
`--set-env NAME=value` sets other provider variables.
|
|
25
|
+
|
|
26
|
+
Review repository-local YAML and `.env` files before running Patch: they can
|
|
27
|
+
configure executable commands and provider endpoints. Never load untrusted
|
|
28
|
+
repository settings with credentials present.
|
|
29
|
+
|
|
30
|
+
## Linting and tests
|
|
31
|
+
|
|
32
|
+
Patch can automatically lint edited files (`--auto-lint`, enabled by default).
|
|
33
|
+
Configure `--lint-cmd COMMAND` or `--lint-cmd 'python: COMMAND'`. Use
|
|
34
|
+
`--test-cmd COMMAND` with `--auto-test` for automatic tests, or `/test COMMAND`
|
|
35
|
+
inside chat. Only configure trusted commands.
|
|
36
|
+
|
|
37
|
+
## State and privacy
|
|
38
|
+
|
|
39
|
+
Patch uses `.patch*` state files and `~/.patch/` for user-level data and caches.
|
|
40
|
+
`--input-history-file` and `--chat-history-file` change history destinations.
|
|
41
|
+
History, logs, and verbose settings can contain sensitive data; do not publish them
|
|
42
|
+
without review. Patch does not migrate `.aider*` or `AIDER_*` settings automatically.
|
|
43
|
+
See [analytics](analytics.md) for the disabled-by-default telemetry instrumentation.
|
patch/docs/git.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Git integration
|
|
2
|
+
|
|
3
|
+
Patch works best in a Git repository. By default it commits its edits with a
|
|
4
|
+
generated message and can commit pre-existing dirty changes before editing those
|
|
5
|
+
files. Review changes with `/diff` and normal Git commands.
|
|
6
|
+
|
|
7
|
+
`--no-auto-commits` disables automatic commits of model edits.
|
|
8
|
+
`--no-dirty-commits` disables the pre-edit commit of existing changes.
|
|
9
|
+
`--no-git` disables Git integration. `/commit` asks Patch to commit changes;
|
|
10
|
+
`/git <arguments>` runs Git directly.
|
|
11
|
+
|
|
12
|
+
## Undo
|
|
13
|
+
|
|
14
|
+
`/undo` reverts the last eligible Patch commit. It is not a general backup system;
|
|
15
|
+
check `git status` and `git diff` before destructive Git operations. Keep your own
|
|
16
|
+
commits and backups, especially when automatic commits are disabled.
|
|
17
|
+
|
|
18
|
+
## Attribution
|
|
19
|
+
|
|
20
|
+
Use `--attribute-author`, `--attribute-committer`, and the commit-message
|
|
21
|
+
attribution options shown by `python -m patch --help` to control Patch attribution.
|
|
22
|
+
Configure your own Git user name and email before working in a repository.
|
patch/docs/install.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Installing Patch
|
|
2
|
+
|
|
3
|
+
Patch supports Python 3.10–3.14. Use an isolated virtual environment:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
python -m venv .venv
|
|
7
|
+
# macOS / Linux:
|
|
8
|
+
source .venv/bin/activate
|
|
9
|
+
# Windows PowerShell: .venv\Scripts\Activate.ps1
|
|
10
|
+
python -m pip install patch-code
|
|
11
|
+
python -m patch --help
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Run `python -m pip install --upgrade patch-code` to upgrade. Update notices do not
|
|
15
|
+
install anything automatically. Use `python -m patch` to avoid the Unix `patch`
|
|
16
|
+
utility. For source development see [CONTRIBUTING.md](../../CONTRIBUTING.md).
|
|
17
|
+
|
|
18
|
+
## Interactive help
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
python -m pip install 'patch-code[help]' --extra-index-url https://download.pytorch.org/whl/cpu
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`/help <question>` can offer to install this extra. The first use downloads a
|
|
25
|
+
Hugging Face embedding model and indexes the packaged Markdown docs; subsequent
|
|
26
|
+
uses cache the index under `~/.patch/caches/`. Retrieval runs locally, but the
|
|
27
|
+
question and relevant docs are sent to your configured chat model for an answer.
|
|
28
|
+
Bare `/help` and `python -m patch --help` do not require this extra.
|
|
29
|
+
|
|
30
|
+
## Browser UI
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
python -m pip install 'patch-code[browser]'
|
|
34
|
+
python -m patch --browser
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The experimental UI is intended for a trusted single user. Do not expose it to
|
|
38
|
+
untrusted networks or share it with other users: it can edit files and run commands.
|
|
39
|
+
|
|
40
|
+
## Enable Playwright
|
|
41
|
+
|
|
42
|
+
For pages that need browser rendering:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
python -m pip install 'patch-code[playwright]'
|
|
46
|
+
python -m playwright install --with-deps chromium
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Installation problems
|
|
50
|
+
|
|
51
|
+
Check that `python --version` is supported and `python -m pip --version` points at
|
|
52
|
+
the active environment. Use the same Python for installation and launch. Avoid
|
|
53
|
+
naming local files `patch.py` or `patch`, which can shadow the installed package.
|
|
54
|
+
Recreate the virtual environment if it contains incompatible dependencies.
|
patch/docs/models.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Models
|
|
2
|
+
|
|
3
|
+
Select a model with `python -m patch --model MODEL` or `/model MODEL` in chat.
|
|
4
|
+
`python -m patch --list-models QUERY` lists matching known names. Aliases such as
|
|
5
|
+
`sonnet` are defined in [the model module](../models.py); they can change over time.
|
|
6
|
+
|
|
7
|
+
## API keys
|
|
8
|
+
|
|
9
|
+
Use provider environment variables such as `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`,
|
|
10
|
+
`GEMINI_API_KEY`, or `OPENROUTER_API_KEY`. Alternatively use
|
|
11
|
+
`--api-key anthropic=YOUR_KEY` (substitute your provider). Shell history and process
|
|
12
|
+
listings can expose command-line keys, so prefer a private environment or `.env`.
|
|
13
|
+
Never commit credentials or paste them into issue reports.
|
|
14
|
+
|
|
15
|
+
Examples of model selection:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
python -m patch --model sonnet
|
|
19
|
+
python -m patch --model openrouter/anthropic/claude-3.5-sonnet
|
|
20
|
+
python -m patch --model ollama_chat/YOUR_LOCAL_MODEL
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
For Ollama, start its server and pull the model first; set `OLLAMA_API_BASE` if
|
|
24
|
+
needed. For an OpenAI-compatible server use an `openai/` model name and configure
|
|
25
|
+
`OPENAI_API_BASE` and `OPENAI_API_KEY` as required by that server. Availability,
|
|
26
|
+
prices, permissions, and context limits are controlled by the provider.
|
|
27
|
+
|
|
28
|
+
## Model warnings
|
|
29
|
+
|
|
30
|
+
Unknown context limits or token costs mean Patch lacks model metadata, not
|
|
31
|
+
necessarily that the model cannot work. Check the provider's model name and limits.
|
|
32
|
+
Add metadata with `.patch.model.metadata.json` or `--model-metadata-file`.
|
|
33
|
+
The JSON maps model names to LiteLLM metadata such as `max_input_tokens`,
|
|
34
|
+
`max_output_tokens`, `input_cost_per_token`, `output_cost_per_token`,
|
|
35
|
+
`litellm_provider`, and `mode`.
|
|
36
|
+
|
|
37
|
+
## Model settings
|
|
38
|
+
|
|
39
|
+
Use `.patch.model.settings.yml` or `--model-settings-file` for a YAML list of
|
|
40
|
+
settings. For example:
|
|
41
|
+
|
|
42
|
+
```yaml
|
|
43
|
+
- name: openai/your-model
|
|
44
|
+
edit_format: whole
|
|
45
|
+
weak_model_name: null
|
|
46
|
+
use_repo_map: true
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
See [the packaged settings](../resources/model-settings.yml) for supported examples.
|
|
50
|
+
These are distinct from model metadata. Use `--alias short:provider/model` to
|
|
51
|
+
define a short name. See [configuration](config.md) for storing command options.
|
|
52
|
+
|
|
53
|
+
## Edit formats
|
|
54
|
+
|
|
55
|
+
Patch chooses an edit format from its model settings. `diff` uses SEARCH/REPLACE
|
|
56
|
+
blocks; `whole` replaces full files; `udiff` uses unified diffs; `patch` uses
|
|
57
|
+
explicit patch operations. `--edit-format FORMAT` overrides the default.
|
|
58
|
+
Choose a format your model follows reliably. Use `/ask` for discussion without
|
|
59
|
+
edits and `/architect` for a plan followed by a separate editor model.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Troubleshooting
|
|
2
|
+
|
|
3
|
+
Start with `python -m patch --help` and `/help`. For installation/import problems
|
|
4
|
+
see [installation](install.md#installation-problems); for credentials and provider
|
|
5
|
+
errors see [models](models.md#api-keys). Report reproducible problems in
|
|
6
|
+
[Patch issues](https://github.com/PierrunoYT/patch/issues), omitting credentials,
|
|
7
|
+
private code, and unredacted logs.
|
|
8
|
+
|
|
9
|
+
## Edit errors
|
|
10
|
+
|
|
11
|
+
An edit error means Patch could not apply the model's requested format or match
|
|
12
|
+
its source text. Patch normally sends feedback to the model so it can retry.
|
|
13
|
+
If failures repeat, reduce the request, add the correct files, and use a model
|
|
14
|
+
with reliable editing support. Check [edit formats](models.md#edit-formats).
|
|
15
|
+
Review the diff: a failed response may still have applied some edits.
|
|
16
|
+
|
|
17
|
+
## Token limits
|
|
18
|
+
|
|
19
|
+
Use `/tokens` to inspect context usage. `/drop` unnecessary files and `/clear`
|
|
20
|
+
old chat history; ask for smaller changes. Large files, pasted output, and long
|
|
21
|
+
conversations consume input context. Output limits can truncate edits, so review
|
|
22
|
+
files and tests rather than assuming a finished response made complete changes.
|
|
23
|
+
Check [model metadata](models.md#model-warnings) if the reported limits look wrong.
|
patch/docs/usage.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Using Patch
|
|
2
|
+
|
|
3
|
+
Patch is an AI pair-programming tool for your terminal, forked from Aider.
|
|
4
|
+
Start in your project directory with `python -m patch file1.py file2.py`, then
|
|
5
|
+
describe the change you want. Review the edits and run your tests.
|
|
6
|
+
See [installation](install.md), [models](models.md), [configuration](config.md),
|
|
7
|
+
[Git integration](git.md), and [troubleshooting](troubleshooting.md).
|
|
8
|
+
|
|
9
|
+
## Adding files
|
|
10
|
+
|
|
11
|
+
Use `/add path/to/file` to share editable files, `/read path/to/file` for read-only
|
|
12
|
+
context, `/ls` to list files, and `/drop path/to/file` to remove files from chat.
|
|
13
|
+
Only add files needed for the task: adding everything costs tokens and can confuse
|
|
14
|
+
the model. The repository map supplies related context without adding every file.
|
|
15
|
+
Use `/tokens` to inspect context usage.
|
|
16
|
+
|
|
17
|
+
## Large repositories
|
|
18
|
+
|
|
19
|
+
Start Patch in the repository and add only the files you expect to edit.
|
|
20
|
+
Use `.patchignore` with Git-ignore patterns to exclude irrelevant files from the
|
|
21
|
+
repository map and file discovery. Adjust `--map-tokens` to control map size;
|
|
22
|
+
`--map-tokens 0` disables the map. `/map` shows it and `/map-refresh` refreshes it.
|
|
23
|
+
|
|
24
|
+
## Commands and modes
|
|
25
|
+
|
|
26
|
+
- `/help` lists commands without installing extras. `/help <question>` retrieves
|
|
27
|
+
these packaged docs and asks your configured model for an answer; it requires
|
|
28
|
+
the [help extra](install.md#interactive-help) and may incur model API charges.
|
|
29
|
+
- `/ask <question>` discusses code without editing; `/code <request>` requests edits.
|
|
30
|
+
- `/architect <request>` uses a planning model followed by an editor model.
|
|
31
|
+
`/chat-mode` changes the mode for subsequent messages.
|
|
32
|
+
- `/model <name>` changes the main model; `/models <query>` searches model names.
|
|
33
|
+
- `/diff` reviews the last changes; `/undo` undoes the last eligible Patch commit.
|
|
34
|
+
- `/run <command>` runs a shell command and offers to add its output to chat.
|
|
35
|
+
- `/test <command>` runs tests and offers to fix failures; `/lint` runs configured linters.
|
|
36
|
+
- `/clear` clears chat history; `/reset` also drops files; `/exit` ends the session.
|
|
37
|
+
|
|
38
|
+
## More context
|
|
39
|
+
|
|
40
|
+
Use `/web <URL>` to read a page, `/add` for a supported image, and `/voice` for
|
|
41
|
+
microphone transcription. Voice requires an audio device and transcription provider
|
|
42
|
+
credentials. Use `/paste` for clipboard content and `/editor` to write a prompt
|
|
43
|
+
in your editor. `/multiline-mode` toggles multiline input.
|
|
44
|
+
|
|
45
|
+
`--watch-files` reacts to AI comment markers in source files. Only enable it in
|
|
46
|
+
trusted projects: other local processes that write files can trigger model turns.
|
|
47
|
+
The experimental `--browser` UI requires the [browser extra](install.md#browser-ui).
|
patch/dump.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import traceback
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def cvt(s):
|
|
6
|
+
if isinstance(s, str):
|
|
7
|
+
return s
|
|
8
|
+
try:
|
|
9
|
+
return json.dumps(s, indent=4)
|
|
10
|
+
except TypeError:
|
|
11
|
+
return str(s)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def dump(*vals):
|
|
15
|
+
# http://docs.python.org/library/traceback.html
|
|
16
|
+
stack = traceback.extract_stack()
|
|
17
|
+
vars = stack[-2][3]
|
|
18
|
+
|
|
19
|
+
# strip away the call to dump()
|
|
20
|
+
vars = "(".join(vars.split("(")[1:])
|
|
21
|
+
vars = ")".join(vars.split(")")[:-1])
|
|
22
|
+
|
|
23
|
+
vals = [cvt(v) for v in vals]
|
|
24
|
+
has_newline = sum(1 for v in vals if "\n" in v)
|
|
25
|
+
if has_newline:
|
|
26
|
+
print("%s:" % vars)
|
|
27
|
+
print(", ".join(vals))
|
|
28
|
+
else:
|
|
29
|
+
print("%s:" % vars, ", ".join(vals))
|