search-replace-py 0.0.1__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.
@@ -0,0 +1,29 @@
1
+ name: Linting
2
+
3
+ on:
4
+ push:
5
+ branches: ["master"]
6
+ pull_request:
7
+ branches: ["master"]
8
+
9
+ jobs:
10
+ lint:
11
+ runs-on: ubuntu-latest
12
+
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - name: Install uv
17
+ uses: astral-sh/setup-uv@v7
18
+ with:
19
+ enable-cache: true
20
+
21
+ - name: Set up Python
22
+ run: uv python install 3.14
23
+
24
+ - name: Install dependencies
25
+ run: uv sync
26
+
27
+ - name: Run linter
28
+ shell: bash
29
+ run: uv run -- make lint
@@ -0,0 +1,29 @@
1
+ name: Unit Testing
2
+
3
+ on:
4
+ push:
5
+ branches: ["master"]
6
+ pull_request:
7
+ branches: ["master"]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - name: Install uv
17
+ uses: astral-sh/setup-uv@v7
18
+ with:
19
+ enable-cache: true
20
+
21
+ - name: Set up Python
22
+ run: uv python install 3.14
23
+
24
+ - name: Install dependencies
25
+ run: uv sync
26
+
27
+ - name: Run tests
28
+ shell: bash
29
+ run: uv run -- make test
@@ -0,0 +1,11 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+ aider-main/
@@ -0,0 +1 @@
1
+ 3.14
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 marcin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,37 @@
1
+ .PHONY: init install format lint test build publish clean
2
+
3
+ PYTHON ?= uv run
4
+
5
+ DIST_DIR := dist
6
+
7
+ init:
8
+ @command -v uv >/dev/null 2>&1 || { echo >&2 "Error: uv is not installed."; exit 1; }
9
+
10
+ install: init
11
+ @uv sync
12
+
13
+ format: init
14
+ @$(PYTHON) ruff check search_replace tests --fix
15
+ @$(PYTHON) black search_replace tests
16
+
17
+ lint: init
18
+ @$(PYTHON) ruff check search_replace tests
19
+ @$(PYTHON) black --check search_replace tests
20
+ @$(PYTHON) mypy search_replace
21
+
22
+ test: init
23
+ @$(PYTHON) pytest
24
+
25
+ # Preferred build (uv)
26
+ build: init
27
+ @rm -rf $(DIST_DIR)
28
+ @uv build
29
+ @ls -la $(DIST_DIR)
30
+
31
+ # Publish to PyPI (requires UV_PUBLISH_TOKEN)
32
+ publish: build
33
+ @test -n "$$UV_PUBLISH_TOKEN" || { echo >&2 "Error: UV_PUBLISH_TOKEN is not set"; exit 1; }
34
+ @uv publish
35
+
36
+ clean:
37
+ @rm -rf $(DIST_DIR) .pytest_cache .ruff_cache .mypy_cache
@@ -0,0 +1,234 @@
1
+ Metadata-Version: 2.4
2
+ Name: search-replace-py
3
+ Version: 0.0.1
4
+ Summary: Parse and apply Aider-style SEARCH/REPLACE patch blocks to files
5
+ Project-URL: Homepage, https://github.com/marcius-llmus/search-replace-py
6
+ Project-URL: Repository, https://github.com/marcius-llmus/search-replace-py
7
+ Project-URL: Issues, https://github.com/marcius-llmus/search-replace-py/issues
8
+ Project-URL: Changelog, https://github.com/marcius-llmus/search-replace-py/releases
9
+ Author: marcin
10
+ License: MIT License
11
+
12
+ Copyright (c) 2026 marcin
13
+
14
+ Permission is hereby granted, free of charge, to any person obtaining a copy
15
+ of this software and associated documentation files (the "Software"), to deal
16
+ in the Software without restriction, including without limitation the rights
17
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18
+ copies of the Software, and to permit persons to whom the Software is
19
+ furnished to do so, subject to the following conditions:
20
+
21
+ The above copyright notice and this permission notice shall be included in all
22
+ copies or substantial portions of the Software.
23
+
24
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30
+ SOFTWARE.
31
+ License-File: LICENSE
32
+ Keywords: aider,diff,editblock,llm,patch,replace,search,tooling
33
+ Classifier: Development Status :: 3 - Alpha
34
+ Classifier: Intended Audience :: Developers
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Programming Language :: Python :: 3
37
+ Classifier: Programming Language :: Python :: 3 :: Only
38
+ Classifier: Programming Language :: Python :: 3.14
39
+ Classifier: Topic :: Software Development :: Build Tools
40
+ Classifier: Topic :: Software Development :: Version Control
41
+ Classifier: Typing :: Typed
42
+ Requires-Python: >=3.14
43
+ Description-Content-Type: text/markdown
44
+
45
+ # search-replace-py
46
+
47
+ A standalone Python library for parsing and applying SEARCH/REPLACE patch blocks, extracted from [Aider's](https://github.com/Aider-AI/aider) editblock engine.
48
+
49
+ Use it to give any LLM the ability to propose and apply precise code changes using the battle-tested editblock format.
50
+
51
+ ---
52
+
53
+ ## How it works
54
+
55
+ The editblock format is Aider's primary mechanism for LLM-driven code editing. The LLM is prompted to output changes as structured `SEARCH/REPLACE` blocks:
56
+
57
+ ````
58
+ ```python
59
+ mathweb/flask/app.py
60
+ <<<<<<< SEARCH
61
+ from flask import Flask
62
+ =======
63
+ import math
64
+ from flask import Flask
65
+ >>>>>>> REPLACE
66
+ ```
67
+ ````
68
+
69
+ This library provides:
70
+
71
+ 1. **`render_system_prompt()`** — returns the rendered system prompt string to instruct the LLM.
72
+ 2. **`get_example_messages()`** — returns a `FewShotExampleMessages` named tuple with four plain strings (two user + two assistant turns) to prepend to the conversation history.
73
+ 3. **`apply_diff(llm_response, root)`** — parses the LLM's response and applies all blocks to disk in one call.
74
+
75
+ ---
76
+
77
+ ## What is included
78
+
79
+ - Block parsing (`<<<<<<< SEARCH`, `=======`, `>>>>>>> REPLACE`) with filename discovery and fuzzy filename resolution.
80
+ - Three replacement strategies:
81
+ - exact match
82
+ - leading-whitespace-tolerant match
83
+ - dotdotdot (`...`) segmented replacement
84
+ - Typed errors (`ParseError`, `ApplyError`) for clean error handling in retry loops.
85
+
86
+ ---
87
+
88
+ ## Installation
89
+
90
+ ```bash
91
+ pip install search-replace-py
92
+ # or with uv
93
+ uv add search-replace-py
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Quick start
99
+
100
+ ```python
101
+ from pathlib import Path
102
+ from search_replace import render_system_prompt, get_example_messages, apply_diff
103
+
104
+ # 1. Build the system prompt — plain string, append your own context if needed
105
+ system_prompt = render_system_prompt()
106
+
107
+ # 2. Build the messages list; prepend few-shot examples before the real request
108
+ ex = get_example_messages()
109
+ messages = [{"role": "system", "content": system_prompt}]
110
+ messages += [
111
+ {"role": "user", "content": ex.first_user_message},
112
+ {"role": "assistant", "content": ex.first_assistant_message},
113
+ {"role": "user", "content": ex.second_user_message},
114
+ {"role": "assistant", "content": ex.second_assistant_message},
115
+ ]
116
+ messages.append({"role": "user", "content": "Add a docstring to the greet() function in hello.py"})
117
+
118
+ # 3. Send to your LLM and get a response string
119
+ llm_response = "..."
120
+
121
+ # 4. Parse and apply in one call
122
+ apply_diff(llm_response, root=Path("."))
123
+ ```
124
+
125
+ ---
126
+
127
+ ## Integration with Pydantic AI
128
+
129
+ [Pydantic AI](https://ai.pydantic.dev) accepts a string for `instructions` and a list of `ModelMessage` objects for `message_history`.
130
+
131
+ ```python
132
+ from pathlib import Path
133
+
134
+ from pydantic_ai import Agent, ModelRequest, ModelResponse, TextPart, UserPromptPart
135
+ from search_replace import render_system_prompt, get_example_messages, apply_diff
136
+
137
+ ex = get_example_messages()
138
+
139
+ few_shot = [
140
+ ModelRequest(parts=[UserPromptPart(content=ex.first_user_message)]),
141
+ ModelResponse(parts=[TextPart(content=ex.first_assistant_message)]),
142
+ ModelRequest(parts=[UserPromptPart(content=ex.second_user_message)]),
143
+ ModelResponse(parts=[TextPart(content=ex.second_assistant_message)]),
144
+ ]
145
+
146
+ agent = Agent("openai:gpt-5.2", instructions=render_system_prompt())
147
+
148
+ auth_py = Path("auth.py").read_text()
149
+ result = agent.run_sync(
150
+ f"Refactor the login function in auth.py to use bcrypt.\n\nauth.py\n```python\n{auth_py}\n```",
151
+ message_history=few_shot,
152
+ )
153
+
154
+ apply_diff(result.output, root=Path("."))
155
+ ```
156
+
157
+ ### With dry-run validation before writing
158
+
159
+ ```python
160
+ from search_replace import parse_edit_blocks, apply_edits
161
+ from search_replace.errors import ApplyError
162
+
163
+ blocks = parse_edit_blocks(result.output)
164
+
165
+ # Validate all blocks match before touching disk.
166
+ # Without dry_run, blocks that match are written immediately — a later failure
167
+ # would leave files partially patched with no rollback.
168
+ try:
169
+ apply_edits(blocks.edits, root=Path("."), dry_run=True)
170
+ except ApplyError as e:
171
+ # feed the error back to the LLM for a retry
172
+ print(f"Patch would not apply: {e}")
173
+ else:
174
+ apply_edits(blocks.edits, root=Path("."))
175
+ ```
176
+
177
+ ---
178
+
179
+ ## Public API
180
+
181
+ ```python
182
+ from search_replace import (
183
+ # Prompt
184
+ render_system_prompt,
185
+ get_example_messages, # returns FewShotExampleMessages
186
+ FewShotExampleMessages, # NamedTuple: first/second_user/assistant_message
187
+ render_prompt, # render a single template string
188
+ EditBlockFencedPrompts, # raw class with main_system, system_reminder, example_messages
189
+
190
+ # Parse + apply (convenience)
191
+ apply_diff,
192
+
193
+ # Parsing
194
+ parse_edit_blocks,
195
+ find_original_update_blocks,
196
+ EditBlock,
197
+
198
+ # Applying
199
+ apply_edits, # pass dry_run=True to validate without writing
200
+
201
+ # Errors
202
+ ParseError,
203
+ ApplyError,
204
+ MissingFilenameError,
205
+ )
206
+ ```
207
+
208
+ ---
209
+
210
+ ## Tests and validation
211
+
212
+ - `tests/test_parser.py` — block parsing, filename resolution, edge cases
213
+ - `tests/test_apply.py` — replacement strategies, whitespace tolerance, new-file creation
214
+ - `tests/test_prompts.py` — `render_system_prompt` and `get_example_messages` output
215
+ - `tests/test_parity_harness.py` — byte-for-byte comparison against Aider's reference output on the real 100K-line `chat-history.md` fixture
216
+
217
+ ```bash
218
+ uv run python -m pytest tests/
219
+ ```
220
+
221
+ ---
222
+
223
+ ## Credits
224
+
225
+ The parsing engine, replacement strategies, and prompt templates in this library are derived from [Aider](https://github.com/Aider-AI/aider), created by [Paul Gauthier](https://github.com/paul-gauthier). Aider is an outstanding AI pair-programming tool — this library simply extracts and packages its editblock mechanism so it can be reused in other applications. All credit for the original design and implementation goes to him.
226
+
227
+ ---
228
+
229
+ ## Extraction notes
230
+
231
+ - Extracted from `aider/coders/editblock_coder.py` and the fenced editblock prompt module.
232
+ - Runtime coupling to Aider's coder/model lifecycle is fully removed.
233
+ - Error message contracts for malformed blocks and failed apply paths are preserved to maintain LLM retry-loop compatibility.
234
+ - `replace_closest_edit_distance()` remains defined but inactive, preserving the original behaviour of the early return in `replace_most_similar_chunk()`.
@@ -0,0 +1,190 @@
1
+ # search-replace-py
2
+
3
+ A standalone Python library for parsing and applying SEARCH/REPLACE patch blocks, extracted from [Aider's](https://github.com/Aider-AI/aider) editblock engine.
4
+
5
+ Use it to give any LLM the ability to propose and apply precise code changes using the battle-tested editblock format.
6
+
7
+ ---
8
+
9
+ ## How it works
10
+
11
+ The editblock format is Aider's primary mechanism for LLM-driven code editing. The LLM is prompted to output changes as structured `SEARCH/REPLACE` blocks:
12
+
13
+ ````
14
+ ```python
15
+ mathweb/flask/app.py
16
+ <<<<<<< SEARCH
17
+ from flask import Flask
18
+ =======
19
+ import math
20
+ from flask import Flask
21
+ >>>>>>> REPLACE
22
+ ```
23
+ ````
24
+
25
+ This library provides:
26
+
27
+ 1. **`render_system_prompt()`** — returns the rendered system prompt string to instruct the LLM.
28
+ 2. **`get_example_messages()`** — returns a `FewShotExampleMessages` named tuple with four plain strings (two user + two assistant turns) to prepend to the conversation history.
29
+ 3. **`apply_diff(llm_response, root)`** — parses the LLM's response and applies all blocks to disk in one call.
30
+
31
+ ---
32
+
33
+ ## What is included
34
+
35
+ - Block parsing (`<<<<<<< SEARCH`, `=======`, `>>>>>>> REPLACE`) with filename discovery and fuzzy filename resolution.
36
+ - Three replacement strategies:
37
+ - exact match
38
+ - leading-whitespace-tolerant match
39
+ - dotdotdot (`...`) segmented replacement
40
+ - Typed errors (`ParseError`, `ApplyError`) for clean error handling in retry loops.
41
+
42
+ ---
43
+
44
+ ## Installation
45
+
46
+ ```bash
47
+ pip install search-replace-py
48
+ # or with uv
49
+ uv add search-replace-py
50
+ ```
51
+
52
+ ---
53
+
54
+ ## Quick start
55
+
56
+ ```python
57
+ from pathlib import Path
58
+ from search_replace import render_system_prompt, get_example_messages, apply_diff
59
+
60
+ # 1. Build the system prompt — plain string, append your own context if needed
61
+ system_prompt = render_system_prompt()
62
+
63
+ # 2. Build the messages list; prepend few-shot examples before the real request
64
+ ex = get_example_messages()
65
+ messages = [{"role": "system", "content": system_prompt}]
66
+ messages += [
67
+ {"role": "user", "content": ex.first_user_message},
68
+ {"role": "assistant", "content": ex.first_assistant_message},
69
+ {"role": "user", "content": ex.second_user_message},
70
+ {"role": "assistant", "content": ex.second_assistant_message},
71
+ ]
72
+ messages.append({"role": "user", "content": "Add a docstring to the greet() function in hello.py"})
73
+
74
+ # 3. Send to your LLM and get a response string
75
+ llm_response = "..."
76
+
77
+ # 4. Parse and apply in one call
78
+ apply_diff(llm_response, root=Path("."))
79
+ ```
80
+
81
+ ---
82
+
83
+ ## Integration with Pydantic AI
84
+
85
+ [Pydantic AI](https://ai.pydantic.dev) accepts a string for `instructions` and a list of `ModelMessage` objects for `message_history`.
86
+
87
+ ```python
88
+ from pathlib import Path
89
+
90
+ from pydantic_ai import Agent, ModelRequest, ModelResponse, TextPart, UserPromptPart
91
+ from search_replace import render_system_prompt, get_example_messages, apply_diff
92
+
93
+ ex = get_example_messages()
94
+
95
+ few_shot = [
96
+ ModelRequest(parts=[UserPromptPart(content=ex.first_user_message)]),
97
+ ModelResponse(parts=[TextPart(content=ex.first_assistant_message)]),
98
+ ModelRequest(parts=[UserPromptPart(content=ex.second_user_message)]),
99
+ ModelResponse(parts=[TextPart(content=ex.second_assistant_message)]),
100
+ ]
101
+
102
+ agent = Agent("openai:gpt-5.2", instructions=render_system_prompt())
103
+
104
+ auth_py = Path("auth.py").read_text()
105
+ result = agent.run_sync(
106
+ f"Refactor the login function in auth.py to use bcrypt.\n\nauth.py\n```python\n{auth_py}\n```",
107
+ message_history=few_shot,
108
+ )
109
+
110
+ apply_diff(result.output, root=Path("."))
111
+ ```
112
+
113
+ ### With dry-run validation before writing
114
+
115
+ ```python
116
+ from search_replace import parse_edit_blocks, apply_edits
117
+ from search_replace.errors import ApplyError
118
+
119
+ blocks = parse_edit_blocks(result.output)
120
+
121
+ # Validate all blocks match before touching disk.
122
+ # Without dry_run, blocks that match are written immediately — a later failure
123
+ # would leave files partially patched with no rollback.
124
+ try:
125
+ apply_edits(blocks.edits, root=Path("."), dry_run=True)
126
+ except ApplyError as e:
127
+ # feed the error back to the LLM for a retry
128
+ print(f"Patch would not apply: {e}")
129
+ else:
130
+ apply_edits(blocks.edits, root=Path("."))
131
+ ```
132
+
133
+ ---
134
+
135
+ ## Public API
136
+
137
+ ```python
138
+ from search_replace import (
139
+ # Prompt
140
+ render_system_prompt,
141
+ get_example_messages, # returns FewShotExampleMessages
142
+ FewShotExampleMessages, # NamedTuple: first/second_user/assistant_message
143
+ render_prompt, # render a single template string
144
+ EditBlockFencedPrompts, # raw class with main_system, system_reminder, example_messages
145
+
146
+ # Parse + apply (convenience)
147
+ apply_diff,
148
+
149
+ # Parsing
150
+ parse_edit_blocks,
151
+ find_original_update_blocks,
152
+ EditBlock,
153
+
154
+ # Applying
155
+ apply_edits, # pass dry_run=True to validate without writing
156
+
157
+ # Errors
158
+ ParseError,
159
+ ApplyError,
160
+ MissingFilenameError,
161
+ )
162
+ ```
163
+
164
+ ---
165
+
166
+ ## Tests and validation
167
+
168
+ - `tests/test_parser.py` — block parsing, filename resolution, edge cases
169
+ - `tests/test_apply.py` — replacement strategies, whitespace tolerance, new-file creation
170
+ - `tests/test_prompts.py` — `render_system_prompt` and `get_example_messages` output
171
+ - `tests/test_parity_harness.py` — byte-for-byte comparison against Aider's reference output on the real 100K-line `chat-history.md` fixture
172
+
173
+ ```bash
174
+ uv run python -m pytest tests/
175
+ ```
176
+
177
+ ---
178
+
179
+ ## Credits
180
+
181
+ The parsing engine, replacement strategies, and prompt templates in this library are derived from [Aider](https://github.com/Aider-AI/aider), created by [Paul Gauthier](https://github.com/paul-gauthier). Aider is an outstanding AI pair-programming tool — this library simply extracts and packages its editblock mechanism so it can be reused in other applications. All credit for the original design and implementation goes to him.
182
+
183
+ ---
184
+
185
+ ## Extraction notes
186
+
187
+ - Extracted from `aider/coders/editblock_coder.py` and the fenced editblock prompt module.
188
+ - Runtime coupling to Aider's coder/model lifecycle is fully removed.
189
+ - Error message contracts for malformed blocks and failed apply paths are preserved to maintain LLM retry-loop compatibility.
190
+ - `replace_closest_edit_distance()` remains defined but inactive, preserving the original behaviour of the early return in `replace_most_similar_chunk()`.
@@ -0,0 +1,6 @@
1
+ def main():
2
+ print("Hello from search-replace-py!")
3
+
4
+
5
+ if __name__ == "__main__":
6
+ main()
@@ -0,0 +1,51 @@
1
+ [project]
2
+ name = "search-replace-py"
3
+ version = "0.0.1"
4
+ description = "Parse and apply Aider-style SEARCH/REPLACE patch blocks to files"
5
+ readme = "README.md"
6
+ requires-python = ">=3.14"
7
+ dependencies = []
8
+
9
+ license = { file = "LICENSE" }
10
+ authors = [{ name = "marcin" }]
11
+ keywords = ["search", "replace", "patch", "diff", "editblock", "aider", "llm", "tooling"]
12
+ classifiers = [
13
+ "Development Status :: 3 - Alpha",
14
+ "Intended Audience :: Developers",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3 :: Only",
18
+ "Programming Language :: Python :: 3.14",
19
+ "Topic :: Software Development :: Build Tools",
20
+ "Topic :: Software Development :: Version Control",
21
+ "Typing :: Typed",
22
+ ]
23
+
24
+ [project.urls]
25
+ Homepage = "https://github.com/marcius-llmus/search-replace-py"
26
+ Repository = "https://github.com/marcius-llmus/search-replace-py"
27
+ Issues = "https://github.com/marcius-llmus/search-replace-py/issues"
28
+ Changelog = "https://github.com/marcius-llmus/search-replace-py/releases"
29
+
30
+ [dependency-groups]
31
+ dev = [
32
+ "pytest>=9.0.2",
33
+ "ruff>=0.2.2,<1.0.0",
34
+ "black>=25.12.0",
35
+ "mypy>=1.8.0,<2.0.0",
36
+ "build>=1.0.0",
37
+ "twine>=5.0.0",
38
+ ]
39
+
40
+ [tool.uv]
41
+ default-groups = ["dev"]
42
+
43
+ [build-system]
44
+ requires = ["hatchling"]
45
+ build-backend = "hatchling.build"
46
+
47
+ [tool.hatch.build.targets.wheel]
48
+ packages = ["search_replace"]
49
+
50
+ [tool.pytest.ini_options]
51
+ testpaths = ["tests"]
@@ -0,0 +1,38 @@
1
+ from .apply import apply_diff, apply_edits
2
+ from .errors import (
3
+ ApplyError,
4
+ MissingFilenameError,
5
+ ParseError,
6
+ PathEscapeError,
7
+ SearchReplaceError,
8
+ )
9
+ from .parser import all_fences, find_original_update_blocks, parse_edit_blocks
10
+ from .prompts import (
11
+ EditBlockFencedPrompts,
12
+ FewShotExampleMessages,
13
+ get_example_messages,
14
+ render_system_prompt,
15
+ )
16
+ from .types import ApplyResult, DEFAULT_FENCE, EditBlock, Fence, ParseResult
17
+
18
+ __all__ = [
19
+ "ApplyError",
20
+ "ApplyResult",
21
+ "apply_diff",
22
+ "apply_edits",
23
+ "all_fences",
24
+ "DEFAULT_FENCE",
25
+ "EditBlock",
26
+ "EditBlockFencedPrompts",
27
+ "Fence",
28
+ "FewShotExampleMessages",
29
+ "find_original_update_blocks",
30
+ "get_example_messages",
31
+ "MissingFilenameError",
32
+ "parse_edit_blocks",
33
+ "ParseError",
34
+ "PathEscapeError",
35
+ "ParseResult",
36
+ "render_system_prompt",
37
+ "SearchReplaceError",
38
+ ]