auto-coder 0.1.344__py3-none-any.whl → 0.1.345__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.

Potentially problematic release.


This version of auto-coder might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: auto-coder
3
- Version: 0.1.344
3
+ Version: 0.1.345
4
4
  Summary: AutoCoder: AutoCoder
5
5
  Author: allwefantasy
6
6
  Classifier: Programming Language :: Python :: 3.10
@@ -14,7 +14,7 @@ autocoder/command_parser.py,sha256=fx1g9E6GaM273lGTcJqaFQ-hoksS_Ik2glBMnVltPCE,1
14
14
  autocoder/lang.py,sha256=PFtATuOhHRnfpqHQkXr6p4C893JvpsgwTMif3l-GEi0,14321
15
15
  autocoder/models.py,sha256=_SCar82QIeBFTZZBdM2jPS6atKVhHnvE0gX3V0CsxD4,11590
16
16
  autocoder/run_context.py,sha256=IUfSO6_gp2Wt1blFWAmOpN0b0nDrTTk4LmtCYUBIoro,1643
17
- autocoder/version.py,sha256=0IbmbEY0zOBghpsboGY0La9spRPSLxD1p5QDr2CBCHY,23
17
+ autocoder/version.py,sha256=eIxV5f6b3So1kdcvglpjpAxjsitG6MNkwtws5Kg5FXM,23
18
18
  autocoder/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  autocoder/agent/agentic_edit.py,sha256=XsfePZ-t6M-uBSdG1VLZXk1goqXk2HPeJ_A8IYyBuWQ,58896
20
20
  autocoder/agent/agentic_edit_types.py,sha256=oFcDd_cxJ2yH9Ed1uTpD3BipudgoIEWDMPb5pAkq4gI,3288
@@ -126,21 +126,22 @@ autocoder/common/v2/code_editblock_manager.py,sha256=G0CIuV9Ki0FqMLnpA8nBT4pnkCN
126
126
  autocoder/common/v2/code_manager.py,sha256=C403bS-f6urixwitlKHcml-J03hci-UyNwHJOqBiY6Q,9182
127
127
  autocoder/common/v2/code_strict_diff_manager.py,sha256=v-J1kDyLg7tLGg_6_lbO9S4fNkx7M_L8Xr2G7fPptiU,9347
128
128
  autocoder/common/v2/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
129
- autocoder/common/v2/agent/agentic_edit.py,sha256=oqEMULpRjHOsqoeFI4rjQHUjaitYZIsgEwTZhp78Xeg,91682
129
+ autocoder/common/v2/agent/agentic_edit.py,sha256=LLRJmJs0wcB-N5_zZInV_SqlXC2SKgcXqSn5L_mHZck,91526
130
130
  autocoder/common/v2/agent/agentic_edit_conversation.py,sha256=qLLhTegH619JQTp3s1bj5FVn2hAcoV-DlhGO3UyIOMc,7338
131
131
  autocoder/common/v2/agent/agentic_edit_types.py,sha256=6qBLLmvdlcsbzrpMHsYQVIHqbOWubMXOnmkqTs1pBWQ,4629
132
132
  autocoder/common/v2/agent/agentic_tool_display.py,sha256=WKirt-2V346KLnbHgH3NVJiK3xvriD9oaCWj2IdvzLU,7309
133
+ autocoder/common/v2/agent/ignore_utils.py,sha256=gnUchRzKMLbUm_jvnKL-r-K9MWKPtt-6iiuzijY7Es0,1717
133
134
  autocoder/common/v2/agent/agentic_edit_tools/__init__.py,sha256=wGICCc1dYh07osB21j62zOQ9Ws0PyyOQ12UYRHmHrtI,1229
134
135
  autocoder/common/v2/agent/agentic_edit_tools/ask_followup_question_tool_resolver.py,sha256=YktmocUR0ygth8CVq9gXdUzIhX3FicULhwmnA_DTfik,3099
135
136
  autocoder/common/v2/agent/agentic_edit_tools/attempt_completion_tool_resolver.py,sha256=82ZGKeRBSDKeead_XVBW4FxpiE-5dS7tBOk_3RZ6B5s,1511
136
137
  autocoder/common/v2/agent/agentic_edit_tools/base_tool_resolver.py,sha256=Zid2m1uZd-2wVFGc_n_KAViXZyNjbdLSpI5n7ut1RUQ,1036
137
138
  autocoder/common/v2/agent/agentic_edit_tools/execute_command_tool_resolver.py,sha256=GueQfIY2hVu2R5j9R5rBtn2znl5MlmEdGtsa6snsMHs,4112
138
139
  autocoder/common/v2/agent/agentic_edit_tools/list_code_definition_names_tool_resolver.py,sha256=8QoMsADUDWliqiDt_dpguz31403syB8eeW0Pcw-qfb8,3842
139
- autocoder/common/v2/agent/agentic_edit_tools/list_files_tool_resolver.py,sha256=ERM5E7s2azQ8vcvogan4A_LZci8Pmhmxw1uQaNQhon4,5469
140
+ autocoder/common/v2/agent/agentic_edit_tools/list_files_tool_resolver.py,sha256=d0LzGPA3zsIHK5s1-arPry6ddWFSymRsMY3VbkV6v5A,5795
140
141
  autocoder/common/v2/agent/agentic_edit_tools/plan_mode_respond_tool_resolver.py,sha256=lGT4_QYJK6Fa9f6HVSGo0cSsGK7qCsDYgJGUowNxPzk,1499
141
142
  autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py,sha256=9Bh0KVbL0qiIqwChlb77biiBiETQ3zekxGe5Fj7hXAg,2800
142
143
  autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py,sha256=lpD4fCbVR8GTrynqXON69IjM94nPy3nuUL62Ashm5O4,7988
143
- autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py,sha256=ew0RwJ4oGjnpBOyYYtfxbFqU5W_kclT6FrCkpOB-tgs,6058
144
+ autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py,sha256=FfWL7afSeQ7yoaFzB_Au8areZGKbkHjChuj7OsPCEO0,5717
144
145
  autocoder/common/v2/agent/agentic_edit_tools/use_mcp_tool_resolver.py,sha256=wM2Xy4bcnD0TSLEmcM8rvvyyWenN5_KQnJMO6hJ8lTE,1716
145
146
  autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py,sha256=UO4SrkDek3WDlRdlHH022W1roSNMdMcipJqDxRBlheM,3044
146
147
  autocoder/compilers/__init__.py,sha256=C0HOms70QA747XD0uZEMmGtRFcIPenohyqECNStv0Bw,1647
@@ -213,7 +214,7 @@ autocoder/rag/doc_filter.py,sha256=UduVO2mlrngwJICrefjDJTYfdmQ4GcRXrfWDQ7xXksk,1
213
214
  autocoder/rag/document_retriever.py,sha256=5BDqKVJqLPScEnua5S5suXhWuCaALIfPf5obXeJoWfs,8461
214
215
  autocoder/rag/lang.py,sha256=HvcMeu6jReEJOGxyLMn4rwBoD-myFwmykS3VLceBJLs,3364
215
216
  autocoder/rag/llm_wrapper.py,sha256=Ht5GF5yJtrztoliujsZzx_ooWZmHkd5xLZKcGEiicZw,4303
216
- autocoder/rag/long_context_rag.py,sha256=RE4xse3XxSC_HQA5erqrx6MhanP_29mBRdYOTJQZYGc,42106
217
+ autocoder/rag/long_context_rag.py,sha256=bZy428EYnsh6ke4zNuxI6rP5k-m7Whel6GgLu98boQA,42239
217
218
  autocoder/rag/qa_conversation_strategy.py,sha256=1AcHV0MU00yTls20LlCPO-Un_OhSrr_p-H5lxLleAq4,6060
218
219
  autocoder/rag/rag_config.py,sha256=8LwFcTd8OJWWwi1_WY4IzjqgtT6RyE2j4PjxS5cCTDE,802
219
220
  autocoder/rag/rag_entry.py,sha256=6TKtErZ0Us9XSV6HgRKXA6yR3SiZGPHpynOKSaR1wgE,2463
@@ -276,9 +277,9 @@ autocoder/utils/types.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
276
277
  autocoder/utils/auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
277
278
  autocoder/utils/auto_coder_utils/chat_stream_out.py,sha256=KW0mlmcHlStXi8-_6fXZ2-ifeJ5mgP0OV7DQFzCtIsw,14008
278
279
  autocoder/utils/chat_auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
279
- auto_coder-0.1.344.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
280
- auto_coder-0.1.344.dist-info/METADATA,sha256=g-mR3QxVTRm1gl4iC9LNSvTjYzIFre3tpp8oxs5Qeg0,2728
281
- auto_coder-0.1.344.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
282
- auto_coder-0.1.344.dist-info/entry_points.txt,sha256=0nzHtHH4pNcM7xq4EBA2toS28Qelrvcbrr59GqD_0Ak,350
283
- auto_coder-0.1.344.dist-info/top_level.txt,sha256=Jqc0_uJSw2GwoFQAa9iJxYns-2mWla-9ok_Y3Gcznjk,10
284
- auto_coder-0.1.344.dist-info/RECORD,,
280
+ auto_coder-0.1.345.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
281
+ auto_coder-0.1.345.dist-info/METADATA,sha256=TzDlQ_--wVXbK1CZmNIKGlHH8obbgYOLGZl8gRYrSSM,2728
282
+ auto_coder-0.1.345.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
283
+ auto_coder-0.1.345.dist-info/entry_points.txt,sha256=0nzHtHH4pNcM7xq4EBA2toS28Qelrvcbrr59GqD_0Ak,350
284
+ auto_coder-0.1.345.dist-info/top_level.txt,sha256=Jqc0_uJSw2GwoFQAa9iJxYns-2mWla-9ok_Y3Gcznjk,10
285
+ auto_coder-0.1.345.dist-info/RECORD,,
@@ -736,8 +736,8 @@ class AgenticEdit:
736
736
  Analyzes the user request, interacts with the LLM, parses responses,
737
737
  executes tools, and yields structured events for visualization until completion or error.
738
738
  """
739
- system_prompt = self._analyze.prompt(request)
740
- # print(system_prompt)
739
+ system_prompt = self._analyze.prompt(request)
740
+ # print(system_prompt)
741
741
  conversations = [
742
742
  {"role": "system", "content": system_prompt},
743
743
  ] + self.conversation_manager.get_history()
@@ -747,13 +747,13 @@ class AgenticEdit:
747
747
  self.conversation_manager.add_user_message(request.user_input)
748
748
  logger.debug(
749
749
  f"Initial conversation history size: {len(conversations)}")
750
-
750
+
751
751
  tool_executed = False
752
752
  while True:
753
753
  global_cancel.check_and_raise()
754
754
  logger.info(
755
755
  f"Starting LLM interaction cycle. History size: {len(conversations)}")
756
-
756
+
757
757
  assistant_buffer = ""
758
758
 
759
759
  llm_response_gen = stream_chat_with_continue(
@@ -954,10 +954,10 @@ class AgenticEdit:
954
954
  params['options'])
955
955
  except json.JSONDecodeError:
956
956
  logger.warning(
957
- f"Could not decode JSON options for plan_mode_respond_tool: {params['options']}")
957
+ f"Could not decode JSON options for plan_mode_respond_tool: {params['options']}")
958
958
  # Handle recursive for list_files
959
959
  if tool_tag == 'list_files' and 'recursive' in params:
960
- params['recursive'] = params['recursive'].lower() == 'true'
960
+ params['recursive'] = params['recursive'].lower() == 'true'
961
961
  return tool_cls(**params)
962
962
  else:
963
963
  logger.error(f"Tool class not found for tag: {tool_tag}")
@@ -995,7 +995,7 @@ class AgenticEdit:
995
995
  break
996
996
 
997
997
  # 2. Check for </tool_tag> if inside tool block
998
- elif in_tool_block:
998
+ elif in_tool_block:
999
999
  end_tag = f"</{current_tool_tag}>"
1000
1000
  end_tool_pos = buffer.find(end_tag)
1001
1001
  if end_tool_pos != -1:
@@ -1155,16 +1155,16 @@ class AgenticEdit:
1155
1155
  event_manager.write_result(
1156
1156
  content=content.to_dict(), metadata=metadata.to_dict())
1157
1157
  elif isinstance(agent_event, PlanModeRespondEvent):
1158
- metadata.path = "/agent/edit/plan_mode_respond"
1158
+ metadata.path = "/agent/edit/plan_mode_respond"
1159
1159
  content = EventContentCreator.create_markdown_result(
1160
1160
  content=agent_event.completion.response,
1161
1161
  metadata={}
1162
- )
1162
+ )
1163
1163
  event_manager.write_result(
1164
1164
  content=content.to_dict(), metadata=metadata.to_dict())
1165
1165
 
1166
- elif isinstance(agent_event, TokenUsageEvent):
1167
- last_meta: SingleOutputMeta = agent_event.usage
1166
+ elif isinstance(agent_event, TokenUsageEvent):
1167
+ last_meta: SingleOutputMeta = agent_event.usage
1168
1168
  # Get model info for pricing
1169
1169
  from autocoder.utils import llms as llm_utils
1170
1170
  model_name = ",".join(llm_utils.get_llm_names(self.llm))
@@ -1181,7 +1181,7 @@ class AgenticEdit:
1181
1181
  # Convert to millions
1182
1182
  output_cost = (
1183
1183
  last_meta.generated_tokens_count * output_price) / 1000000
1184
-
1184
+
1185
1185
  get_event_manager(self.args.event_file).write_result(
1186
1186
  EventContentCreator.create_result(content=EventContentCreator.ResultTokenStatContent(
1187
1187
  model_name=model_name,
@@ -1191,7 +1191,7 @@ class AgenticEdit:
1191
1191
  output_tokens=last_meta.generated_tokens_count,
1192
1192
  input_cost=input_cost,
1193
1193
  output_cost=output_cost
1194
- ).to_dict()),metadata=metadata.to_dict())
1194
+ ).to_dict()), metadata=metadata.to_dict())
1195
1195
 
1196
1196
  elif isinstance(agent_event, CompletionEvent):
1197
1197
  # 在这里完成实际合并
@@ -1236,7 +1236,10 @@ class AgenticEdit:
1236
1236
  except Exception as e:
1237
1237
  logger.exception(
1238
1238
  "An unexpected error occurred during agent execution:")
1239
- metadata.path = "/agent/edit/error"
1239
+ metadata = EventMetadata(
1240
+ action_file=self.args.file,
1241
+ is_streaming=False,
1242
+ stream_out_type="/agent/edit/error")
1240
1243
  error_content = EventContentCreator.create_error(
1241
1244
  error_code="AGENT_FATAL_ERROR",
1242
1245
  error_message=f"An unexpected error occurred: {str(e)}",
@@ -1250,7 +1253,7 @@ class AgenticEdit:
1250
1253
  def apply_changes(self):
1251
1254
  """
1252
1255
  Apply all tracked file changes to the original project directory.
1253
- """
1256
+ """
1254
1257
  for (file_path, change) in self.get_all_file_changes().items():
1255
1258
  with open(file_path, 'w', encoding='utf-8') as f:
1256
1259
  f.write(change.content)
@@ -1,11 +1,13 @@
1
1
  import os
2
2
  from typing import Dict, Any, Optional
3
3
  from autocoder.common.v2.agent.agentic_edit_tools.base_tool_resolver import BaseToolResolver
4
- from autocoder.common.v2.agent.agentic_edit_types import ListFilesTool, ToolResult # Import ToolResult from types
4
+ from autocoder.common.v2.agent.agentic_edit_types import ListFilesTool, ToolResult # Import ToolResult from types
5
5
  from loguru import logger
6
6
  import typing
7
7
  from autocoder.common import AutoCoderArgs
8
8
 
9
+ from autocoder.common.v2.agent.ignore_utils import load_ignore_spec, should_ignore, DEFAULT_IGNORED_DIRS
10
+
9
11
  if typing.TYPE_CHECKING:
10
12
  from autocoder.common.v2.agent.agentic_edit import AgenticEdit
11
13
 
@@ -13,26 +15,25 @@ if typing.TYPE_CHECKING:
13
15
  class ListFilesToolResolver(BaseToolResolver):
14
16
  def __init__(self, agent: Optional['AgenticEdit'], tool: ListFilesTool, args: AutoCoderArgs):
15
17
  super().__init__(agent, tool, args)
16
- self.tool: ListFilesTool = tool # For type hinting
18
+ self.tool: ListFilesTool = tool # For type hinting
17
19
  self.shadow_manager = self.agent.shadow_manager
18
20
 
19
21
  def resolve(self) -> ToolResult:
20
22
  list_path_str = self.tool.path
21
23
  recursive = self.tool.recursive or False
22
24
  source_dir = self.args.source_dir or "."
25
+ absolute_source_dir = os.path.abspath(source_dir)
23
26
  absolute_list_path = os.path.abspath(os.path.join(source_dir, list_path_str))
24
27
 
28
+ # Load ignore spec from .autocoderignore if exists
29
+ ignore_spec = load_ignore_spec(absolute_source_dir)
30
+
25
31
  # Security check: Allow listing outside source_dir IF the original path is outside?
26
- # For now, let's restrict to source_dir for safety, unless path explicitly starts absolute
27
- # This needs careful consideration based on security requirements.
28
- # Let's allow listing anywhere for now, but log a warning if outside source_dir.
29
- is_outside_source = not absolute_list_path.startswith(os.path.abspath(source_dir))
32
+ is_outside_source = not absolute_list_path.startswith(absolute_source_dir)
30
33
  if is_outside_source:
31
- logger.warning(f"Listing path is outside the project source directory: {list_path_str}")
32
- # Add more checks if needed, e.g., prevent listing sensitive system dirs
34
+ logger.warning(f"Listing path is outside the project source directory: {list_path_str}")
33
35
 
34
36
  # Check if shadow directory exists for this path
35
- shadow_paths = []
36
37
  shadow_exists = False
37
38
  shadow_dir_path = None
38
39
  if self.shadow_manager:
@@ -57,17 +58,23 @@ class ListFilesToolResolver(BaseToolResolver):
57
58
  try:
58
59
  if recursive:
59
60
  for root, dirs, files in os.walk(base_dir):
61
+ # Modify dirs in-place to skip ignored dirs early
62
+ dirs[:] = [d for d in dirs if not should_ignore(os.path.join(root, d), ignore_spec, DEFAULT_IGNORED_DIRS, absolute_source_dir)]
60
63
  for name in files:
61
64
  full_path = os.path.join(root, name)
65
+ if should_ignore(full_path, ignore_spec, DEFAULT_IGNORED_DIRS, absolute_source_dir):
66
+ continue
62
67
  display_path = os.path.relpath(full_path, source_dir) if not is_outside_source else full_path
63
68
  result.add(display_path)
64
- for name in dirs:
65
- full_path = os.path.join(root, name)
69
+ for d in dirs:
70
+ full_path = os.path.join(root, d)
66
71
  display_path = os.path.relpath(full_path, source_dir) if not is_outside_source else full_path
67
72
  result.add(display_path + "/")
68
73
  else:
69
74
  for item in os.listdir(base_dir):
70
75
  full_path = os.path.join(base_dir, item)
76
+ if should_ignore(full_path, ignore_spec, DEFAULT_IGNORED_DIRS, absolute_source_dir):
77
+ continue
71
78
  display_path = os.path.relpath(full_path, source_dir) if not is_outside_source else full_path
72
79
  if os.path.isdir(full_path):
73
80
  result.add(display_path + "/")
@@ -87,9 +94,7 @@ class ListFilesToolResolver(BaseToolResolver):
87
94
  source_files_set = list_files_in_dir(absolute_list_path)
88
95
 
89
96
  # Merge results, prioritizing shadow files if exist
90
- merged_files = set()
91
97
  if shadow_exists:
92
- # Use shadow files + source files that are NOT shadowed
93
98
  merged_files = shadow_files_set.union(
94
99
  {f for f in source_files_set if f not in shadow_files_set}
95
100
  )
@@ -102,4 +107,4 @@ class ListFilesToolResolver(BaseToolResolver):
102
107
  return ToolResult(success=True, message=message, content=sorted(merged_files))
103
108
  except Exception as e:
104
109
  logger.error(f"Error listing files in '{list_path_str}': {str(e)}")
105
- return ToolResult(success=False, message=f"An unexpected error occurred while listing files: {str(e)}")
110
+ return ToolResult(success=False, message=f"An unexpected error occurred while listing files: {str(e)}")
@@ -1,13 +1,16 @@
1
+
1
2
  import os
2
3
  import re
3
4
  import glob
4
5
  from typing import Dict, Any, Optional
5
6
  from autocoder.common.v2.agent.agentic_edit_tools.base_tool_resolver import BaseToolResolver
6
- from autocoder.common.v2.agent.agentic_edit_types import SearchFilesTool, ToolResult # Import ToolResult from types
7
+ from autocoder.common.v2.agent.agentic_edit_types import SearchFilesTool, ToolResult # Import ToolResult from types
7
8
  from loguru import logger
8
9
  from autocoder.common import AutoCoderArgs
9
10
  import typing
10
11
 
12
+ from autocoder.common.v2.agent.ignore_utils import load_ignore_spec, should_ignore, DEFAULT_IGNORED_DIRS
13
+
11
14
  if typing.TYPE_CHECKING:
12
15
  from autocoder.common.v2.agent.agentic_edit import AgenticEdit
13
16
 
@@ -15,18 +18,22 @@ if typing.TYPE_CHECKING:
15
18
  class SearchFilesToolResolver(BaseToolResolver):
16
19
  def __init__(self, agent: Optional['AgenticEdit'], tool: SearchFilesTool, args: AutoCoderArgs):
17
20
  super().__init__(agent, tool, args)
18
- self.tool: SearchFilesTool = tool # For type hinting
21
+ self.tool: SearchFilesTool = tool
19
22
  self.shadow_manager = self.agent.shadow_manager if self.agent else None
20
23
 
21
24
  def resolve(self) -> ToolResult:
22
25
  search_path_str = self.tool.path
23
26
  regex_pattern = self.tool.regex
24
- file_pattern = self.tool.file_pattern or "*" # Default to all files
27
+ file_pattern = self.tool.file_pattern or "*"
25
28
  source_dir = self.args.source_dir or "."
29
+ absolute_source_dir = os.path.abspath(source_dir)
26
30
  absolute_search_path = os.path.abspath(os.path.join(source_dir, search_path_str))
27
31
 
32
+ # Load ignore spec from .autocoderignore if exists
33
+ ignore_spec = load_ignore_spec(absolute_source_dir)
34
+
28
35
  # Security check
29
- if not absolute_search_path.startswith(os.path.abspath(source_dir)):
36
+ if not absolute_search_path.startswith(absolute_source_dir):
30
37
  return ToolResult(success=False, message=f"Error: Access denied. Attempted to search outside the project directory: {search_path_str}")
31
38
 
32
39
  # Determine search base directory: prefer shadow if exists
@@ -54,12 +61,11 @@ class SearchFilesToolResolver(BaseToolResolver):
54
61
  compiled_regex = re.compile(regex_pattern)
55
62
  search_glob_pattern = os.path.join(search_base_path, "**", file_pattern)
56
63
 
57
- ignored_dirs = ['.git',".auto-coder", 'node_modules', '.mvn', '.idea', '__pycache__', '.venv', 'venv', 'dist', 'build', '.gradle']
58
- logger.info(f"Searching for regex '{regex_pattern}' in files matching '{file_pattern}' under '{search_base_path}' (shadow: {shadow_exists}), ignoring directories: {ignored_dirs}")
64
+ logger.info(f"Searching for regex '{regex_pattern}' in files matching '{file_pattern}' under '{search_base_path}' (shadow: {shadow_exists}) with ignore rules applied.")
59
65
 
60
66
  for filepath in glob.glob(search_glob_pattern, recursive=True):
61
- normalized_path = filepath.replace("\\", "/") # Normalize for Windows paths
62
- if any(f"/{ignored_dir}/" in normalized_path or normalized_path.endswith(f"/{ignored_dir}") or f"/{ignored_dir}/" in normalized_path for ignored_dir in ignored_dirs):
67
+ abs_path = os.path.abspath(filepath)
68
+ if should_ignore(abs_path, ignore_spec, DEFAULT_IGNORED_DIRS, absolute_source_dir):
63
69
  continue
64
70
 
65
71
  if os.path.isfile(filepath):
@@ -68,11 +74,9 @@ class SearchFilesToolResolver(BaseToolResolver):
68
74
  lines = f.readlines()
69
75
  for i, line in enumerate(lines):
70
76
  if compiled_regex.search(line):
71
- # Provide context (e.g., line number and surrounding lines)
72
77
  context_start = max(0, i - 2)
73
78
  context_end = min(len(lines), i + 3)
74
79
  context = "".join([f"{j+1}: {lines[j]}" for j in range(context_start, context_end)])
75
- # For shadow files, convert to project-relative path
76
80
  if shadow_exists and self.shadow_manager:
77
81
  try:
78
82
  abs_project_path = self.shadow_manager.from_shadow_path(filepath)
@@ -87,10 +91,9 @@ class SearchFilesToolResolver(BaseToolResolver):
87
91
  "match_line": line.strip(),
88
92
  "context": context.strip()
89
93
  })
90
- # Limit results per file? Or overall? For now, collect all.
91
94
  except Exception as e:
92
95
  logger.warning(f"Could not read or process file {filepath}: {e}")
93
- continue # Skip files that can't be read
96
+ continue
94
97
 
95
98
  message = f"Search completed. Found {len(results)} matches."
96
99
  logger.info(message)
@@ -101,4 +104,4 @@ class SearchFilesToolResolver(BaseToolResolver):
101
104
  return ToolResult(success=False, message=f"Invalid regex pattern: {e}")
102
105
  except Exception as e:
103
106
  logger.error(f"Error during file search: {str(e)}")
104
- return ToolResult(success=False, message=f"An unexpected error occurred during search: {str(e)}")
107
+ return ToolResult(success=False, message=f"An unexpected error occurred during search: {str(e)}")
@@ -0,0 +1,50 @@
1
+
2
+ import os
3
+ from typing import Optional, List
4
+ import pathspec
5
+
6
+ DEFAULT_IGNORED_DIRS = ['.git', '.auto-coder', 'node_modules', '.mvn', '.idea', '__pycache__', '.venv', 'venv', 'dist', 'build', '.gradle']
7
+
8
+
9
+ def load_ignore_spec(source_dir: str) -> Optional[pathspec.PathSpec]:
10
+ """
11
+ Loads .autocoderignore file from the source_dir if it exists.
12
+ Returns a PathSpec object or None if no ignore file.
13
+ """
14
+ ignore_file_path = os.path.join(source_dir, ".autocoderignore")
15
+ if not os.path.isfile(ignore_file_path):
16
+ return None
17
+ try:
18
+ with open(ignore_file_path, "r") as f:
19
+ ignore_patterns = f.read().splitlines()
20
+ spec = pathspec.PathSpec.from_lines("gitwildmatch", ignore_patterns)
21
+ return spec
22
+ except Exception:
23
+ return None
24
+
25
+
26
+ def should_ignore(path: str, ignore_spec: Optional[pathspec.PathSpec], ignored_dirs: List[str], source_dir: str) -> bool:
27
+ """
28
+ Determine if a given path should be ignored based on ignore_spec and ignored_dirs.
29
+ - path: absolute path
30
+ - ignore_spec: PathSpec object or None
31
+ - ignored_dirs: list of directory names to ignore
32
+ - source_dir: root source directory absolute path
33
+ """
34
+ rel_path = os.path.relpath(path, source_dir)
35
+ parts = rel_path.split(os.sep)
36
+
37
+ # Always ignore if any part matches ignored_dirs
38
+ for part in parts:
39
+ if part in ignored_dirs:
40
+ return True
41
+
42
+ # If ignore_spec exists, use it to check
43
+ if ignore_spec:
44
+ # pathspec expects posix style paths
45
+ rel_path_posix = rel_path.replace(os.sep, "/")
46
+ # Check both file and dir ignoring
47
+ if ignore_spec.match_file(rel_path_posix):
48
+ return True
49
+
50
+ return False
@@ -268,6 +268,9 @@ class LongContextRAG:
268
268
 
269
269
  def _load_ignore_file(self):
270
270
  serveignore_path = os.path.join(self.path, ".serveignore")
271
+ if not os.path.exists(serveignore_path):
272
+ serveignore_path = os.path.join(self.path, ".autocoderignore")
273
+
271
274
  gitignore_path = os.path.join(self.path, ".gitignore")
272
275
 
273
276
  if os.path.exists(serveignore_path):
autocoder/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.344"
1
+ __version__ = "0.1.345"