auto-coder 0.1.354__py3-none-any.whl → 0.1.356__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.
- {auto_coder-0.1.354.dist-info → auto_coder-0.1.356.dist-info}/METADATA +1 -1
- {auto_coder-0.1.354.dist-info → auto_coder-0.1.356.dist-info}/RECORD +40 -35
- autocoder/agent/agentic_filter.py +1 -1
- autocoder/agent/auto_learn.py +631 -0
- autocoder/auto_coder.py +8 -0
- autocoder/auto_coder_runner.py +59 -87
- autocoder/chat/conf_command.py +270 -0
- autocoder/chat/models_command.py +485 -0
- autocoder/chat/rules_command.py +458 -0
- autocoder/chat_auto_coder.py +34 -24
- autocoder/chat_auto_coder_lang.py +156 -2
- autocoder/commands/auto_command.py +1 -1
- autocoder/commands/auto_web.py +1 -1
- autocoder/common/__init__.py +2 -0
- autocoder/common/auto_coder_lang.py +9 -1
- autocoder/common/command_completer.py +58 -12
- autocoder/common/command_completer_v2.py +615 -0
- autocoder/common/global_cancel.py +53 -16
- autocoder/common/rulefiles/autocoderrules_utils.py +83 -0
- autocoder/common/v2/agent/agentic_edit.py +4 -4
- autocoder/common/v2/code_agentic_editblock_manager.py +9 -9
- autocoder/common/v2/code_diff_manager.py +2 -2
- autocoder/common/v2/code_editblock_manager.py +11 -10
- autocoder/common/v2/code_strict_diff_manager.py +3 -2
- autocoder/dispacher/actions/action.py +6 -6
- autocoder/dispacher/actions/plugins/action_regex_project.py +2 -2
- autocoder/events/event_manager_singleton.py +1 -1
- autocoder/index/index.py +2 -2
- autocoder/rag/cache/local_byzer_storage_cache.py +1 -1
- autocoder/rag/cache/local_duckdb_storage_cache.py +8 -0
- autocoder/rag/loaders/image_loader.py +25 -13
- autocoder/rag/long_context_rag.py +2 -2
- autocoder/utils/auto_coder_utils/chat_stream_out.py +3 -4
- autocoder/utils/model_provider_selector.py +14 -2
- autocoder/utils/thread_utils.py +9 -27
- autocoder/version.py +1 -1
- {auto_coder-0.1.354.dist-info → auto_coder-0.1.356.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.354.dist-info → auto_coder-0.1.356.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.354.dist-info → auto_coder-0.1.356.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.354.dist-info → auto_coder-0.1.356.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import io
|
|
3
|
+
import fnmatch
|
|
4
|
+
import json
|
|
5
|
+
import pathspec
|
|
6
|
+
from typing import Dict, Any, List, Callable, Optional
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
from rich.panel import Panel
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
from rich.text import Text
|
|
11
|
+
from rich.markdown import Markdown
|
|
12
|
+
from autocoder.auto_coder_runner import get_memory, save_memory
|
|
13
|
+
from autocoder.common.rulefiles.autocoderrules_utils import AutocoderRulesManager
|
|
14
|
+
from autocoder.agent.auto_learn import AutoLearn
|
|
15
|
+
from autocoder.common import SourceCode, SourceCodeList
|
|
16
|
+
from autocoder.auto_coder_runner import get_final_config, get_single_llm
|
|
17
|
+
from autocoder.chat_auto_coder_lang import get_message, get_message_with_format
|
|
18
|
+
from autocoder.rag.token_counter import count_tokens
|
|
19
|
+
from autocoder.common.printer import Printer
|
|
20
|
+
from autocoder.command_parser import CommandParser
|
|
21
|
+
from loguru import logger
|
|
22
|
+
|
|
23
|
+
printer = Printer()
|
|
24
|
+
|
|
25
|
+
# Helper function to print the rules files table (internal implementation)
|
|
26
|
+
def _print_rules_table(rules: Dict[str, str], pattern: str = "*"):
|
|
27
|
+
"""Display rules files in a Rich table format."""
|
|
28
|
+
console = Console() # Capture output
|
|
29
|
+
|
|
30
|
+
# Create a styled table with rounded borders
|
|
31
|
+
title_text = get_message_with_format("rules_file_list_title", pattern=pattern)
|
|
32
|
+
printer.print_str_in_terminal(title_text)
|
|
33
|
+
table = Table(
|
|
34
|
+
show_header=True,
|
|
35
|
+
header_style="bold magenta",
|
|
36
|
+
title=title_text,
|
|
37
|
+
title_style="bold blue",
|
|
38
|
+
border_style="blue",
|
|
39
|
+
show_lines=True
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Add columns with explicit width and alignment
|
|
43
|
+
column_title = get_message("rules_file_path")
|
|
44
|
+
printer.print_str_in_terminal(column_title)
|
|
45
|
+
table.add_column(column_title, style="cyan", justify="left", width=80, no_wrap=False)
|
|
46
|
+
table.add_column("Token数", style="green", justify="right", width=15, no_wrap=False)
|
|
47
|
+
|
|
48
|
+
# Sort keys for consistent display
|
|
49
|
+
for file_path in sorted(rules.keys()):
|
|
50
|
+
if not fnmatch.fnmatch(os.path.basename(file_path), pattern):
|
|
51
|
+
continue
|
|
52
|
+
|
|
53
|
+
content = rules[file_path]
|
|
54
|
+
token_count = count_tokens(content)
|
|
55
|
+
|
|
56
|
+
# Format display values
|
|
57
|
+
formatted_path = Text(file_path, style="cyan")
|
|
58
|
+
formatted_token_count = Text(str(token_count), style="bright_cyan")
|
|
59
|
+
|
|
60
|
+
table.add_row(formatted_path, formatted_token_count)
|
|
61
|
+
|
|
62
|
+
# Add padding and print with a panel
|
|
63
|
+
console.print(Panel(
|
|
64
|
+
table,
|
|
65
|
+
padding=(1, 2),
|
|
66
|
+
subtitle=f"[italic]{get_message('rules_help_subtitle')}[/italic]",
|
|
67
|
+
border_style="blue"
|
|
68
|
+
))
|
|
69
|
+
|
|
70
|
+
# --- Command Handlers ---
|
|
71
|
+
|
|
72
|
+
def _handle_list_rules(memory: Dict[str, Any], args: List[str]) -> str:
|
|
73
|
+
"""Handles listing rules files, supports wildcard filtering."""
|
|
74
|
+
from autocoder.common.rulefiles.autocoderrules_utils import get_rules
|
|
75
|
+
|
|
76
|
+
# 获取项目根目录(当前工作目录)
|
|
77
|
+
project_root = os.getcwd()
|
|
78
|
+
|
|
79
|
+
# 获取所有规则文件
|
|
80
|
+
rules = get_rules(project_root)
|
|
81
|
+
|
|
82
|
+
if not rules:
|
|
83
|
+
message = get_message("rules_no_files_found")
|
|
84
|
+
printer.print_str_in_terminal(message)
|
|
85
|
+
return message
|
|
86
|
+
|
|
87
|
+
# 如果提供了参数,使用它作为过滤模式
|
|
88
|
+
pattern = args[0] if args else "*"
|
|
89
|
+
|
|
90
|
+
# 使用通配符匹配规则文件
|
|
91
|
+
if pattern != "*":
|
|
92
|
+
# 使用 pathspec 处理通配符路径
|
|
93
|
+
try:
|
|
94
|
+
# 创建 pathspec 对象,支持 .gitignore 风格的路径匹配
|
|
95
|
+
spec = pathspec.PathSpec.from_lines(
|
|
96
|
+
pathspec.patterns.GitWildMatchPattern, [pattern]
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# 获取相对于项目根目录的路径
|
|
100
|
+
rel_paths = {}
|
|
101
|
+
for file_path in rules.keys():
|
|
102
|
+
# 计算相对路径用于匹配
|
|
103
|
+
if os.path.isabs(file_path):
|
|
104
|
+
rel_path = os.path.relpath(file_path, project_root)
|
|
105
|
+
else:
|
|
106
|
+
rel_path = file_path
|
|
107
|
+
rel_paths[file_path] = rel_path
|
|
108
|
+
|
|
109
|
+
# 使用 pathspec 匹配文件
|
|
110
|
+
filtered_rules = {}
|
|
111
|
+
for file_path, rel_path in rel_paths.items():
|
|
112
|
+
if spec.match_file(rel_path):
|
|
113
|
+
filtered_rules[file_path] = rules[file_path]
|
|
114
|
+
|
|
115
|
+
if not filtered_rules:
|
|
116
|
+
message = get_message_with_format("rules_no_matching_files", pattern=pattern)
|
|
117
|
+
printer.print_str_in_terminal(message)
|
|
118
|
+
return message
|
|
119
|
+
return _print_rules_table(filtered_rules, pattern)
|
|
120
|
+
except Exception as e:
|
|
121
|
+
message = f"Error matching pattern '{pattern}': {str(e)}"
|
|
122
|
+
printer.print_str_in_terminal(message)
|
|
123
|
+
return message
|
|
124
|
+
else:
|
|
125
|
+
return _print_rules_table(rules)
|
|
126
|
+
|
|
127
|
+
def _handle_remove_rules(memory: Dict[str, Any], args: List[str]) -> str:
|
|
128
|
+
"""Handles removing rules files based on glob pattern."""
|
|
129
|
+
if not args:
|
|
130
|
+
message = get_message("rules_remove_param_required")
|
|
131
|
+
printer.print_str_in_terminal(message)
|
|
132
|
+
return message
|
|
133
|
+
|
|
134
|
+
pattern = args[0]
|
|
135
|
+
|
|
136
|
+
# 获取规则管理器
|
|
137
|
+
rules_manager = AutocoderRulesManager()
|
|
138
|
+
rules = rules_manager.get_rules()
|
|
139
|
+
|
|
140
|
+
if not rules:
|
|
141
|
+
message = get_message("rules_no_files_found")
|
|
142
|
+
printer.print_str_in_terminal(message)
|
|
143
|
+
return message
|
|
144
|
+
|
|
145
|
+
# 获取项目根目录
|
|
146
|
+
project_root = os.getcwd()
|
|
147
|
+
|
|
148
|
+
# 使用 pathspec 匹配要删除的文件
|
|
149
|
+
files_to_remove = []
|
|
150
|
+
try:
|
|
151
|
+
# 创建 pathspec 对象,支持 .gitignore 风格的路径匹配
|
|
152
|
+
spec = pathspec.PathSpec.from_lines(
|
|
153
|
+
pathspec.patterns.GitWildMatchPattern, [pattern]
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# 获取相对于项目根目录的路径
|
|
157
|
+
for file_path in rules.keys():
|
|
158
|
+
# 计算相对路径用于匹配
|
|
159
|
+
if os.path.isabs(file_path):
|
|
160
|
+
rel_path = os.path.relpath(file_path, project_root)
|
|
161
|
+
else:
|
|
162
|
+
rel_path = file_path
|
|
163
|
+
|
|
164
|
+
if spec.match_file(rel_path):
|
|
165
|
+
files_to_remove.append(file_path)
|
|
166
|
+
except Exception as e:
|
|
167
|
+
message = f"Error matching pattern '{pattern}': {str(e)}"
|
|
168
|
+
printer.print_str_in_terminal(message)
|
|
169
|
+
return message
|
|
170
|
+
|
|
171
|
+
if not files_to_remove:
|
|
172
|
+
message = get_message_with_format("rules_no_files_to_remove", pattern=pattern)
|
|
173
|
+
printer.print_str_in_terminal(message)
|
|
174
|
+
return message
|
|
175
|
+
|
|
176
|
+
# 删除匹配的文件
|
|
177
|
+
removed_count = 0
|
|
178
|
+
for file_path in files_to_remove:
|
|
179
|
+
try:
|
|
180
|
+
os.remove(file_path)
|
|
181
|
+
removed_count += 1
|
|
182
|
+
except Exception as e:
|
|
183
|
+
message = get_message_with_format("rules_delete_error", file_path=file_path, error=str(e))
|
|
184
|
+
printer.print_str_in_terminal(message)
|
|
185
|
+
return message
|
|
186
|
+
|
|
187
|
+
# 重新加载规则
|
|
188
|
+
rules_manager._load_rules()
|
|
189
|
+
|
|
190
|
+
message = get_message_with_format("rules_delete_success", count=removed_count)
|
|
191
|
+
printer.print_str_in_terminal(message)
|
|
192
|
+
return message
|
|
193
|
+
|
|
194
|
+
def _handle_analyze_commit_rules(memory: Dict[str, Any], args: List[str],commit_id: str, coding_func=None) -> str:
|
|
195
|
+
"""Handles analyzing current files with rules."""
|
|
196
|
+
query = " ".join(args) if args else ""
|
|
197
|
+
|
|
198
|
+
args = get_final_config()
|
|
199
|
+
llm = get_single_llm(args.model, product_mode=args.product_mode)
|
|
200
|
+
auto_learn = AutoLearn(llm=llm, args=args)
|
|
201
|
+
changes, _ = auto_learn.get_commit_changes(commit_id)
|
|
202
|
+
|
|
203
|
+
try:
|
|
204
|
+
result = auto_learn.analyze_commit.prompt(
|
|
205
|
+
querie_with_urls_and_changes=changes,
|
|
206
|
+
new_query=query
|
|
207
|
+
)
|
|
208
|
+
# 如果传入了 coding_func,则执行
|
|
209
|
+
if coding_func is not None:
|
|
210
|
+
coding_func(query=result)
|
|
211
|
+
return result
|
|
212
|
+
except Exception as e:
|
|
213
|
+
logger.exception(e)
|
|
214
|
+
import traceback
|
|
215
|
+
traceback.print_exc()
|
|
216
|
+
message = get_message_with_format("rules_analysis_error", error=str(e))
|
|
217
|
+
printer.print_str_in_terminal(message)
|
|
218
|
+
return message
|
|
219
|
+
|
|
220
|
+
def _handle_analyze_rules(memory: Dict[str, Any], args: List[str], coding_func=None) -> str:
|
|
221
|
+
"""Handles analyzing current files with rules."""
|
|
222
|
+
query = " ".join(args) if args else ""
|
|
223
|
+
|
|
224
|
+
args = get_final_config()
|
|
225
|
+
llm = get_single_llm(args.model, product_mode=args.product_mode)
|
|
226
|
+
auto_learn = AutoLearn(llm=llm, args=args)
|
|
227
|
+
|
|
228
|
+
files = memory.get("current_files", {}).get("files", [])
|
|
229
|
+
if not files:
|
|
230
|
+
message = get_message("rules_no_active_files")
|
|
231
|
+
printer.print_str_in_terminal(message)
|
|
232
|
+
return message
|
|
233
|
+
|
|
234
|
+
sources = SourceCodeList([])
|
|
235
|
+
for file in files:
|
|
236
|
+
try:
|
|
237
|
+
with open(file, "r", encoding="utf-8") as f:
|
|
238
|
+
source_code = f.read()
|
|
239
|
+
sources.sources.append(SourceCode(module_name=file, source_code=source_code))
|
|
240
|
+
except Exception as e:
|
|
241
|
+
message = get_message_with_format("rules_file_read_error", file_path=file, error=str(e))
|
|
242
|
+
printer.print_str_in_terminal(message)
|
|
243
|
+
continue
|
|
244
|
+
|
|
245
|
+
try:
|
|
246
|
+
result = auto_learn.analyze_modules.prompt(sources=sources, query=query)
|
|
247
|
+
# 如果传入了 coding_func,则执行
|
|
248
|
+
if coding_func is not None:
|
|
249
|
+
coding_func(query=result)
|
|
250
|
+
return result
|
|
251
|
+
except Exception as e:
|
|
252
|
+
message = get_message_with_format("rules_analysis_error", error=str(e))
|
|
253
|
+
printer.print_str_in_terminal(message)
|
|
254
|
+
return message
|
|
255
|
+
|
|
256
|
+
def _handle_get_rules(memory: Dict[str, Any], args: List[str]) -> str:
|
|
257
|
+
"""Handles displaying the content of rules files based on glob pattern."""
|
|
258
|
+
if not args:
|
|
259
|
+
message = get_message("rules_get_param_required")
|
|
260
|
+
printer.print_str_in_terminal(message)
|
|
261
|
+
return message
|
|
262
|
+
|
|
263
|
+
pattern = args[0]
|
|
264
|
+
|
|
265
|
+
# 获取规则管理器
|
|
266
|
+
from autocoder.common.rulefiles.autocoderrules_utils import get_rules
|
|
267
|
+
project_root = os.getcwd()
|
|
268
|
+
rules = get_rules(project_root)
|
|
269
|
+
|
|
270
|
+
if not rules:
|
|
271
|
+
message = get_message("rules_no_files_found")
|
|
272
|
+
printer.print_str_in_terminal(message)
|
|
273
|
+
return message
|
|
274
|
+
|
|
275
|
+
# 使用 pathspec 匹配文件
|
|
276
|
+
matched_files = []
|
|
277
|
+
try:
|
|
278
|
+
# 创建 pathspec 对象,支持 .gitignore 风格的路径匹配
|
|
279
|
+
spec = pathspec.PathSpec.from_lines(
|
|
280
|
+
pathspec.patterns.GitWildMatchPattern, [pattern]
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
# 获取相对于项目根目录的路径
|
|
284
|
+
for file_path in rules.keys():
|
|
285
|
+
# 计算相对路径用于匹配
|
|
286
|
+
if os.path.isabs(file_path):
|
|
287
|
+
rel_path = os.path.relpath(file_path, project_root)
|
|
288
|
+
else:
|
|
289
|
+
rel_path = file_path
|
|
290
|
+
|
|
291
|
+
if spec.match_file(rel_path):
|
|
292
|
+
matched_files.append(file_path)
|
|
293
|
+
except Exception as e:
|
|
294
|
+
message = f"Error matching pattern '{pattern}': {str(e)}"
|
|
295
|
+
printer.print_str_in_terminal(message)
|
|
296
|
+
return message
|
|
297
|
+
|
|
298
|
+
if not matched_files:
|
|
299
|
+
message = get_message_with_format("rules_get_no_matching_files", pattern=pattern)
|
|
300
|
+
printer.print_str_in_terminal(message)
|
|
301
|
+
return message
|
|
302
|
+
|
|
303
|
+
# 创建一个真实的控制台,而不是捕获输出
|
|
304
|
+
console = Console()
|
|
305
|
+
|
|
306
|
+
# 打印每个匹配文件的内容
|
|
307
|
+
for file_path in sorted(matched_files):
|
|
308
|
+
try:
|
|
309
|
+
# 获取文件内容
|
|
310
|
+
content = rules[file_path]
|
|
311
|
+
|
|
312
|
+
# 打印文件标题
|
|
313
|
+
console.print("\n")
|
|
314
|
+
console.print(Panel(
|
|
315
|
+
get_message_with_format("rules_get_file_title", file_path=file_path),
|
|
316
|
+
style="bold blue"
|
|
317
|
+
))
|
|
318
|
+
|
|
319
|
+
# 以Markdown格式打印内容
|
|
320
|
+
md = Markdown(content)
|
|
321
|
+
console.print(md)
|
|
322
|
+
|
|
323
|
+
except Exception as e:
|
|
324
|
+
printer.print_str_in_terminal(get_message_with_format("rules_get_read_error", file_path=file_path, error=str(e)))
|
|
325
|
+
logger.exception(e)
|
|
326
|
+
|
|
327
|
+
# 由于控制台直接打印,返回空字符串
|
|
328
|
+
return ""
|
|
329
|
+
|
|
330
|
+
def _handle_help(memory: Dict[str, Any], args: List[str]) -> str:
|
|
331
|
+
"""Provides help text for the /rules command."""
|
|
332
|
+
help_text = get_message("rules_help_text")
|
|
333
|
+
if not help_text:
|
|
334
|
+
# 如果没有翻译好的帮助文本,提供默认的中文帮助文本
|
|
335
|
+
help_text = """
|
|
336
|
+
/rules 命令帮助:
|
|
337
|
+
/rules /list [通配符] - 列出规则文件,可选通配符过滤
|
|
338
|
+
/rules /get [通配符] - 查看规则文件内容,可选通配符过滤
|
|
339
|
+
/rules /remove [通配符] - 删除规则文件,使用通配符匹配
|
|
340
|
+
/rules /analyze [查询] - 分析当前文件,可选提供查询内容
|
|
341
|
+
/rules /commit <提交ID> /query <查询> - 分析特定提交,必须提供提交ID和查询内容
|
|
342
|
+
/rules /help - 显示此帮助信息
|
|
343
|
+
|
|
344
|
+
默认情况下,直接使用 /rules [查询] 相当于 /rules /analyze [查询]
|
|
345
|
+
"""
|
|
346
|
+
|
|
347
|
+
printer.print_str_in_terminal(help_text)
|
|
348
|
+
return help_text
|
|
349
|
+
|
|
350
|
+
def _handle_commit_rules(memory: Dict[str, Any], args: List[str], coding_func=None) -> str:
|
|
351
|
+
"""处理 commit 命令,要求格式为 /commit <commit_id> /query <查询内容>"""
|
|
352
|
+
if not args:
|
|
353
|
+
message = get_message("rules_commit_param_required")
|
|
354
|
+
printer.print_str_in_terminal(message)
|
|
355
|
+
return message
|
|
356
|
+
|
|
357
|
+
# 构建要解析的完整命令字符串
|
|
358
|
+
command_str = " ".join(args)
|
|
359
|
+
|
|
360
|
+
# 使用 CommandParser 解析命令
|
|
361
|
+
parser = CommandParser()
|
|
362
|
+
|
|
363
|
+
## 传递过来的命令行是没有 /commit 子命令的,需要补充上
|
|
364
|
+
commands = parser.parse(f"/commit {command_str}")
|
|
365
|
+
|
|
366
|
+
# 验证必须包含 commit 和 query 命令
|
|
367
|
+
if not commands.get('commit') or not commands.get('query'):
|
|
368
|
+
message = get_message("rules_commit_format_error")
|
|
369
|
+
printer.print_str_in_terminal(message)
|
|
370
|
+
return message
|
|
371
|
+
|
|
372
|
+
# 获取 commit_id 和查询内容
|
|
373
|
+
commit_args = commands.get('commit', {}).get('args', [])
|
|
374
|
+
if not commit_args:
|
|
375
|
+
message = get_message("rules_commit_id_required")
|
|
376
|
+
printer.print_str_in_terminal(message)
|
|
377
|
+
return message
|
|
378
|
+
|
|
379
|
+
commit_id = commit_args[0]
|
|
380
|
+
|
|
381
|
+
# 获取查询内容
|
|
382
|
+
query_args = commands.get('query', {}).get('args', [])
|
|
383
|
+
if not query_args:
|
|
384
|
+
message = get_message("rules_query_required")
|
|
385
|
+
printer.print_str_in_terminal(message)
|
|
386
|
+
return message
|
|
387
|
+
|
|
388
|
+
query = " ".join(query_args)
|
|
389
|
+
|
|
390
|
+
# 实现实际的 commit 分析逻辑...
|
|
391
|
+
try:
|
|
392
|
+
_handle_analyze_commit_rules(memory, [query], commit_id=commit_id, coding_func=coding_func)
|
|
393
|
+
message = get_message_with_format("rules_commit_success", commit_id=commit_id, query=query)
|
|
394
|
+
printer.print_str_in_terminal(message)
|
|
395
|
+
return message
|
|
396
|
+
except Exception as e:
|
|
397
|
+
message = get_message_with_format("rules_commit_error", commit_id=commit_id, error=str(e))
|
|
398
|
+
printer.print_str_in_terminal(message)
|
|
399
|
+
return message
|
|
400
|
+
|
|
401
|
+
# Command dispatch table
|
|
402
|
+
COMMAND_HANDLERS: Dict[str, Callable[[Dict[str, Any], List[str]], str]] = {
|
|
403
|
+
"list": _handle_list_rules,
|
|
404
|
+
"remove": _handle_remove_rules,
|
|
405
|
+
"get": _handle_get_rules,
|
|
406
|
+
"analyze": _handle_analyze_rules, # 默认行为
|
|
407
|
+
"help": _handle_help,
|
|
408
|
+
"commit": _handle_commit_rules, # 添加新的命令处理函数
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
def handle_rules_command(command_args: str, memory: Dict[str, Any], coding_func=None) -> str:
|
|
412
|
+
"""
|
|
413
|
+
Handles the /rules command and its subcommands.
|
|
414
|
+
|
|
415
|
+
Args:
|
|
416
|
+
command_args: The arguments string following the /rules command.
|
|
417
|
+
Example: "analyze code quality", "/list", "/remove *.md"
|
|
418
|
+
memory: The current session memory dictionary.
|
|
419
|
+
|
|
420
|
+
Returns:
|
|
421
|
+
A string response to be displayed to the user.
|
|
422
|
+
"""
|
|
423
|
+
rules_str = command_args.strip()
|
|
424
|
+
# 处理空命令
|
|
425
|
+
if not rules_str:
|
|
426
|
+
return _handle_help(memory, [])
|
|
427
|
+
|
|
428
|
+
# 处理子命令
|
|
429
|
+
if rules_str.startswith("/"):
|
|
430
|
+
# 解析子命令
|
|
431
|
+
parts = rules_str[1:].strip().split(maxsplit=1)
|
|
432
|
+
subcommand = parts[0].lower() if parts else ""
|
|
433
|
+
args = parts[1].split() if len(parts) > 1 else []
|
|
434
|
+
|
|
435
|
+
handler = COMMAND_HANDLERS.get(subcommand)
|
|
436
|
+
if handler:
|
|
437
|
+
try:
|
|
438
|
+
# 仅 analyze 需要 coding_func
|
|
439
|
+
if subcommand == "analyze":
|
|
440
|
+
return handler(memory, args, coding_func=coding_func)
|
|
441
|
+
elif subcommand == "commit":
|
|
442
|
+
return handler(memory, args, coding_func=coding_func)
|
|
443
|
+
else:
|
|
444
|
+
return handler(memory, args)
|
|
445
|
+
except Exception as e:
|
|
446
|
+
message = get_message_with_format("rules_command_error", subcommand=subcommand, error=str(e))
|
|
447
|
+
printer.print_str_in_terminal(message)
|
|
448
|
+
return message
|
|
449
|
+
else:
|
|
450
|
+
message = get_message_with_format("rules_unknown_command", subcommand=subcommand)
|
|
451
|
+
printer.print_str_in_terminal(message)
|
|
452
|
+
return message
|
|
453
|
+
elif rules_str.lower() == "help":
|
|
454
|
+
# 处理无斜杠的 help 命令
|
|
455
|
+
return _handle_help(memory, [])
|
|
456
|
+
else:
|
|
457
|
+
# 将整个字符串作为查询参数传递给 analyze
|
|
458
|
+
return _handle_analyze_rules(memory, [rules_str], coding_func=coding_func)
|
autocoder/chat_auto_coder.py
CHANGED
|
@@ -7,7 +7,7 @@ import argparse
|
|
|
7
7
|
import os
|
|
8
8
|
from prompt_toolkit import PromptSession
|
|
9
9
|
from prompt_toolkit.key_binding import KeyBindings
|
|
10
|
-
from prompt_toolkit.history import
|
|
10
|
+
from prompt_toolkit.history import FileHistory
|
|
11
11
|
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
|
|
12
12
|
from prompt_toolkit.styles import Style
|
|
13
13
|
from autocoder.version import __version__
|
|
@@ -16,13 +16,15 @@ from prompt_toolkit.formatted_text import FormattedText
|
|
|
16
16
|
from prompt_toolkit.completion import Completer, Completion
|
|
17
17
|
from autocoder.plugins import PluginManager
|
|
18
18
|
from autocoder.events.event_manager_singleton import gengerate_event_file_path
|
|
19
|
+
from autocoder.common.global_cancel import global_cancel
|
|
20
|
+
from autocoder.chat.models_command import handle_models_command
|
|
19
21
|
from autocoder.auto_coder_runner import (
|
|
20
22
|
auto_command,
|
|
21
23
|
load_memory,
|
|
22
24
|
save_memory,
|
|
23
|
-
configure,
|
|
24
|
-
manage_models,
|
|
25
|
-
print_conf,
|
|
25
|
+
configure, # Keep configure if it's used elsewhere or by handle_conf_command internally (though we adapted handle_conf_command not to)
|
|
26
|
+
# manage_models, # Removed
|
|
27
|
+
# print_conf, # Removed
|
|
26
28
|
exclude_dirs,
|
|
27
29
|
exclude_files,
|
|
28
30
|
ask,
|
|
@@ -51,7 +53,10 @@ from autocoder.auto_coder_runner import (
|
|
|
51
53
|
summon,
|
|
52
54
|
get_memory,
|
|
53
55
|
active_context,
|
|
56
|
+
rules
|
|
54
57
|
)
|
|
58
|
+
# Ensure the correct import is present
|
|
59
|
+
from autocoder.chat.conf_command import handle_conf_command
|
|
55
60
|
|
|
56
61
|
# Create a global plugin manager
|
|
57
62
|
plugin_manager = PluginManager()
|
|
@@ -377,8 +382,13 @@ def main():
|
|
|
377
382
|
# 创建一个继承Completer的增强补全器
|
|
378
383
|
enhanced_completer = EnhancedCompleter(completer, plugin_manager)
|
|
379
384
|
|
|
385
|
+
# Define the path for the history file
|
|
386
|
+
history_file_path = os.path.join(os.getcwd(), ".auto-coder", "auto-coder.chat", "history", "command_history.txt")
|
|
387
|
+
# Ensure the directory exists
|
|
388
|
+
os.makedirs(os.path.dirname(history_file_path), exist_ok=True)
|
|
389
|
+
|
|
380
390
|
session = PromptSession(
|
|
381
|
-
history=
|
|
391
|
+
history=FileHistory(history_file_path), # Use FileHistory
|
|
382
392
|
auto_suggest=AutoSuggestFromHistory(),
|
|
383
393
|
enable_history_search=False,
|
|
384
394
|
completer=enhanced_completer,
|
|
@@ -482,6 +492,7 @@ def main():
|
|
|
482
492
|
):
|
|
483
493
|
event_file, file_id = gengerate_event_file_path()
|
|
484
494
|
configure(f"event_file:{event_file}")
|
|
495
|
+
global_cancel.register_token(event_file)
|
|
485
496
|
auto_command(user_input)
|
|
486
497
|
|
|
487
498
|
elif memory["mode"] == "voice_input" and not user_input.startswith("/"):
|
|
@@ -508,6 +519,9 @@ def main():
|
|
|
508
519
|
index_query(query)
|
|
509
520
|
|
|
510
521
|
elif user_input.startswith("/index/build"):
|
|
522
|
+
event_file, file_id = gengerate_event_file_path()
|
|
523
|
+
configure(f"event_file:{event_file}")
|
|
524
|
+
global_cancel.register_token(event_file)
|
|
511
525
|
index_build()
|
|
512
526
|
|
|
513
527
|
elif user_input.startswith("/index/export"):
|
|
@@ -523,10 +537,7 @@ def main():
|
|
|
523
537
|
|
|
524
538
|
elif user_input.startswith("/models"):
|
|
525
539
|
query = user_input[len("/models") :].strip()
|
|
526
|
-
|
|
527
|
-
print("Please enter your query.")
|
|
528
|
-
else:
|
|
529
|
-
manage_models(query)
|
|
540
|
+
handle_models_command(query, get_memory())
|
|
530
541
|
|
|
531
542
|
elif user_input.startswith("/mode"):
|
|
532
543
|
conf = user_input[len("/mode") :].strip()
|
|
@@ -538,27 +549,19 @@ def main():
|
|
|
538
549
|
elif user_input.startswith("/conf/export"):
|
|
539
550
|
from autocoder.common.conf_import_export import export_conf
|
|
540
551
|
|
|
541
|
-
export_path = user_input[len("/conf/export") :].strip()
|
|
542
|
-
export_conf(os.getcwd(), export_path)
|
|
543
|
-
|
|
544
|
-
elif user_input.startswith("/conf/import"):
|
|
545
|
-
from autocoder.common.conf_import_export import import_conf
|
|
546
|
-
|
|
547
|
-
import_path = user_input[len("/conf/import") :].strip()
|
|
548
|
-
import_conf(os.getcwd(), import_path)
|
|
549
|
-
|
|
550
552
|
elif user_input.startswith("/plugins"):
|
|
551
553
|
# 提取命令参数并交由 plugin_manager 处理
|
|
552
554
|
args = user_input[len("/plugins") :].strip().split()
|
|
553
555
|
result = plugin_manager.handle_plugins_command(args)
|
|
554
556
|
print(result, end="")
|
|
555
557
|
|
|
558
|
+
# Handle /conf and its subcommands like /conf /export, /conf /import
|
|
556
559
|
elif user_input.startswith("/conf"):
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
560
|
+
# Extract everything after "/conf"
|
|
561
|
+
command_args = user_input[len("/conf"):].strip()
|
|
562
|
+
# Call the handler from conf_command.py and print its string result
|
|
563
|
+
result_message = handle_conf_command(command_args, memory)
|
|
564
|
+
print(result_message)
|
|
562
565
|
elif user_input.startswith("/revert"):
|
|
563
566
|
revert()
|
|
564
567
|
elif user_input.startswith("/commit"):
|
|
@@ -592,14 +595,16 @@ def main():
|
|
|
592
595
|
elif user_input.startswith("/coding"):
|
|
593
596
|
event_file, file_id = gengerate_event_file_path()
|
|
594
597
|
configure(f"event_file:{event_file}")
|
|
598
|
+
global_cancel.register_token(event_file)
|
|
595
599
|
query = user_input[len("/coding") :].strip()
|
|
596
600
|
if not query:
|
|
597
601
|
print("\033[91mPlease enter your request.\033[0m")
|
|
598
602
|
continue
|
|
599
|
-
coding(query)
|
|
603
|
+
coding(query)
|
|
600
604
|
elif user_input.startswith("/chat"):
|
|
601
605
|
event_file, file_id = gengerate_event_file_path()
|
|
602
606
|
configure(f"event_file:{event_file}")
|
|
607
|
+
global_cancel.register_token(event_file)
|
|
603
608
|
query = user_input[len("/chat") :].strip()
|
|
604
609
|
if not query:
|
|
605
610
|
print("\033[91mPlease enter your request.\033[0m")
|
|
@@ -624,6 +629,10 @@ def main():
|
|
|
624
629
|
args = user_input[len("/lib") :].strip().split()
|
|
625
630
|
lib_command(args)
|
|
626
631
|
|
|
632
|
+
elif user_input.startswith("/rules"):
|
|
633
|
+
query = user_input[len("/rules") :].strip()
|
|
634
|
+
rules(query)
|
|
635
|
+
|
|
627
636
|
elif user_input.startswith("/mcp"):
|
|
628
637
|
query = user_input[len("/mcp") :].strip()
|
|
629
638
|
if not query:
|
|
@@ -656,6 +665,7 @@ def main():
|
|
|
656
665
|
else:
|
|
657
666
|
if command.startswith("/chat"):
|
|
658
667
|
event_file, file_id = gengerate_event_file_path()
|
|
668
|
+
global_cancel.register_token(event_file)
|
|
659
669
|
configure(f"event_file:{event_file}")
|
|
660
670
|
command = command[len("/chat") :].strip()
|
|
661
671
|
gen_and_exec_shell_command(command)
|