patch-chat 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.
Files changed (146) hide show
  1. patch/__init__.py +20 -0
  2. patch/__main__.py +4 -0
  3. patch/_version.py +24 -0
  4. patch/analytics.py +260 -0
  5. patch/args.py +955 -0
  6. patch/args_formatter.py +228 -0
  7. patch/coders/__init__.py +32 -0
  8. patch/coders/architect_coder.py +47 -0
  9. patch/coders/architect_prompts.py +40 -0
  10. patch/coders/ask_coder.py +9 -0
  11. patch/coders/ask_prompts.py +41 -0
  12. patch/coders/base_coder.py +2548 -0
  13. patch/coders/base_prompts.py +60 -0
  14. patch/coders/chat_chunks.py +64 -0
  15. patch/coders/context_coder.py +53 -0
  16. patch/coders/context_prompts.py +75 -0
  17. patch/coders/edit_apply.py +6 -0
  18. patch/coders/editblock_coder.py +620 -0
  19. patch/coders/editblock_fenced_coder.py +10 -0
  20. patch/coders/editblock_fenced_prompts.py +143 -0
  21. patch/coders/editblock_func_coder.py +141 -0
  22. patch/coders/editblock_func_prompts.py +27 -0
  23. patch/coders/editblock_prompts.py +172 -0
  24. patch/coders/editor_diff_fenced_coder.py +9 -0
  25. patch/coders/editor_diff_fenced_prompts.py +11 -0
  26. patch/coders/editor_editblock_coder.py +8 -0
  27. patch/coders/editor_editblock_prompts.py +18 -0
  28. patch/coders/editor_whole_coder.py +8 -0
  29. patch/coders/editor_whole_prompts.py +10 -0
  30. patch/coders/patch_coder.py +731 -0
  31. patch/coders/patch_prompts.py +159 -0
  32. patch/coders/search_replace.py +757 -0
  33. patch/coders/shell.py +37 -0
  34. patch/coders/single_wholefile_func_coder.py +102 -0
  35. patch/coders/single_wholefile_func_prompts.py +27 -0
  36. patch/coders/udiff_coder.py +419 -0
  37. patch/coders/udiff_prompts.py +113 -0
  38. patch/coders/udiff_simple.py +14 -0
  39. patch/coders/udiff_simple_prompts.py +25 -0
  40. patch/coders/wholefile_coder.py +144 -0
  41. patch/coders/wholefile_func_coder.py +134 -0
  42. patch/coders/wholefile_func_prompts.py +27 -0
  43. patch/coders/wholefile_prompts.py +64 -0
  44. patch/commands.py +1668 -0
  45. patch/copypaste.py +70 -0
  46. patch/deprecated.py +126 -0
  47. patch/diffs.py +128 -0
  48. patch/dump.py +29 -0
  49. patch/editor.py +147 -0
  50. patch/exceptions.py +113 -0
  51. patch/format_settings.py +47 -0
  52. patch/gui.py +545 -0
  53. patch/history.py +143 -0
  54. patch/io.py +1209 -0
  55. patch/linter.py +304 -0
  56. patch/llm.py +47 -0
  57. patch/main.py +1466 -0
  58. patch/mdstream.py +243 -0
  59. patch/models.py +1342 -0
  60. patch/onboarding.py +428 -0
  61. patch/openrouter.py +128 -0
  62. patch/prompts.py +61 -0
  63. patch/queries/tree-sitter-language-pack/README.md +7 -0
  64. patch/queries/tree-sitter-language-pack/arduino-tags.scm +5 -0
  65. patch/queries/tree-sitter-language-pack/bash-tags.scm +8 -0
  66. patch/queries/tree-sitter-language-pack/c-tags.scm +9 -0
  67. patch/queries/tree-sitter-language-pack/chatito-tags.scm +16 -0
  68. patch/queries/tree-sitter-language-pack/clojure-tags.scm +7 -0
  69. patch/queries/tree-sitter-language-pack/commonlisp-tags.scm +122 -0
  70. patch/queries/tree-sitter-language-pack/cpp-tags.scm +15 -0
  71. patch/queries/tree-sitter-language-pack/csharp-tags.scm +26 -0
  72. patch/queries/tree-sitter-language-pack/d-tags.scm +26 -0
  73. patch/queries/tree-sitter-language-pack/dart-tags.scm +92 -0
  74. patch/queries/tree-sitter-language-pack/elisp-tags.scm +5 -0
  75. patch/queries/tree-sitter-language-pack/elixir-tags.scm +54 -0
  76. patch/queries/tree-sitter-language-pack/elm-tags.scm +19 -0
  77. patch/queries/tree-sitter-language-pack/gleam-tags.scm +41 -0
  78. patch/queries/tree-sitter-language-pack/go-tags.scm +42 -0
  79. patch/queries/tree-sitter-language-pack/java-tags.scm +20 -0
  80. patch/queries/tree-sitter-language-pack/javascript-tags.scm +88 -0
  81. patch/queries/tree-sitter-language-pack/lua-tags.scm +34 -0
  82. patch/queries/tree-sitter-language-pack/matlab-tags.scm +10 -0
  83. patch/queries/tree-sitter-language-pack/ocaml-tags.scm +115 -0
  84. patch/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +98 -0
  85. patch/queries/tree-sitter-language-pack/pony-tags.scm +39 -0
  86. patch/queries/tree-sitter-language-pack/properties-tags.scm +5 -0
  87. patch/queries/tree-sitter-language-pack/python-tags.scm +14 -0
  88. patch/queries/tree-sitter-language-pack/r-tags.scm +21 -0
  89. patch/queries/tree-sitter-language-pack/racket-tags.scm +12 -0
  90. patch/queries/tree-sitter-language-pack/ruby-tags.scm +64 -0
  91. patch/queries/tree-sitter-language-pack/rust-tags.scm +60 -0
  92. patch/queries/tree-sitter-language-pack/solidity-tags.scm +43 -0
  93. patch/queries/tree-sitter-language-pack/swift-tags.scm +51 -0
  94. patch/queries/tree-sitter-language-pack/udev-tags.scm +20 -0
  95. patch/queries/tree-sitter-languages/README.md +24 -0
  96. patch/queries/tree-sitter-languages/bash-tags.scm +8 -0
  97. patch/queries/tree-sitter-languages/c-tags.scm +9 -0
  98. patch/queries/tree-sitter-languages/c_sharp-tags.scm +46 -0
  99. patch/queries/tree-sitter-languages/cpp-tags.scm +15 -0
  100. patch/queries/tree-sitter-languages/dart-tags.scm +91 -0
  101. patch/queries/tree-sitter-languages/elisp-tags.scm +8 -0
  102. patch/queries/tree-sitter-languages/elixir-tags.scm +54 -0
  103. patch/queries/tree-sitter-languages/elm-tags.scm +19 -0
  104. patch/queries/tree-sitter-languages/fortran-tags.scm +15 -0
  105. patch/queries/tree-sitter-languages/go-tags.scm +30 -0
  106. patch/queries/tree-sitter-languages/haskell-tags.scm +3 -0
  107. patch/queries/tree-sitter-languages/hcl-tags.scm +77 -0
  108. patch/queries/tree-sitter-languages/java-tags.scm +20 -0
  109. patch/queries/tree-sitter-languages/javascript-tags.scm +88 -0
  110. patch/queries/tree-sitter-languages/julia-tags.scm +60 -0
  111. patch/queries/tree-sitter-languages/kotlin-tags.scm +27 -0
  112. patch/queries/tree-sitter-languages/matlab-tags.scm +10 -0
  113. patch/queries/tree-sitter-languages/ocaml-tags.scm +115 -0
  114. patch/queries/tree-sitter-languages/ocaml_interface-tags.scm +98 -0
  115. patch/queries/tree-sitter-languages/php-tags.scm +26 -0
  116. patch/queries/tree-sitter-languages/python-tags.scm +12 -0
  117. patch/queries/tree-sitter-languages/ql-tags.scm +26 -0
  118. patch/queries/tree-sitter-languages/ruby-tags.scm +64 -0
  119. patch/queries/tree-sitter-languages/rust-tags.scm +60 -0
  120. patch/queries/tree-sitter-languages/scala-tags.scm +65 -0
  121. patch/queries/tree-sitter-languages/typescript-tags.scm +41 -0
  122. patch/queries/tree-sitter-languages/zig-tags.scm +3 -0
  123. patch/reasoning_tags.py +82 -0
  124. patch/repo.py +612 -0
  125. patch/repomap.py +859 -0
  126. patch/report.py +200 -0
  127. patch/resources/__init__.py +3 -0
  128. patch/resources/model-metadata.json +715 -0
  129. patch/resources/model-settings.yml +3128 -0
  130. patch/run_cmd.py +146 -0
  131. patch/scrape.py +316 -0
  132. patch/sendchat.py +61 -0
  133. patch/special.py +203 -0
  134. patch/urls.py +17 -0
  135. patch/utils.py +365 -0
  136. patch/versioncheck.py +113 -0
  137. patch/voice.py +200 -0
  138. patch/waiting.py +222 -0
  139. patch/watch.py +318 -0
  140. patch/watch_prompts.py +12 -0
  141. patch_chat-0.1.0.dist-info/METADATA +375 -0
  142. patch_chat-0.1.0.dist-info/RECORD +146 -0
  143. patch_chat-0.1.0.dist-info/WHEEL +5 -0
  144. patch_chat-0.1.0.dist-info/entry_points.txt +2 -0
  145. patch_chat-0.1.0.dist-info/licenses/LICENSE.txt +202 -0
  146. patch_chat-0.1.0.dist-info/top_level.txt +1 -0
patch/__init__.py ADDED
@@ -0,0 +1,20 @@
1
+ from packaging import version
2
+
3
+ __version__ = "0.1.0.dev"
4
+ safe_version = __version__
5
+
6
+ try:
7
+ from patch._version import __version__
8
+ except Exception:
9
+ __version__ = safe_version + "+import"
10
+
11
+ if type(__version__) is not str:
12
+ __version__ = safe_version + "+type"
13
+ else:
14
+ try:
15
+ if version.parse(__version__) < version.parse(safe_version):
16
+ __version__ = safe_version + "+less"
17
+ except Exception:
18
+ __version__ = safe_version + "+parse"
19
+
20
+ __all__ = [__version__]
patch/__main__.py ADDED
@@ -0,0 +1,4 @@
1
+ from .main import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
patch/_version.py ADDED
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '0.1.0'
22
+ __version_tuple__ = version_tuple = (0, 1, 0)
23
+
24
+ __commit_id__ = commit_id = 'ge5b97a66a'
patch/analytics.py ADDED
@@ -0,0 +1,260 @@
1
+ import json
2
+ import platform
3
+ import sys
4
+ import time
5
+ import uuid
6
+ from pathlib import Path
7
+
8
+ from mixpanel import MixpanelException
9
+ from posthog import Posthog
10
+
11
+ from patch import __version__
12
+ from patch.dump import dump # noqa: F401
13
+ from patch.models import model_info_manager
14
+
15
+ PERCENT = 10
16
+
17
+
18
+ def compute_hex_threshold(percent):
19
+ """Convert percentage to 6-digit hex threshold.
20
+
21
+ Args:
22
+ percent: Percentage threshold (0-100)
23
+
24
+ Returns:
25
+ str: 6-digit hex threshold
26
+ """
27
+ return format(int(0xFFFFFF * percent / 100), "06x")
28
+
29
+
30
+ def is_uuid_in_percentage(uuid_str, percent):
31
+ """Check if a UUID string falls within the first X percent of the UUID space.
32
+
33
+ Args:
34
+ uuid_str: UUID string to test
35
+ percent: Percentage threshold (0-100)
36
+
37
+ Returns:
38
+ bool: True if UUID falls within the first X percent
39
+ """
40
+ if not (0 <= percent <= 100):
41
+ raise ValueError("Percentage must be between 0 and 100")
42
+
43
+ if not uuid_str:
44
+ return False
45
+
46
+ # Convert percentage to hex threshold (1% = "04...", 10% = "1a...", etc)
47
+ # Using first 6 hex digits
48
+ if percent == 0:
49
+ return False
50
+
51
+ threshold = compute_hex_threshold(percent)
52
+ return uuid_str[:6] <= threshold
53
+
54
+
55
+ mixpanel_project_token = "6da9a43058a5d1b9f3353153921fb04d"
56
+ posthog_project_api_key = "phc_99T7muzafUMMZX15H8XePbMSreEUzahHbtWjy3l5Qbv"
57
+ posthog_host = "https://us.i.posthog.com"
58
+
59
+
60
+ class Analytics:
61
+ # providers
62
+ mp = None
63
+ ph = None
64
+
65
+ # saved
66
+ user_id = None
67
+ permanently_disable = None
68
+ asked_opt_in = None
69
+
70
+ # ephemeral
71
+ logfile = None
72
+
73
+ def __init__(
74
+ self,
75
+ logfile=None,
76
+ permanently_disable=False,
77
+ posthog_host=None,
78
+ posthog_project_api_key=None,
79
+ ):
80
+ self.mp = None
81
+ self.ph = None
82
+ self.logfile = logfile
83
+ self.get_or_create_uuid()
84
+ self.custom_posthog_host = posthog_host
85
+ self.custom_posthog_project_api_key = posthog_project_api_key
86
+
87
+ if self.permanently_disable or permanently_disable or not self.asked_opt_in:
88
+ self.disable(permanently_disable)
89
+
90
+ def enable(self):
91
+ if not self.user_id:
92
+ self.disable(False)
93
+ return
94
+
95
+ if self.permanently_disable:
96
+ self.disable(True)
97
+ return
98
+
99
+ if not self.asked_opt_in:
100
+ self.disable(False)
101
+ return
102
+
103
+ # self.mp = Mixpanel(mixpanel_project_token)
104
+ self.ph = Posthog(
105
+ project_api_key=self.custom_posthog_project_api_key or posthog_project_api_key,
106
+ host=self.custom_posthog_host or posthog_host,
107
+ on_error=self.posthog_error,
108
+ enable_exception_autocapture=False,
109
+ super_properties=self.get_system_info(), # Add system info to all events
110
+ )
111
+
112
+ def disable(self, permanently):
113
+ self.mp = None
114
+ self.ph = None
115
+
116
+ if permanently:
117
+ self.asked_opt_in = True
118
+ self.permanently_disable = True
119
+ self.save_data()
120
+
121
+ def need_to_ask(self, args_analytics):
122
+ if args_analytics is False:
123
+ return False
124
+
125
+ could_ask = not self.asked_opt_in and not self.permanently_disable
126
+ if not could_ask:
127
+ return False
128
+
129
+ if args_analytics is True:
130
+ return True
131
+
132
+ assert args_analytics is None, args_analytics
133
+
134
+ if not self.user_id:
135
+ return False
136
+
137
+ return is_uuid_in_percentage(self.user_id, PERCENT)
138
+
139
+ def get_data_file_path(self):
140
+ try:
141
+ data_file = Path.home() / ".patch" / "analytics.json"
142
+ data_file.parent.mkdir(parents=True, exist_ok=True)
143
+ return data_file
144
+ except OSError:
145
+ # If we can't create/access the directory, just disable analytics
146
+ self.disable(permanently=False)
147
+ return None
148
+
149
+ def get_or_create_uuid(self):
150
+ self.load_data()
151
+ if self.user_id:
152
+ return
153
+
154
+ self.user_id = str(uuid.uuid4())
155
+ self.save_data()
156
+
157
+ def load_data(self):
158
+ data_file = self.get_data_file_path()
159
+ if not data_file:
160
+ return
161
+
162
+ if data_file.exists():
163
+ try:
164
+ data = json.loads(data_file.read_text())
165
+ self.permanently_disable = data.get("permanently_disable")
166
+ self.user_id = data.get("uuid")
167
+ self.asked_opt_in = data.get("asked_opt_in", False)
168
+ except (json.decoder.JSONDecodeError, OSError):
169
+ self.disable(permanently=False)
170
+
171
+ def save_data(self):
172
+ data_file = self.get_data_file_path()
173
+ if not data_file:
174
+ return
175
+
176
+ data = dict(
177
+ uuid=self.user_id,
178
+ permanently_disable=self.permanently_disable,
179
+ asked_opt_in=self.asked_opt_in,
180
+ )
181
+
182
+ try:
183
+ data_file.write_text(json.dumps(data, indent=4))
184
+ except OSError:
185
+ # If we can't write the file, just disable analytics
186
+ self.disable(permanently=False)
187
+
188
+ def get_system_info(self):
189
+ return {
190
+ "python_version": sys.version.split()[0],
191
+ "os_platform": platform.system(),
192
+ "os_release": platform.release(),
193
+ "machine": platform.machine(),
194
+ "patch_version": __version__,
195
+ }
196
+
197
+ def _redact_model_name(self, model):
198
+ if not model:
199
+ return None
200
+
201
+ info = model_info_manager.get_model_from_cached_json_db(model.name)
202
+ if info:
203
+ return model.name
204
+ elif "/" in model.name:
205
+ return model.name.split("/")[0] + "/REDACTED"
206
+ return None
207
+
208
+ def posthog_error(self, error, batch=None):
209
+ """disable posthog if we get an error"""
210
+ # https://github.com/PostHog/posthog-python/blob/9e1bb8c58afaa229da24c4fb576c08bb88a75752/posthog/consumer.py#L86
211
+ # https://github.com/PierrunoYT/patch/issues/2532
212
+ self.ph = None
213
+
214
+ def event(self, event_name, main_model=None, **kwargs):
215
+ if not self.mp and not self.ph and not self.logfile:
216
+ return
217
+
218
+ properties = {}
219
+
220
+ if main_model:
221
+ properties["main_model"] = self._redact_model_name(main_model)
222
+ properties["weak_model"] = self._redact_model_name(main_model.weak_model)
223
+ properties["editor_model"] = self._redact_model_name(main_model.editor_model)
224
+
225
+ properties.update(kwargs)
226
+
227
+ # Handle numeric values
228
+ for key, value in properties.items():
229
+ if isinstance(value, (int, float)):
230
+ properties[key] = value
231
+ else:
232
+ properties[key] = str(value)
233
+
234
+ if self.mp:
235
+ try:
236
+ self.mp.track(self.user_id, event_name, dict(properties))
237
+ except MixpanelException:
238
+ self.mp = None # Disable mixpanel on connection errors
239
+
240
+ ph = self.ph
241
+ if ph:
242
+ ph.capture(event_name, distinct_id=self.user_id, properties=dict(properties))
243
+
244
+ if self.logfile:
245
+ log_entry = {
246
+ "event": event_name,
247
+ "properties": properties,
248
+ "user_id": self.user_id,
249
+ "time": int(time.time()),
250
+ }
251
+ try:
252
+ with open(self.logfile, "a") as f:
253
+ json.dump(log_entry, f)
254
+ f.write("\n")
255
+ except OSError:
256
+ pass # Ignore OS errors when writing to logfile
257
+
258
+
259
+ if __name__ == "__main__":
260
+ dump(compute_hex_threshold(PERCENT))