aider-ce 0.88.20__py3-none-any.whl → 0.88.38__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 (113) hide show
  1. aider/__init__.py +1 -1
  2. aider/_version.py +2 -2
  3. aider/args.py +63 -43
  4. aider/coders/agent_coder.py +331 -79
  5. aider/coders/agent_prompts.py +3 -15
  6. aider/coders/architect_coder.py +21 -5
  7. aider/coders/base_coder.py +661 -413
  8. aider/coders/base_prompts.py +6 -3
  9. aider/coders/chat_chunks.py +39 -17
  10. aider/commands.py +79 -15
  11. aider/diffs.py +10 -9
  12. aider/exceptions.py +1 -1
  13. aider/helpers/coroutines.py +8 -0
  14. aider/helpers/requests.py +45 -0
  15. aider/history.py +5 -0
  16. aider/io.py +179 -25
  17. aider/main.py +86 -35
  18. aider/models.py +16 -8
  19. aider/queries/tree-sitter-language-pack/c-tags.scm +3 -0
  20. aider/queries/tree-sitter-language-pack/clojure-tags.scm +5 -0
  21. aider/queries/tree-sitter-language-pack/commonlisp-tags.scm +5 -0
  22. aider/queries/tree-sitter-language-pack/cpp-tags.scm +3 -0
  23. aider/queries/tree-sitter-language-pack/csharp-tags.scm +6 -0
  24. aider/queries/tree-sitter-language-pack/dart-tags.scm +5 -0
  25. aider/queries/tree-sitter-language-pack/elixir-tags.scm +5 -0
  26. aider/queries/tree-sitter-language-pack/elm-tags.scm +3 -0
  27. aider/queries/tree-sitter-language-pack/go-tags.scm +7 -0
  28. aider/queries/tree-sitter-language-pack/java-tags.scm +6 -0
  29. aider/queries/tree-sitter-language-pack/javascript-tags.scm +8 -0
  30. aider/queries/tree-sitter-language-pack/lua-tags.scm +5 -0
  31. aider/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +3 -0
  32. aider/queries/tree-sitter-language-pack/python-tags.scm +10 -0
  33. aider/queries/tree-sitter-language-pack/r-tags.scm +6 -0
  34. aider/queries/tree-sitter-language-pack/ruby-tags.scm +5 -0
  35. aider/queries/tree-sitter-language-pack/rust-tags.scm +3 -0
  36. aider/queries/tree-sitter-language-pack/solidity-tags.scm +1 -1
  37. aider/queries/tree-sitter-language-pack/swift-tags.scm +4 -1
  38. aider/queries/tree-sitter-languages/c-tags.scm +3 -0
  39. aider/queries/tree-sitter-languages/c_sharp-tags.scm +6 -0
  40. aider/queries/tree-sitter-languages/cpp-tags.scm +3 -0
  41. aider/queries/tree-sitter-languages/dart-tags.scm +2 -1
  42. aider/queries/tree-sitter-languages/elixir-tags.scm +5 -0
  43. aider/queries/tree-sitter-languages/elm-tags.scm +3 -0
  44. aider/queries/tree-sitter-languages/fortran-tags.scm +3 -0
  45. aider/queries/tree-sitter-languages/go-tags.scm +6 -0
  46. aider/queries/tree-sitter-languages/haskell-tags.scm +2 -0
  47. aider/queries/tree-sitter-languages/java-tags.scm +6 -0
  48. aider/queries/tree-sitter-languages/javascript-tags.scm +8 -0
  49. aider/queries/tree-sitter-languages/julia-tags.scm +2 -2
  50. aider/queries/tree-sitter-languages/kotlin-tags.scm +3 -0
  51. aider/queries/tree-sitter-languages/ocaml_interface-tags.scm +6 -0
  52. aider/queries/tree-sitter-languages/php-tags.scm +6 -0
  53. aider/queries/tree-sitter-languages/python-tags.scm +10 -0
  54. aider/queries/tree-sitter-languages/ruby-tags.scm +5 -0
  55. aider/queries/tree-sitter-languages/rust-tags.scm +3 -0
  56. aider/queries/tree-sitter-languages/scala-tags.scm +2 -3
  57. aider/queries/tree-sitter-languages/typescript-tags.scm +3 -0
  58. aider/queries/tree-sitter-languages/zig-tags.scm +20 -3
  59. aider/repomap.py +71 -11
  60. aider/resources/model-metadata.json +27335 -635
  61. aider/resources/model-settings.yml +190 -0
  62. aider/scrape.py +2 -0
  63. aider/tools/__init__.py +2 -0
  64. aider/tools/command.py +84 -94
  65. aider/tools/command_interactive.py +95 -110
  66. aider/tools/delete_block.py +131 -159
  67. aider/tools/delete_line.py +97 -132
  68. aider/tools/delete_lines.py +120 -160
  69. aider/tools/extract_lines.py +288 -312
  70. aider/tools/finished.py +30 -43
  71. aider/tools/git_branch.py +107 -109
  72. aider/tools/git_diff.py +44 -56
  73. aider/tools/git_log.py +39 -53
  74. aider/tools/git_remote.py +37 -51
  75. aider/tools/git_show.py +33 -47
  76. aider/tools/git_status.py +30 -44
  77. aider/tools/grep.py +214 -242
  78. aider/tools/indent_lines.py +175 -201
  79. aider/tools/insert_block.py +220 -253
  80. aider/tools/list_changes.py +65 -80
  81. aider/tools/ls.py +64 -80
  82. aider/tools/make_editable.py +57 -73
  83. aider/tools/make_readonly.py +50 -66
  84. aider/tools/remove.py +64 -80
  85. aider/tools/replace_all.py +96 -109
  86. aider/tools/replace_line.py +118 -156
  87. aider/tools/replace_lines.py +160 -197
  88. aider/tools/replace_text.py +159 -160
  89. aider/tools/show_numbered_context.py +115 -141
  90. aider/tools/thinking.py +52 -0
  91. aider/tools/undo_change.py +78 -91
  92. aider/tools/update_todo_list.py +130 -138
  93. aider/tools/utils/base_tool.py +64 -0
  94. aider/tools/utils/output.py +118 -0
  95. aider/tools/view.py +38 -54
  96. aider/tools/view_files_matching.py +131 -134
  97. aider/tools/view_files_with_symbol.py +108 -120
  98. aider/urls.py +1 -1
  99. aider/versioncheck.py +4 -3
  100. aider/website/docs/config/adv-model-settings.md +237 -0
  101. aider/website/docs/config/agent-mode.md +36 -3
  102. aider/website/docs/config/model-aliases.md +2 -1
  103. aider/website/docs/faq.md +6 -11
  104. aider/website/docs/languages.md +2 -2
  105. aider/website/docs/more/infinite-output.md +27 -0
  106. {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/METADATA +112 -70
  107. {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/RECORD +112 -107
  108. aider_ce-0.88.38.dist-info/entry_points.txt +6 -0
  109. aider_ce-0.88.20.dist-info/entry_points.txt +0 -2
  110. /aider/tools/{tool_utils.py → utils/helpers.py} +0 -0
  111. {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/WHEEL +0 -0
  112. {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/licenses/LICENSE.txt +0 -0
  113. {aider_ce-0.88.20.dist-info → aider_ce-0.88.38.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,8 @@
1
- from .tool_utils import (
1
+ import difflib
2
+ import json
3
+
4
+ from aider.tools.utils.base_tool import BaseTool
5
+ from aider.tools.utils.helpers import (
2
6
  ToolError,
3
7
  apply_change,
4
8
  format_tool_result,
@@ -6,182 +10,177 @@ from .tool_utils import (
6
10
  handle_tool_error,
7
11
  validate_file_for_edit,
8
12
  )
9
-
10
- schema = {
11
- "type": "function",
12
- "function": {
13
- "name": "ReplaceText",
14
- "description": "Replace text in a file.",
15
- "parameters": {
16
- "type": "object",
17
- "properties": {
18
- "file_path": {"type": "string"},
19
- "find_text": {"type": "string"},
20
- "replace_text": {"type": "string"},
21
- "near_context": {"type": "string"},
22
- "occurrence": {"type": "integer", "default": 1},
23
- "change_id": {"type": "string"},
24
- "dry_run": {"type": "boolean", "default": False},
13
+ from aider.tools.utils.output import color_markers, tool_footer, tool_header
14
+
15
+
16
+ class Tool(BaseTool):
17
+ NORM_NAME = "replacetext"
18
+ SCHEMA = {
19
+ "type": "function",
20
+ "function": {
21
+ "name": "ReplaceText",
22
+ "description": "Replace text in a file.",
23
+ "parameters": {
24
+ "type": "object",
25
+ "properties": {
26
+ "file_path": {"type": "string"},
27
+ "find_text": {"type": "string"},
28
+ "replace_text": {"type": "string"},
29
+ "near_context": {"type": "string"},
30
+ "occurrence": {"type": "integer", "default": 1},
31
+ "change_id": {"type": "string"},
32
+ "dry_run": {"type": "boolean", "default": False},
33
+ },
34
+ "required": ["file_path", "find_text", "replace_text"],
25
35
  },
26
- "required": ["file_path", "find_text", "replace_text"],
27
36
  },
28
- },
29
- }
30
-
31
- # Normalized tool name for lookup
32
- NORM_NAME = "replacetext"
33
-
34
-
35
- def _execute_replace_text(
36
- coder,
37
- file_path,
38
- find_text,
39
- replace_text,
40
- near_context=None,
41
- occurrence=1,
42
- change_id=None,
43
- dry_run=False,
44
- ):
45
- """
46
- Replace specific text with new text, optionally using nearby context for disambiguation.
47
- Uses utility functions for validation, finding occurrences, and applying changes.
48
- """
49
- tool_name = "ReplaceText"
50
- try:
51
- # 1. Validate file and get content
52
- abs_path, rel_path, original_content = validate_file_for_edit(coder, file_path)
53
-
54
- # 2. Find occurrences using helper function
55
- # Note: _find_occurrences is currently on the Coder class, not in tool_utils
56
- occurrences = coder._find_occurrences(original_content, find_text, near_context)
57
-
58
- if not occurrences:
59
- err_msg = f"Text '{find_text}' not found"
60
- if near_context:
61
- err_msg += f" near context '{near_context}'"
62
- err_msg += f" in file '{file_path}'."
63
- raise ToolError(err_msg)
64
-
65
- # 3. Select the occurrence index
66
- num_occurrences = len(occurrences)
37
+ }
38
+
39
+ @classmethod
40
+ def execute(
41
+ cls,
42
+ coder,
43
+ file_path,
44
+ find_text,
45
+ replace_text,
46
+ near_context=None,
47
+ occurrence=1,
48
+ change_id=None,
49
+ dry_run=False,
50
+ ):
51
+ """
52
+ Replace specific text with new text, optionally using nearby context for disambiguation.
53
+ Uses utility functions for validation, finding occurrences, and applying changes.
54
+ """
55
+ tool_name = "ReplaceText"
67
56
  try:
68
- occurrence = int(occurrence)
69
- if occurrence == -1:
70
- if num_occurrences == 0:
71
- raise ToolError(f"Text '{find_text}' not found, cannot select last occurrence.")
72
- target_idx = num_occurrences - 1
73
- elif 1 <= occurrence <= num_occurrences:
74
- target_idx = occurrence - 1 # Convert 1-based to 0-based
75
- else:
76
- err_msg = (
77
- f"Occurrence number {occurrence} is out of range. Found"
78
- f" {num_occurrences} occurrences of '{find_text}'"
79
- )
57
+ # 1. Validate file and get content
58
+ abs_path, rel_path, original_content = validate_file_for_edit(coder, file_path)
59
+
60
+ # 2. Find occurrences using helper function
61
+ # Note: _find_occurrences is currently on the Coder class, not in tool_utils
62
+ occurrences = coder._find_occurrences(original_content, find_text, near_context)
63
+
64
+ if not occurrences:
65
+ err_msg = f"Text '{find_text}' not found"
80
66
  if near_context:
81
- err_msg += f" near '{near_context}'"
82
- err_msg += f" in '{file_path}'."
67
+ err_msg += f" near context '{near_context}'"
68
+ err_msg += f" in file '{file_path}'."
83
69
  raise ToolError(err_msg)
84
- except ValueError:
85
- raise ToolError(f"Invalid occurrence value: '{occurrence}'. Must be an integer.")
86
70
 
87
- start_index = occurrences[target_idx]
71
+ # 3. Select the occurrence index
72
+ num_occurrences = len(occurrences)
73
+ try:
74
+ occurrence = int(occurrence)
75
+ if occurrence == -1:
76
+ if num_occurrences == 0:
77
+ raise ToolError(
78
+ f"Text '{find_text}' not found, cannot select last occurrence."
79
+ )
80
+ target_idx = num_occurrences - 1
81
+ elif 1 <= occurrence <= num_occurrences:
82
+ target_idx = occurrence - 1 # Convert 1-based to 0-based
83
+ else:
84
+ err_msg = (
85
+ f"Occurrence number {occurrence} is out of range. Found"
86
+ f" {num_occurrences} occurrences of '{find_text}'"
87
+ )
88
+ if near_context:
89
+ err_msg += f" near '{near_context}'"
90
+ err_msg += f" in '{file_path}'."
91
+ raise ToolError(err_msg)
92
+ except ValueError:
93
+ raise ToolError(f"Invalid occurrence value: '{occurrence}'. Must be an integer.")
94
+
95
+ start_index = occurrences[target_idx]
96
+
97
+ # 4. Perform the replacement
98
+ new_content = (
99
+ original_content[:start_index]
100
+ + replace_text
101
+ + original_content[start_index + len(find_text) :]
102
+ )
88
103
 
89
- # 4. Perform the replacement
90
- new_content = (
91
- original_content[:start_index]
92
- + replace_text
93
- + original_content[start_index + len(find_text) :]
94
- )
104
+ if original_content == new_content:
105
+ coder.io.tool_warning("No changes made: replacement text is identical to original")
106
+ return "Warning: No changes made (replacement identical to original)"
95
107
 
96
- if original_content == new_content:
97
- coder.io.tool_warning("No changes made: replacement text is identical to original")
98
- return "Warning: No changes made (replacement identical to original)"
108
+ # 5. Generate diff for feedback
109
+ # Note: _generate_diff_snippet is currently on the Coder class
110
+ diff_snippet = generate_unified_diff_snippet(original_content, new_content, rel_path)
111
+ occurrence_str = f"occurrence {occurrence}" if num_occurrences > 1 else "text"
99
112
 
100
- # 5. Generate diff for feedback
101
- # Note: _generate_diff_snippet is currently on the Coder class
102
- diff_snippet = generate_unified_diff_snippet(original_content, new_content, rel_path)
103
- occurrence_str = f"occurrence {occurrence}" if num_occurrences > 1 else "text"
113
+ # 6. Handle dry run
114
+ if dry_run:
115
+ dry_run_message = (
116
+ f"Dry run: Would replace {occurrence_str} of '{find_text}' in {file_path}."
117
+ )
118
+ return format_tool_result(
119
+ coder,
120
+ tool_name,
121
+ "",
122
+ dry_run=True,
123
+ dry_run_message=dry_run_message,
124
+ diff_snippet=diff_snippet,
125
+ )
104
126
 
105
- # 6. Handle dry run
106
- if dry_run:
107
- dry_run_message = (
108
- f"Dry run: Would replace {occurrence_str} of '{find_text}' in {file_path}."
127
+ # 7. Apply Change (Not dry run)
128
+ metadata = {
129
+ "start_index": start_index,
130
+ "find_text": find_text,
131
+ "replace_text": replace_text,
132
+ "near_context": near_context,
133
+ "occurrence": occurrence,
134
+ }
135
+ final_change_id = apply_change(
136
+ coder,
137
+ abs_path,
138
+ rel_path,
139
+ original_content,
140
+ new_content,
141
+ "replacetext",
142
+ metadata,
143
+ change_id,
109
144
  )
145
+
146
+ coder.files_edited_by_tools.add(rel_path)
147
+ # 8. Format and return result
148
+ success_message = f"Replaced {occurrence_str} in {file_path}"
110
149
  return format_tool_result(
111
150
  coder,
112
151
  tool_name,
113
- "",
114
- dry_run=True,
115
- dry_run_message=dry_run_message,
152
+ success_message,
153
+ change_id=final_change_id,
116
154
  diff_snippet=diff_snippet,
117
155
  )
118
156
 
119
- # 7. Apply Change (Not dry run)
120
- metadata = {
121
- "start_index": start_index,
122
- "find_text": find_text,
123
- "replace_text": replace_text,
124
- "near_context": near_context,
125
- "occurrence": occurrence,
126
- }
127
- final_change_id = apply_change(
128
- coder,
129
- abs_path,
130
- rel_path,
131
- original_content,
132
- new_content,
133
- "replacetext",
134
- metadata,
135
- change_id,
157
+ except ToolError as e:
158
+ # Handle errors raised by utility functions or explicitly raised here
159
+ return handle_tool_error(coder, tool_name, e, add_traceback=False)
160
+ except Exception as e:
161
+ # Handle unexpected errors
162
+ return handle_tool_error(coder, tool_name, e)
163
+
164
+ @classmethod
165
+ def format_output(cls, coder, mcp_server, tool_response):
166
+ color_start, color_end = color_markers(coder)
167
+ params = json.loads(tool_response.function.arguments)
168
+ diff = difflib.unified_diff(
169
+ params["find_text"].splitlines(),
170
+ params["replace_text"].splitlines(),
171
+ lineterm="",
172
+ n=float("inf"),
136
173
  )
137
174
 
138
- coder.files_edited_by_tools.add(rel_path)
139
- # 8. Format and return result
140
- success_message = f"Replaced {occurrence_str} in {file_path}"
141
- return format_tool_result(
142
- coder, tool_name, success_message, change_id=final_change_id, diff_snippet=diff_snippet
143
- )
175
+ tool_header(coder=coder, mcp_server=mcp_server, tool_response=tool_response)
144
176
 
145
- except ToolError as e:
146
- # Handle errors raised by utility functions or explicitly raised here
147
- return handle_tool_error(coder, tool_name, e, add_traceback=False)
148
- except Exception as e:
149
- # Handle unexpected errors
150
- return handle_tool_error(coder, tool_name, e)
151
-
152
-
153
- def process_response(coder, params):
154
- """
155
- Process the ReplaceText tool response.
156
-
157
- Args:
158
- coder: The Coder instance
159
- params: Dictionary of parameters
160
-
161
- Returns:
162
- str: Result message
163
- """
164
- file_path = params.get("file_path")
165
- find_text = params.get("find_text")
166
- replace_text = params.get("replace_text")
167
- near_context = params.get("near_context")
168
- occurrence = params.get("occurrence", 1)
169
- change_id = params.get("change_id")
170
- dry_run = params.get("dry_run", False)
171
-
172
- if file_path is not None and find_text is not None and replace_text is not None:
173
- return _execute_replace_text(
174
- coder,
175
- file_path,
176
- find_text,
177
- replace_text,
178
- near_context,
179
- occurrence,
180
- change_id,
181
- dry_run,
182
- )
183
- else:
184
- return (
185
- "Error: Missing required parameters for ReplaceText (file_path,"
186
- " find_text, replace_text)"
187
- )
177
+ coder.io.tool_output("")
178
+ coder.io.tool_output(f"{color_start}file_path:{color_end}")
179
+ coder.io.tool_output(params["file_path"])
180
+ coder.io.tool_output("")
181
+
182
+ coder.io.tool_output(f"{color_start}diff:{color_end}")
183
+ coder.io.tool_output("\n".join(list(diff)[2:]))
184
+ coder.io.tool_output("")
185
+
186
+ tool_footer(coder=coder, tool_response=tool_response)
@@ -1,147 +1,121 @@
1
1
  import os
2
2
 
3
- from .tool_utils import ToolError, handle_tool_error, resolve_paths
4
-
5
- schema = {
6
- "type": "function",
7
- "function": {
8
- "name": "ShowNumberedContext",
9
- "description": "Show numbered lines of context around a pattern or line number.",
10
- "parameters": {
11
- "type": "object",
12
- "properties": {
13
- "file_path": {"type": "string"},
14
- "pattern": {"type": "string"},
15
- "line_number": {"type": "integer"},
16
- "context_lines": {"type": "integer", "default": 3},
3
+ from aider.tools.utils.base_tool import BaseTool
4
+ from aider.tools.utils.helpers import ToolError, handle_tool_error, resolve_paths
5
+
6
+
7
+ class Tool(BaseTool):
8
+ NORM_NAME = "shownumberedcontext"
9
+ SCHEMA = {
10
+ "type": "function",
11
+ "function": {
12
+ "name": "ShowNumberedContext",
13
+ "description": "Show numbered lines of context around a pattern or line number.",
14
+ "parameters": {
15
+ "type": "object",
16
+ "properties": {
17
+ "file_path": {"type": "string"},
18
+ "pattern": {"type": "string"},
19
+ "line_number": {"type": "integer"},
20
+ "context_lines": {"type": "integer", "default": 3},
21
+ },
22
+ "required": ["file_path"],
17
23
  },
18
- "required": ["file_path"],
19
24
  },
20
- },
21
- }
22
-
23
- # Normalized tool name for lookup
24
- NORM_NAME = "shownumberedcontext"
25
-
26
-
27
- def execute_show_numbered_context(
28
- coder, file_path, pattern=None, line_number=None, context_lines=3
29
- ):
30
- """
31
- Displays numbered lines from file_path centered around a target location
32
- (pattern or line_number), without adding the file to context.
33
- Uses utility functions for path resolution and error handling.
34
- """
35
- tool_name = "ShowNumberedContext"
36
- try:
37
- # 1. Validate arguments
38
- if not (pattern is None) ^ (line_number is None):
39
- raise ToolError("Provide exactly one of 'pattern' or 'line_number'.")
40
-
41
- # 2. Resolve path
42
- abs_path, rel_path = resolve_paths(coder, file_path)
43
- if not os.path.exists(abs_path):
44
- # Check existence after resolving, as resolve_paths doesn't guarantee existence
45
- raise ToolError(f"File not found: {file_path}")
46
-
47
- # 3. Read file content
48
- content = coder.io.read_text(abs_path)
49
- if content is None:
50
- raise ToolError(f"Could not read file: {file_path}")
51
- lines = content.splitlines()
52
- num_lines = len(lines)
53
-
54
- # 4. Determine center line index
55
- center_line_idx = -1
56
- found_by = ""
57
-
58
- if line_number is not None:
59
- try:
60
- line_number_int = int(line_number)
61
- if 1 <= line_number_int <= num_lines:
62
- center_line_idx = line_number_int - 1 # Convert to 0-based index
63
- found_by = f"line {line_number_int}"
25
+ }
26
+
27
+ @classmethod
28
+ def execute(cls, coder, file_path, pattern=None, line_number=None, context_lines=3):
29
+ """
30
+ Displays numbered lines from file_path centered around a target location
31
+ (pattern or line_number), without adding the file to context.
32
+ Uses utility functions for path resolution and error handling.
33
+ """
34
+ tool_name = "ShowNumberedContext"
35
+ try:
36
+ # 1. Validate arguments
37
+ if not (pattern is None) ^ (line_number is None):
38
+ raise ToolError("Provide exactly one of 'pattern' or 'line_number'.")
39
+
40
+ # 2. Resolve path
41
+ abs_path, rel_path = resolve_paths(coder, file_path)
42
+ if not os.path.exists(abs_path):
43
+ # Check existence after resolving, as resolve_paths doesn't guarantee existence
44
+ raise ToolError(f"File not found: {file_path}")
45
+
46
+ # 3. Read file content
47
+ content = coder.io.read_text(abs_path)
48
+ if content is None:
49
+ raise ToolError(f"Could not read file: {file_path}")
50
+ lines = content.splitlines()
51
+ num_lines = len(lines)
52
+
53
+ # 4. Determine center line index
54
+ center_line_idx = -1
55
+ found_by = ""
56
+
57
+ if line_number is not None:
58
+ try:
59
+ line_number_int = int(line_number)
60
+ if 1 <= line_number_int <= num_lines:
61
+ center_line_idx = line_number_int - 1 # Convert to 0-based index
62
+ found_by = f"line {line_number_int}"
63
+ else:
64
+ raise ToolError(
65
+ f"Line number {line_number_int} is out of range (1-{num_lines}) for"
66
+ f" {file_path}."
67
+ )
68
+ except ValueError:
69
+ raise ToolError(f"Invalid line number '{line_number}'. Must be an integer.")
70
+
71
+ elif pattern is not None:
72
+ # TODO: Update this section for multiline pattern support later
73
+ first_match_line_idx = -1
74
+ for i, line in enumerate(lines):
75
+ if pattern in line:
76
+ first_match_line_idx = i
77
+ break
78
+
79
+ if first_match_line_idx != -1:
80
+ center_line_idx = first_match_line_idx
81
+ found_by = f"pattern '{pattern}' on line {center_line_idx + 1}"
64
82
  else:
65
- raise ToolError(
66
- f"Line number {line_number_int} is out of range (1-{num_lines}) for"
67
- f" {file_path}."
68
- )
83
+ raise ToolError(f"Pattern '{pattern}' not found in {file_path}.")
84
+
85
+ if center_line_idx == -1:
86
+ # Should not happen if logic above is correct, but as a safeguard
87
+ raise ToolError("Internal error: Could not determine center line.")
88
+
89
+ # 5. Calculate context window
90
+ try:
91
+ context_lines_int = int(context_lines)
92
+ if context_lines_int < 0:
93
+ raise ValueError("Context lines must be non-negative")
69
94
  except ValueError:
70
- raise ToolError(f"Invalid line number '{line_number}'. Must be an integer.")
71
-
72
- elif pattern is not None:
73
- # TODO: Update this section for multiline pattern support later
74
- first_match_line_idx = -1
75
- for i, line in enumerate(lines):
76
- if pattern in line:
77
- first_match_line_idx = i
78
- break
79
-
80
- if first_match_line_idx != -1:
81
- center_line_idx = first_match_line_idx
82
- found_by = f"pattern '{pattern}' on line {center_line_idx + 1}"
83
- else:
84
- raise ToolError(f"Pattern '{pattern}' not found in {file_path}.")
85
-
86
- if center_line_idx == -1:
87
- # Should not happen if logic above is correct, but as a safeguard
88
- raise ToolError("Internal error: Could not determine center line.")
89
-
90
- # 5. Calculate context window
91
- try:
92
- context_lines_int = int(context_lines)
93
- if context_lines_int < 0:
94
- raise ValueError("Context lines must be non-negative")
95
- except ValueError:
96
- coder.io.tool_warning(
97
- f"Invalid context_lines value '{context_lines}', using default 3."
98
- )
99
- context_lines_int = 3
100
-
101
- start_line_idx = max(0, center_line_idx - context_lines_int)
102
- end_line_idx = min(num_lines - 1, center_line_idx + context_lines_int)
103
-
104
- # 6. Format output
105
- # Use rel_path for user-facing messages
106
- output_lines = [f"Displaying context around {found_by} in {rel_path}:"]
107
- max_line_num_width = len(str(end_line_idx + 1)) # Width for padding
108
-
109
- for i in range(start_line_idx, end_line_idx + 1):
110
- line_num_str = str(i + 1).rjust(max_line_num_width)
111
- output_lines.append(f"{line_num_str} | {lines[i]}")
112
-
113
- # Log success and return the formatted context directly
114
- coder.io.tool_output(f"Successfully retrieved context for {rel_path}")
115
- return "\n".join(output_lines)
116
-
117
- except ToolError as e:
118
- # Handle expected errors raised by utility functions or validation
119
- return handle_tool_error(coder, tool_name, e, add_traceback=False)
120
- except Exception as e:
121
- # Handle unexpected errors during processing
122
- return handle_tool_error(coder, tool_name, e)
123
-
124
-
125
- def process_response(coder, params):
126
- """
127
- Process the ShowNumberedContext tool response.
128
-
129
- Args:
130
- coder: The Coder instance
131
- params: Dictionary of parameters
132
-
133
- Returns:
134
- str: Result message
135
- """
136
- file_path = params.get("file_path")
137
- pattern = params.get("pattern")
138
- line_number = params.get("line_number")
139
- context_lines = params.get("context_lines", 3)
140
-
141
- if file_path is not None and (pattern is not None or line_number is not None):
142
- return execute_show_numbered_context(coder, file_path, pattern, line_number, context_lines)
143
- else:
144
- return (
145
- "Error: Missing required parameters for ViewNumberedContext (file_path"
146
- " and either pattern or line_number)"
147
- )
95
+ coder.io.tool_warning(
96
+ f"Invalid context_lines value '{context_lines}', using default 3."
97
+ )
98
+ context_lines_int = 3
99
+
100
+ start_line_idx = max(0, center_line_idx - context_lines_int)
101
+ end_line_idx = min(num_lines - 1, center_line_idx + context_lines_int)
102
+
103
+ # 6. Format output
104
+ # Use rel_path for user-facing messages
105
+ output_lines = [f"Displaying context around {found_by} in {rel_path}:"]
106
+ max_line_num_width = len(str(end_line_idx + 1)) # Width for padding
107
+
108
+ for i in range(start_line_idx, end_line_idx + 1):
109
+ line_num_str = str(i + 1).rjust(max_line_num_width)
110
+ output_lines.append(f"{line_num_str} | {lines[i]}")
111
+
112
+ # Log success and return the formatted context directly
113
+ coder.io.tool_output(f"Successfully retrieved context for {rel_path}")
114
+ return "\n".join(output_lines)
115
+
116
+ except ToolError as e:
117
+ # Handle expected errors raised by utility functions or validation
118
+ return handle_tool_error(coder, tool_name, e, add_traceback=False)
119
+ except Exception as e:
120
+ # Handle unexpected errors during processing
121
+ return handle_tool_error(coder, tool_name, e)