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.
Files changed (153) hide show
  1. patch/__init__.py +3 -0
  2. patch/__main__.py +4 -0
  3. patch/analytics.py +268 -0
  4. patch/args.py +949 -0
  5. patch/args_formatter.py +227 -0
  6. patch/coders/__init__.py +34 -0
  7. patch/coders/architect_coder.py +48 -0
  8. patch/coders/architect_prompts.py +40 -0
  9. patch/coders/ask_coder.py +9 -0
  10. patch/coders/ask_prompts.py +41 -0
  11. patch/coders/base_coder.py +2485 -0
  12. patch/coders/base_prompts.py +60 -0
  13. patch/coders/chat_chunks.py +64 -0
  14. patch/coders/context_coder.py +53 -0
  15. patch/coders/context_prompts.py +75 -0
  16. patch/coders/editblock_coder.py +657 -0
  17. patch/coders/editblock_fenced_coder.py +10 -0
  18. patch/coders/editblock_fenced_prompts.py +143 -0
  19. patch/coders/editblock_func_coder.py +141 -0
  20. patch/coders/editblock_func_prompts.py +27 -0
  21. patch/coders/editblock_prompts.py +172 -0
  22. patch/coders/editor_diff_fenced_coder.py +9 -0
  23. patch/coders/editor_diff_fenced_prompts.py +11 -0
  24. patch/coders/editor_editblock_coder.py +8 -0
  25. patch/coders/editor_editblock_prompts.py +18 -0
  26. patch/coders/editor_whole_coder.py +8 -0
  27. patch/coders/editor_whole_prompts.py +10 -0
  28. patch/coders/help_coder.py +16 -0
  29. patch/coders/help_prompts.py +46 -0
  30. patch/coders/patch_coder.py +706 -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 +429 -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 +1712 -0
  45. patch/copypaste.py +72 -0
  46. patch/deprecated.py +126 -0
  47. patch/diffs.py +128 -0
  48. patch/docs/__init__.py +1 -0
  49. patch/docs/analytics.md +28 -0
  50. patch/docs/config.md +43 -0
  51. patch/docs/git.md +22 -0
  52. patch/docs/install.md +54 -0
  53. patch/docs/models.md +59 -0
  54. patch/docs/troubleshooting.md +23 -0
  55. patch/docs/usage.md +47 -0
  56. patch/dump.py +29 -0
  57. patch/editor.py +147 -0
  58. patch/exceptions.py +113 -0
  59. patch/format_settings.py +26 -0
  60. patch/gui.py +545 -0
  61. patch/help.py +118 -0
  62. patch/history.py +143 -0
  63. patch/io.py +1191 -0
  64. patch/linter.py +304 -0
  65. patch/llm.py +47 -0
  66. patch/main.py +1274 -0
  67. patch/mdstream.py +243 -0
  68. patch/models.py +1338 -0
  69. patch/onboarding.py +428 -0
  70. patch/openrouter.py +128 -0
  71. patch/prompts.py +61 -0
  72. patch/queries/tree-sitter-language-pack/arduino-tags.scm +5 -0
  73. patch/queries/tree-sitter-language-pack/bash-tags.scm +8 -0
  74. patch/queries/tree-sitter-language-pack/c-tags.scm +9 -0
  75. patch/queries/tree-sitter-language-pack/chatito-tags.scm +16 -0
  76. patch/queries/tree-sitter-language-pack/clojure-tags.scm +7 -0
  77. patch/queries/tree-sitter-language-pack/commonlisp-tags.scm +122 -0
  78. patch/queries/tree-sitter-language-pack/cpp-tags.scm +15 -0
  79. patch/queries/tree-sitter-language-pack/csharp-tags.scm +26 -0
  80. patch/queries/tree-sitter-language-pack/d-tags.scm +26 -0
  81. patch/queries/tree-sitter-language-pack/dart-tags.scm +92 -0
  82. patch/queries/tree-sitter-language-pack/elisp-tags.scm +5 -0
  83. patch/queries/tree-sitter-language-pack/elixir-tags.scm +54 -0
  84. patch/queries/tree-sitter-language-pack/elm-tags.scm +19 -0
  85. patch/queries/tree-sitter-language-pack/gleam-tags.scm +41 -0
  86. patch/queries/tree-sitter-language-pack/go-tags.scm +42 -0
  87. patch/queries/tree-sitter-language-pack/java-tags.scm +20 -0
  88. patch/queries/tree-sitter-language-pack/javascript-tags.scm +88 -0
  89. patch/queries/tree-sitter-language-pack/lua-tags.scm +34 -0
  90. patch/queries/tree-sitter-language-pack/matlab-tags.scm +10 -0
  91. patch/queries/tree-sitter-language-pack/ocaml-tags.scm +115 -0
  92. patch/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +98 -0
  93. patch/queries/tree-sitter-language-pack/pony-tags.scm +39 -0
  94. patch/queries/tree-sitter-language-pack/properties-tags.scm +5 -0
  95. patch/queries/tree-sitter-language-pack/python-tags.scm +14 -0
  96. patch/queries/tree-sitter-language-pack/r-tags.scm +21 -0
  97. patch/queries/tree-sitter-language-pack/racket-tags.scm +12 -0
  98. patch/queries/tree-sitter-language-pack/ruby-tags.scm +64 -0
  99. patch/queries/tree-sitter-language-pack/rust-tags.scm +60 -0
  100. patch/queries/tree-sitter-language-pack/solidity-tags.scm +43 -0
  101. patch/queries/tree-sitter-language-pack/swift-tags.scm +51 -0
  102. patch/queries/tree-sitter-language-pack/udev-tags.scm +20 -0
  103. patch/queries/tree-sitter-languages/bash-tags.scm +8 -0
  104. patch/queries/tree-sitter-languages/c-tags.scm +9 -0
  105. patch/queries/tree-sitter-languages/c_sharp-tags.scm +46 -0
  106. patch/queries/tree-sitter-languages/cpp-tags.scm +15 -0
  107. patch/queries/tree-sitter-languages/dart-tags.scm +91 -0
  108. patch/queries/tree-sitter-languages/elisp-tags.scm +8 -0
  109. patch/queries/tree-sitter-languages/elixir-tags.scm +54 -0
  110. patch/queries/tree-sitter-languages/elm-tags.scm +19 -0
  111. patch/queries/tree-sitter-languages/fortran-tags.scm +15 -0
  112. patch/queries/tree-sitter-languages/go-tags.scm +30 -0
  113. patch/queries/tree-sitter-languages/haskell-tags.scm +3 -0
  114. patch/queries/tree-sitter-languages/hcl-tags.scm +77 -0
  115. patch/queries/tree-sitter-languages/java-tags.scm +20 -0
  116. patch/queries/tree-sitter-languages/javascript-tags.scm +88 -0
  117. patch/queries/tree-sitter-languages/julia-tags.scm +60 -0
  118. patch/queries/tree-sitter-languages/kotlin-tags.scm +27 -0
  119. patch/queries/tree-sitter-languages/matlab-tags.scm +10 -0
  120. patch/queries/tree-sitter-languages/ocaml-tags.scm +115 -0
  121. patch/queries/tree-sitter-languages/ocaml_interface-tags.scm +98 -0
  122. patch/queries/tree-sitter-languages/php-tags.scm +26 -0
  123. patch/queries/tree-sitter-languages/python-tags.scm +12 -0
  124. patch/queries/tree-sitter-languages/ql-tags.scm +26 -0
  125. patch/queries/tree-sitter-languages/ruby-tags.scm +64 -0
  126. patch/queries/tree-sitter-languages/rust-tags.scm +60 -0
  127. patch/queries/tree-sitter-languages/scala-tags.scm +65 -0
  128. patch/queries/tree-sitter-languages/typescript-tags.scm +41 -0
  129. patch/queries/tree-sitter-languages/zig-tags.scm +3 -0
  130. patch/reasoning_tags.py +82 -0
  131. patch/repo.py +622 -0
  132. patch/repomap.py +867 -0
  133. patch/report.py +200 -0
  134. patch/resources/__init__.py +3 -0
  135. patch/resources/model-metadata.json +715 -0
  136. patch/resources/model-settings.yml +3128 -0
  137. patch/run_cmd.py +132 -0
  138. patch/scrape.py +284 -0
  139. patch/sendchat.py +61 -0
  140. patch/special.py +203 -0
  141. patch/urls.py +17 -0
  142. patch/utils.py +348 -0
  143. patch/versioncheck.py +130 -0
  144. patch/voice.py +187 -0
  145. patch/waiting.py +221 -0
  146. patch/watch.py +318 -0
  147. patch/watch_prompts.py +12 -0
  148. patch_code-0.1.0.dist-info/METADATA +467 -0
  149. patch_code-0.1.0.dist-info/RECORD +153 -0
  150. patch_code-0.1.0.dist-info/WHEEL +5 -0
  151. patch_code-0.1.0.dist-info/entry_points.txt +2 -0
  152. patch_code-0.1.0.dist-info/licenses/LICENSE.txt +202 -0
  153. patch_code-0.1.0.dist-info/top_level.txt +1 -0
patch/editor.py ADDED
@@ -0,0 +1,147 @@
1
+ """
2
+ Editor module for handling system text editor interactions.
3
+
4
+ This module provides functionality to:
5
+ - Discover and launch the system's configured text editor
6
+ - Create and manage temporary files for editing
7
+ - Handle editor preferences from environment variables
8
+ - Support cross-platform editor operations
9
+ """
10
+
11
+ import os
12
+ import platform
13
+ import subprocess
14
+ import tempfile
15
+
16
+ from rich.console import Console
17
+
18
+ from patch.dump import dump # noqa
19
+
20
+ DEFAULT_EDITOR_NIX = "vi"
21
+ DEFAULT_EDITOR_OS_X = "vim"
22
+ DEFAULT_EDITOR_WINDOWS = "notepad"
23
+
24
+ console = Console()
25
+
26
+
27
+ def print_status_message(success, message, style=None):
28
+ """
29
+ Print a status message with appropriate styling.
30
+
31
+ :param success: Whether the operation was successful
32
+ :param message: The message to display
33
+ :param style: Optional style override. If None, uses green for success and red for failure
34
+ """
35
+ if style is None:
36
+ style = "bold green" if success else "bold red"
37
+ console.print(message, style=style)
38
+ print("")
39
+
40
+
41
+ def write_temp_file(
42
+ input_data="",
43
+ suffix=None,
44
+ prefix=None,
45
+ dir=None,
46
+ ):
47
+ """
48
+ Create a temporary file with the given input data.
49
+
50
+ :param input_data: Content to write to the temporary file
51
+ :param suffix: Optional file extension (without the dot)
52
+ :param prefix: Optional prefix for the temporary filename
53
+ :param dir: Optional directory to create the file in
54
+ :return: Path to the created temporary file
55
+ :raises: OSError if file creation or writing fails
56
+ """
57
+ kwargs = {"prefix": prefix, "dir": dir}
58
+ if suffix:
59
+ kwargs["suffix"] = f".{suffix}"
60
+ fd, filepath = tempfile.mkstemp(**kwargs)
61
+ try:
62
+ with os.fdopen(fd, "w") as f:
63
+ f.write(input_data)
64
+ except Exception:
65
+ os.close(fd)
66
+ raise
67
+ return filepath
68
+
69
+
70
+ def get_environment_editor(default=None):
71
+ """
72
+ Fetches the preferred editor from the environment variables.
73
+
74
+ This function checks the following environment variables in order to
75
+ determine the user's preferred editor:
76
+
77
+ - VISUAL
78
+ - EDITOR
79
+
80
+ :param default: The default editor to return if no environment variable is set.
81
+ :type default: str or None
82
+ :return: The preferred editor as specified by environment variables or the default value.
83
+ :rtype: str or None
84
+ """
85
+ editor = os.environ.get("VISUAL", os.environ.get("EDITOR", default))
86
+ return editor
87
+
88
+
89
+ def discover_editor(editor_override=None):
90
+ """
91
+ Discovers and returns the appropriate editor command.
92
+
93
+ Handles cases where the editor command includes arguments, including quoted arguments
94
+ with spaces (e.g. 'vim -c "set noswapfile"').
95
+
96
+ :return: The editor command as a string
97
+ :rtype: str
98
+ """
99
+ system = platform.system()
100
+ if system == "Windows":
101
+ default_editor = DEFAULT_EDITOR_WINDOWS
102
+ elif system == "Darwin":
103
+ default_editor = DEFAULT_EDITOR_OS_X
104
+ else:
105
+ default_editor = DEFAULT_EDITOR_NIX
106
+
107
+ if editor_override:
108
+ editor = editor_override
109
+ else:
110
+ editor = get_environment_editor(default_editor)
111
+
112
+ return editor
113
+
114
+
115
+ def pipe_editor(input_data="", suffix=None, editor=None):
116
+ """
117
+ Opens the system editor with optional input data and returns the edited content.
118
+
119
+ This function creates a temporary file with the provided input data, opens it in
120
+ the system editor, waits for the user to make changes and close the editor, then
121
+ reads and returns the modified content. The temporary file is deleted afterwards.
122
+
123
+ :param input_data: Initial content to populate the editor with
124
+ :type input_data: str
125
+ :param suffix: Optional file extension for the temporary file (e.g. '.txt', '.md')
126
+ :type suffix: str or None
127
+ :return: The edited content after the editor is closed
128
+ :rtype: str
129
+ """
130
+ filepath = write_temp_file(input_data, suffix)
131
+ command_str = discover_editor(editor)
132
+ command_str += " " + filepath
133
+
134
+ subprocess.call(command_str, shell=True)
135
+ with open(filepath, "r") as f:
136
+ output_data = f.read()
137
+ try:
138
+ os.remove(filepath)
139
+ except PermissionError:
140
+ print_status_message(
141
+ False,
142
+ (
143
+ f"WARNING: Unable to delete temporary file {filepath!r}. You may need to delete it"
144
+ " manually."
145
+ ),
146
+ )
147
+ return output_data
patch/exceptions.py ADDED
@@ -0,0 +1,113 @@
1
+ from dataclasses import dataclass
2
+
3
+ from patch.dump import dump # noqa: F401
4
+
5
+
6
+ @dataclass
7
+ class ExInfo:
8
+ name: str
9
+ retry: bool
10
+ description: str
11
+
12
+
13
+ EXCEPTIONS = [
14
+ ExInfo("APIConnectionError", True, None),
15
+ ExInfo("APIError", True, None),
16
+ ExInfo("APIResponseValidationError", True, None),
17
+ ExInfo(
18
+ "AuthenticationError",
19
+ False,
20
+ "The API provider is not able to authenticate you. Check your API key.",
21
+ ),
22
+ ExInfo("AzureOpenAIError", True, None),
23
+ ExInfo("BadGatewayError", True, "The API provider's servers are down or overloaded."),
24
+ ExInfo("BadRequestError", False, None),
25
+ ExInfo("BudgetExceededError", True, None),
26
+ ExInfo(
27
+ "ContentPolicyViolationError",
28
+ True,
29
+ "The API provider has refused the request due to a safety policy about the content.",
30
+ ),
31
+ ExInfo("ContextWindowExceededError", False, None), # special case handled in base_coder
32
+ ExInfo("ImageFetchError", False, "The API provider was unable to fetch one or more images."),
33
+ ExInfo("InternalServerError", True, "The API provider's servers are down or overloaded."),
34
+ ExInfo("InvalidRequestError", True, None),
35
+ ExInfo("JSONSchemaValidationError", True, None),
36
+ ExInfo("NotFoundError", False, None),
37
+ ExInfo(
38
+ "PermissionDeniedError",
39
+ False,
40
+ "Permission was denied. Check your API key and/or credentials.",
41
+ ),
42
+ ExInfo("OpenAIError", True, None),
43
+ ExInfo(
44
+ "RateLimitError",
45
+ True,
46
+ "The API provider has rate limited you. Try again later or check your quotas.",
47
+ ),
48
+ ExInfo("RouterRateLimitError", True, None),
49
+ ExInfo("ServiceUnavailableError", True, "The API provider's servers are down or overloaded."),
50
+ ExInfo("UnprocessableEntityError", True, None),
51
+ ExInfo("UnsupportedParamsError", True, None),
52
+ ExInfo(
53
+ "Timeout",
54
+ True,
55
+ "The API provider timed out without returning a response. They may be down or overloaded.",
56
+ ),
57
+ ]
58
+
59
+
60
+ class LiteLLMExceptions:
61
+ exceptions = dict()
62
+ exception_info = {exi.name: exi for exi in EXCEPTIONS}
63
+
64
+ def __init__(self):
65
+ self._load()
66
+
67
+ def _load(self, strict=False):
68
+ import litellm
69
+
70
+ for var in dir(litellm):
71
+ # Filter by BaseException because instances of non-exception classes cannot be caught.
72
+ # `litellm.ErrorEventError` is an example of a regular class which just happens to end
73
+ # with `Error`.
74
+ if var.endswith("Error") and issubclass(getattr(litellm, var), BaseException):
75
+ if var not in self.exception_info:
76
+ raise ValueError(f"{var} is in litellm but not in patch's exceptions list")
77
+
78
+ for var in self.exception_info:
79
+ ex = getattr(litellm, var)
80
+ self.exceptions[ex] = self.exception_info[var]
81
+
82
+ def exceptions_tuple(self):
83
+ return tuple(self.exceptions)
84
+
85
+ def get_ex_info(self, ex):
86
+ """Return the ExInfo for a given exception instance"""
87
+ import litellm
88
+
89
+ if ex.__class__ is litellm.APIConnectionError:
90
+ if "boto3" in str(ex):
91
+ return ExInfo("APIConnectionError", False, "You need to: pip install boto3")
92
+ if "OpenrouterException" in str(ex) and "'choices'" in str(ex):
93
+ return ExInfo(
94
+ "APIConnectionError",
95
+ True,
96
+ (
97
+ "OpenRouter or the upstream API provider is down, overloaded or rate"
98
+ " limiting your requests."
99
+ ),
100
+ )
101
+
102
+ # Check for specific non-retryable APIError cases like insufficient credits
103
+ if ex.__class__ is litellm.APIError:
104
+ err_str = str(ex).lower()
105
+ if "insufficient credits" in err_str and '"code":402' in err_str:
106
+ return ExInfo(
107
+ "APIError",
108
+ False,
109
+ "Insufficient credits with the API provider. Please add credits.",
110
+ )
111
+ # Fall through to default APIError handling if not the specific credits error
112
+
113
+ return self.exceptions.get(ex.__class__, ExInfo(None, None, None))
@@ -0,0 +1,26 @@
1
+ def scrub_sensitive_info(args, text):
2
+ # Replace sensitive information with last 4 characters
3
+ if text and args.openai_api_key:
4
+ last_4 = args.openai_api_key[-4:]
5
+ text = text.replace(args.openai_api_key, f"...{last_4}")
6
+ if text and args.anthropic_api_key:
7
+ last_4 = args.anthropic_api_key[-4:]
8
+ text = text.replace(args.anthropic_api_key, f"...{last_4}")
9
+ return text
10
+
11
+
12
+ def format_settings(parser, args):
13
+ show = scrub_sensitive_info(args, parser.format_values())
14
+ # clean up the headings for consistency w/ new lines
15
+ heading_env = "Environment Variables:"
16
+ heading_defaults = "Defaults:"
17
+ if heading_env in show:
18
+ show = show.replace(heading_env, "\n" + heading_env)
19
+ show = show.replace(heading_defaults, "\n" + heading_defaults)
20
+ show += "\n"
21
+ show += "Option settings:\n"
22
+ for arg, val in sorted(vars(args).items()):
23
+ if val:
24
+ val = scrub_sensitive_info(args, str(val))
25
+ show += f" - {arg}: {val}\n" # noqa: E221
26
+ return show