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

Files changed (66) hide show
  1. auto_coder-0.1.399.dist-info/METADATA +396 -0
  2. {auto_coder-0.1.398.dist-info → auto_coder-0.1.399.dist-info}/RECORD +62 -28
  3. {auto_coder-0.1.398.dist-info → auto_coder-0.1.399.dist-info}/WHEEL +1 -1
  4. {auto_coder-0.1.398.dist-info → auto_coder-0.1.399.dist-info}/entry_points.txt +2 -0
  5. autocoder/agent/base_agentic/base_agent.py +2 -2
  6. autocoder/agent/base_agentic/tools/replace_in_file_tool_resolver.py +1 -1
  7. autocoder/agent/entry_command_agent/__init__.py +29 -0
  8. autocoder/agent/entry_command_agent/auto_tool.py +61 -0
  9. autocoder/agent/entry_command_agent/chat.py +475 -0
  10. autocoder/agent/entry_command_agent/designer.py +53 -0
  11. autocoder/agent/entry_command_agent/generate_command.py +50 -0
  12. autocoder/agent/entry_command_agent/project_reader.py +58 -0
  13. autocoder/agent/entry_command_agent/voice2text.py +71 -0
  14. autocoder/auto_coder.py +23 -548
  15. autocoder/auto_coder_runner.py +510 -8
  16. autocoder/chat/rules_command.py +1 -1
  17. autocoder/chat_auto_coder.py +6 -1
  18. autocoder/common/ac_style_command_parser/__init__.py +15 -0
  19. autocoder/common/ac_style_command_parser/example.py +7 -0
  20. autocoder/{command_parser.py → common/ac_style_command_parser/parser.py} +1 -33
  21. autocoder/common/ac_style_command_parser/test_parser.py +516 -0
  22. autocoder/common/command_completer_v2.py +1 -1
  23. autocoder/common/command_file_manager/examples.py +22 -8
  24. autocoder/common/command_file_manager/manager.py +37 -6
  25. autocoder/common/conversations/get_conversation_manager.py +143 -0
  26. autocoder/common/conversations/manager.py +122 -11
  27. autocoder/common/conversations/storage/index_manager.py +89 -0
  28. autocoder/common/v2/agent/agentic_edit.py +131 -18
  29. autocoder/common/v2/agent/agentic_edit_types.py +10 -0
  30. autocoder/common/v2/code_auto_generate_editblock.py +10 -2
  31. autocoder/dispacher/__init__.py +10 -0
  32. autocoder/rags.py +0 -27
  33. autocoder/run_context.py +1 -0
  34. autocoder/sdk/__init__.py +188 -0
  35. autocoder/sdk/cli/__init__.py +15 -0
  36. autocoder/sdk/cli/__main__.py +26 -0
  37. autocoder/sdk/cli/completion_wrapper.py +38 -0
  38. autocoder/sdk/cli/formatters.py +211 -0
  39. autocoder/sdk/cli/handlers.py +174 -0
  40. autocoder/sdk/cli/install_completion.py +301 -0
  41. autocoder/sdk/cli/main.py +284 -0
  42. autocoder/sdk/cli/options.py +72 -0
  43. autocoder/sdk/constants.py +102 -0
  44. autocoder/sdk/core/__init__.py +20 -0
  45. autocoder/sdk/core/auto_coder_core.py +867 -0
  46. autocoder/sdk/core/bridge.py +497 -0
  47. autocoder/sdk/example.py +0 -0
  48. autocoder/sdk/exceptions.py +72 -0
  49. autocoder/sdk/models/__init__.py +19 -0
  50. autocoder/sdk/models/messages.py +209 -0
  51. autocoder/sdk/models/options.py +194 -0
  52. autocoder/sdk/models/responses.py +311 -0
  53. autocoder/sdk/session/__init__.py +32 -0
  54. autocoder/sdk/session/session.py +106 -0
  55. autocoder/sdk/session/session_manager.py +56 -0
  56. autocoder/sdk/utils/__init__.py +24 -0
  57. autocoder/sdk/utils/formatters.py +216 -0
  58. autocoder/sdk/utils/io_utils.py +302 -0
  59. autocoder/sdk/utils/validators.py +287 -0
  60. autocoder/version.py +2 -1
  61. auto_coder-0.1.398.dist-info/METADATA +0 -111
  62. autocoder/common/conversations/compatibility.py +0 -303
  63. autocoder/common/conversations/conversation_manager.py +0 -502
  64. autocoder/common/conversations/example.py +0 -152
  65. {auto_coder-0.1.398.dist-info → auto_coder-0.1.399.dist-info/licenses}/LICENSE +0 -0
  66. {auto_coder-0.1.398.dist-info → auto_coder-0.1.399.dist-info}/top_level.txt +0 -0
@@ -12,6 +12,7 @@ import io
12
12
  import uuid
13
13
  import glob
14
14
  import time
15
+ import datetime
15
16
  import hashlib
16
17
  from contextlib import contextmanager
17
18
  from typing import List, Dict, Any, Optional
@@ -24,6 +25,7 @@ from autocoder.auto_coder import main as auto_coder_main
24
25
  from autocoder.utils import get_last_yaml_file
25
26
  from autocoder.commands.auto_command import CommandAutoTuner, AutoCommandRequest, CommandConfig, MemoryConfig
26
27
  from autocoder.common.v2.agent.agentic_edit import AgenticEdit,AgenticEditRequest
28
+ from autocoder.common.v2.agent.agentic_edit_types import AgenticEditConversationConfig
27
29
  from autocoder.index.symbols_utils import (
28
30
  extract_symbols,
29
31
  SymbolType,
@@ -60,12 +62,13 @@ from autocoder.utils.thread_utils import run_in_raw_thread
60
62
  from autocoder.memory.active_context_manager import ActiveContextManager
61
63
  from autocoder.common.command_completer import CommandCompleter,FileSystemModel as CCFileSystemModel,MemoryConfig as CCMemoryModel
62
64
  from autocoder.common.conf_validator import ConfigValidator
63
- from autocoder import command_parser as CommandParser
65
+ from autocoder.common.ac_style_command_parser import parse_query
64
66
  from loguru import logger as global_logger
65
67
  from autocoder.utils.project_structure import EnhancedFileAnalyzer
66
68
  from autocoder.common import SourceCodeList,SourceCode
67
69
  from autocoder.common.file_monitor import FileMonitor
68
70
  from filelock import FileLock
71
+ from autocoder.common.command_file_manager import CommandManager
69
72
 
70
73
 
71
74
  ## 对外API,用于第三方集成 auto-coder 使用。
@@ -193,7 +196,7 @@ def configure_project_type():
193
196
 
194
197
  if project_type:
195
198
  configure(f"project_type:{project_type}", skip_print=True)
196
- configure("skip_build_index:false", skip_print=True)
199
+ configure("skip_build_index:true", skip_print=True)
197
200
  print_info(f"\n{get_message('project_type_set')} {project_type}")
198
201
  else:
199
202
  print_info(f"\n{get_message('using_default_type')}")
@@ -281,6 +284,16 @@ def start():
281
284
  configure_logger()
282
285
  init_singleton_instances()
283
286
 
287
+ # conversation_manager = get_conversation_manager()
288
+ # if not conversation_manager.get_current_conversation():
289
+ # # Format: yyyyMMdd-MM-ss-uuid
290
+ # current_time = datetime.datetime.now()
291
+ # time_str = current_time.strftime("%Y%m%d-%H-%M-%S")
292
+ # name = f"{time_str}-{str(uuid.uuid4())}"
293
+ # conversation_id = conversation_manager.create_new_conversation(name=name,description="")
294
+ # conversation_manager.set_current_conversation(conversation_id)
295
+
296
+
284
297
  def stop():
285
298
  try:
286
299
  FileMonitor(project_root).stop()
@@ -737,6 +750,266 @@ def revert():
737
750
  }})
738
751
 
739
752
 
753
+ def add_files(args: List[str]):
754
+
755
+ result_manager = ResultManager()
756
+ if "groups" not in memory["current_files"]:
757
+ memory["current_files"]["groups"] = {}
758
+ if "groups_info" not in memory["current_files"]:
759
+ memory["current_files"]["groups_info"] = {}
760
+ if "current_groups" not in memory["current_files"]:
761
+ memory["current_files"]["current_groups"] = []
762
+ groups = memory["current_files"]["groups"]
763
+ groups_info = memory["current_files"]["groups_info"]
764
+
765
+ console = Console()
766
+ printer = Printer()
767
+
768
+ if not args:
769
+ printer.print_in_terminal("add_files_no_args", style="red")
770
+ result_manager.append(content=printer.get_message_from_key("add_files_no_args"),
771
+ meta={"action": "add_files","success":False, "input":{ "args": args}})
772
+ return
773
+
774
+ if args[0] == "/refresh":
775
+ completer.refresh_files()
776
+
777
+
778
+ def _handle_post_commit_and_pr(post_commit: bool, pr: bool, query: str, args, llm):
779
+ """
780
+ 处理 post_commit 和 PR 功能
781
+
782
+ Args:
783
+ post_commit: 是否执行 post_commit
784
+ pr: 是否创建 PR
785
+ query: 原始查询
786
+ args: 配置参数
787
+ llm: LLM 实例
788
+ """
789
+ printer = Printer()
790
+ console = Console()
791
+
792
+ try:
793
+ if post_commit:
794
+ # 执行 post_commit 操作
795
+ printer.print_in_terminal("post_commit_executing", style="blue")
796
+
797
+ # 检查是否有未提交的更改
798
+ uncommitted_changes = git_utils.get_uncommitted_changes(".")
799
+ if uncommitted_changes:
800
+ # 生成提交消息
801
+ commit_message = git_utils.generate_commit_message.with_llm(llm).run(
802
+ uncommitted_changes
803
+ )
804
+
805
+ # 执行提交
806
+ commit_result = git_utils.commit_changes(".", commit_message)
807
+ git_utils.print_commit_info(commit_result=commit_result)
808
+ printer.print_in_terminal("post_commit_success", style="green", message=commit_message)
809
+
810
+ # 如果需要创建 PR,则继续处理
811
+ if pr:
812
+ _create_pull_request(commit_result, query, llm)
813
+ else:
814
+ printer.print_in_terminal("post_commit_no_changes", style="yellow")
815
+
816
+ elif pr:
817
+ # 只创建 PR,不执行 post_commit
818
+ # 获取最后一个 commit
819
+ try:
820
+ repo = git.Repo(".")
821
+ last_commit = repo.head.commit
822
+
823
+ # 创建一个模拟的 commit_result 对象
824
+ class MockCommitResult:
825
+ def __init__(self, commit):
826
+ self.commit_hash = commit.hexsha
827
+ self.commit_message = commit.message.strip()
828
+ self.changed_files = []
829
+
830
+ mock_commit_result = MockCommitResult(last_commit)
831
+ _create_pull_request(mock_commit_result, query, llm)
832
+
833
+ except Exception as e:
834
+ printer.print_in_terminal("pr_get_last_commit_failed", style="red", error=str(e))
835
+
836
+ except Exception as e:
837
+ printer.print_in_terminal("post_commit_pr_failed", style="red", error=str(e))
838
+
839
+
840
+ def _create_pull_request(commit_result, original_query: str, llm):
841
+ """
842
+ 创建 Pull Request
843
+
844
+ Args:
845
+ commit_result: 提交结果对象
846
+ original_query: 原始查询
847
+ llm: LLM 实例
848
+ """
849
+ printer = Printer()
850
+ console = Console()
851
+
852
+ try:
853
+ # 检查是否安装了 gh CLI
854
+ gh_check = subprocess.run(["gh", "--version"], capture_output=True, text=True)
855
+ if gh_check.returncode != 0:
856
+ printer.print_in_terminal("pr_gh_not_installed", style="red")
857
+ return
858
+
859
+ # 检查是否已经登录 GitHub
860
+ auth_check = subprocess.run(["gh", "auth", "status"], capture_output=True, text=True)
861
+ if auth_check.returncode != 0:
862
+ printer.print_in_terminal("pr_gh_not_authenticated", style="red")
863
+ return
864
+
865
+ # 获取当前分支名
866
+ repo = git.Repo(".")
867
+ current_branch = repo.active_branch.name
868
+
869
+ # 如果在 main/master 分支,创建新分支
870
+ if current_branch in ["main", "master"]:
871
+ # 生成新分支名
872
+ import re
873
+ branch_name = re.sub(r'[^a-zA-Z0-9\-_]', '-', original_query.lower())
874
+ branch_name = f"auto-coder-{branch_name[:30]}-{int(time.time())}"
875
+
876
+ # 创建并切换到新分支
877
+ new_branch = repo.create_head(branch_name)
878
+ new_branch.checkout()
879
+ current_branch = branch_name
880
+
881
+ printer.print_in_terminal("pr_created_branch", style="blue", branch=branch_name)
882
+
883
+ # 推送当前分支到远程
884
+ try:
885
+ origin = repo.remotes.origin
886
+ origin.push(current_branch)
887
+ printer.print_in_terminal("pr_pushed_branch", style="blue", branch=current_branch)
888
+ except Exception as e:
889
+ printer.print_in_terminal("pr_push_failed", style="red", error=str(e))
890
+ return
891
+
892
+ # 生成 PR 标题和描述
893
+ pr_title, pr_body = _generate_pr_content(commit_result, original_query, llm)
894
+
895
+ # 创建 PR
896
+ pr_cmd = [
897
+ "gh", "pr", "create",
898
+ "--title", pr_title,
899
+ "--body", pr_body,
900
+ "--head", current_branch
901
+ ]
902
+
903
+ pr_result = subprocess.run(pr_cmd, capture_output=True, text=True)
904
+
905
+ if pr_result.returncode == 0:
906
+ pr_url = pr_result.stdout.strip()
907
+ printer.print_in_terminal("pr_created_success", style="green", url=pr_url)
908
+
909
+ # 显示 PR 信息
910
+ console.print(Panel(
911
+ f"[bold green]Pull Request Created Successfully![/bold green]\n\n"
912
+ f"[bold]Title:[/bold] {pr_title}\n"
913
+ f"[bold]URL:[/bold] {pr_url}\n"
914
+ f"[bold]Branch:[/bold] {current_branch}",
915
+ title="🎉 Pull Request",
916
+ border_style="green"
917
+ ))
918
+ else:
919
+ printer.print_in_terminal("pr_creation_failed", style="red", error=pr_result.stderr)
920
+
921
+ except Exception as e:
922
+ printer.print_in_terminal("pr_creation_error", style="red", error=str(e))
923
+
924
+
925
+ @byzerllm.prompt()
926
+ def _generate_pr_content(commit_result, original_query: str, llm) -> tuple:
927
+ """
928
+ 生成 PR 标题和描述
929
+
930
+ 根据提交信息和原始查询生成合适的 PR 标题和描述。
931
+
932
+ Args:
933
+ commit_result: 提交结果,包含 commit_message 和 changed_files
934
+ original_query: 用户的原始查询请求
935
+
936
+ Returns:
937
+ tuple: (pr_title, pr_body) PR标题和描述内容
938
+
939
+ 请生成简洁明了的 PR 标题(不超过72字符)和详细的描述内容。
940
+ 标题应该概括主要变更,描述应该包含:
941
+ 1. 变更的背景和目的
942
+ 2. 主要修改内容
943
+ 3. 影响的文件(如果有的话)
944
+
945
+ 提交信息:{{ commit_result.commit_message }}
946
+ 原始需求:{{ original_query }}
947
+ {% if commit_result.changed_files %}
948
+ 修改的文件:
949
+ {% for file in commit_result.changed_files %}
950
+ - {{ file }}
951
+ {% endfor %}
952
+ {% endif %}
953
+ """
954
+
955
+ # 这个函数会被 byzerllm 装饰器处理,返回 LLM 生成的内容
956
+ # 实际实现会在运行时由装饰器处理
957
+ pass
958
+
959
+
960
+ # 实际的 PR 内容生成函数
961
+ def _generate_pr_content(commit_result, original_query: str, llm):
962
+ """
963
+ 生成 PR 标题和描述的实际实现
964
+ """
965
+ try:
966
+ # 使用 LLM 生成 PR 内容
967
+ prompt = f"""
968
+ 根据以下信息生成 Pull Request 的标题和描述:
969
+
970
+ 提交信息:{getattr(commit_result, 'commit_message', 'Auto-generated commit')}
971
+ 原始需求:{original_query}
972
+ 修改的文件:{getattr(commit_result, 'changed_files', [])}
973
+
974
+ 请生成:
975
+ 1. 简洁的 PR 标题(不超过72字符)
976
+ 2. 详细的 PR 描述,包含变更背景、主要修改内容等
977
+
978
+ 格式要求:
979
+ TITLE: [标题内容]
980
+ BODY: [描述内容]
981
+ """
982
+
983
+ response = llm.chat([{"role": "user", "content": prompt}])
984
+
985
+ # 解析响应
986
+ lines = response.split('\n')
987
+ title = ""
988
+ body = ""
989
+
990
+ for line in lines:
991
+ if line.startswith("TITLE:"):
992
+ title = line.replace("TITLE:", "").strip()
993
+ elif line.startswith("BODY:"):
994
+ body = line.replace("BODY:", "").strip()
995
+ elif body: # 如果已经开始收集 body,继续添加后续行
996
+ body += "\n" + line
997
+
998
+ # 如果解析失败,使用默认值
999
+ if not title:
1000
+ title = f"Auto-coder: {original_query[:50]}..."
1001
+ if not body:
1002
+ body = f"This PR was automatically generated by Auto-coder.\n\nOriginal request: {original_query}"
1003
+
1004
+ return title, body
1005
+
1006
+ except Exception as e:
1007
+ # 如果 LLM 生成失败,使用默认值
1008
+ title = f"Auto-coder: {original_query[:50]}..."
1009
+ body = f"This PR was automatically generated by Auto-coder.\n\nOriginal request: {original_query}\n\nCommit: {getattr(commit_result, 'commit_message', 'Auto-generated commit')}"
1010
+ return title, body
1011
+
1012
+
740
1013
  def add_files(args: List[str]):
741
1014
 
742
1015
  result_manager = ResultManager()
@@ -1704,7 +1977,7 @@ def chat(query: str):
1704
1977
  yaml_config["emb_model"] = conf["emb_model"]
1705
1978
 
1706
1979
  # 解析命令
1707
- commands_infos = CommandParser.parse_query(query)
1980
+ commands_infos = parse_query(query)
1708
1981
  if len(commands_infos) > 0:
1709
1982
  if "query" in commands_infos:
1710
1983
  query = " ".join(commands_infos["query"]["args"])
@@ -1866,7 +2139,7 @@ def active_context(query: str):
1866
2139
  query: 命令参数,例如 "list" 列出所有任务
1867
2140
  """
1868
2141
  # 解析命令
1869
- commands_infos = CommandParser.parse_query(query)
2142
+ commands_infos = parse_query(query)
1870
2143
  command = "list" # 默认命令是列出所有任务
1871
2144
 
1872
2145
  if len(commands_infos) > 0:
@@ -2918,16 +3191,55 @@ def auto_command(query: str,extra_args: Dict[str,Any]={}):
2918
3191
 
2919
3192
  llm = get_single_llm(args.code_model or args.model,product_mode=args.product_mode)
2920
3193
  conversation_history = extra_args.get("conversations",[])
3194
+
3195
+ command_infos = parse_query(query)
3196
+
3197
+ # terminal 的总是接着上次对话, 所以这里总是设置为 resume
3198
+ conversation_config = AgenticEditConversationConfig(
3199
+ action="resume"
3200
+ )
3201
+
3202
+ ## web 模式会自己管理对话,所以这里总是设置为新对话
3203
+ if get_run_context().mode == RunMode.WEB:
3204
+ command_infos = {
3205
+ "new":{
3206
+ "args":[query]
3207
+ }
3208
+ }
3209
+
3210
+ task_query = query
3211
+
3212
+ if "new" in command_infos:
3213
+ conversation_config.action = "new"
3214
+ task_query = " ".join(command_infos["new"]["args"])
3215
+
3216
+ if "resume" in command_infos:
3217
+ conversation_config.action = "resume"
3218
+ conversation_config.conversation_id = command_infos["resume"]["args"][0]
3219
+ task_query = " ".join(command_infos["resume"]["args"][1:])
3220
+
3221
+ if "list" in command_infos:
3222
+ conversation_config.action = "list"
3223
+
3224
+ if "command" in command_infos:
3225
+ conversation_config.action = "command"
3226
+ task_query = render_command_file_with_variables(command_infos)
3227
+
3228
+
3229
+ conversation_config.query = task_query
3230
+
2921
3231
  agent = AgenticEdit(llm=llm,args=args,files=SourceCodeList(sources=sources),
2922
3232
  conversation_history=conversation_history,
2923
3233
  memory_config=MemoryConfig(memory=memory,
2924
3234
  save_memory_func=save_memory), command_config=CommandConfig,
2925
- conversation_name="current"
3235
+ conversation_name="current",
3236
+ conversation_config=conversation_config
2926
3237
  )
2927
3238
  if get_run_context().mode == RunMode.WEB:
2928
- agent.run_with_events(AgenticEditRequest(user_input=query))
2929
- else:
2930
- agent.run_in_terminal(AgenticEditRequest(user_input=query))
3239
+ agent.run_with_events(AgenticEditRequest(user_input=task_query))
3240
+
3241
+ if get_run_context().mode == RunMode.TERMINAL:
3242
+ agent.run_in_terminal(AgenticEditRequest(user_input=task_query))
2931
3243
 
2932
3244
  completer.refresh_files()
2933
3245
  return
@@ -2983,3 +3295,193 @@ def auto_command(query: str,extra_args: Dict[str,Any]={}):
2983
3295
  padding=(1, 2)
2984
3296
  ))
2985
3297
  completer.refresh_files()
3298
+
3299
+
3300
+
3301
+ def run_auto_command(query: str,
3302
+ pre_commit:bool=False,
3303
+ post_commit:bool=False,
3304
+ pr:bool=False,
3305
+ extra_args: Dict[str,Any]={}
3306
+ ):
3307
+ """处理/auto指令"""
3308
+ args = get_final_config()
3309
+ memory = get_memory()
3310
+ if args.enable_agentic_edit:
3311
+ from autocoder.run_context import get_run_context,RunMode
3312
+ execute_file,args = generate_new_yaml(query)
3313
+ args.file = execute_file
3314
+ current_files = memory.get("current_files",{}).get("files",[])
3315
+ sources = []
3316
+ for file in current_files:
3317
+ try:
3318
+ with open(file,"r",encoding="utf-8") as f:
3319
+ sources.append(SourceCode(module_name=file,source_code=f.read()))
3320
+ except Exception as e:
3321
+ global_logger.error(f"Failed to read file {file}: {e}")
3322
+
3323
+ llm = get_single_llm(args.code_model or args.model,product_mode=args.product_mode)
3324
+ conversation_history = extra_args.get("conversations",[])
3325
+
3326
+ command_infos = parse_query(query)
3327
+
3328
+ # terminal 的总是接着上次对话, 所以这里总是设置为 resume
3329
+ conversation_config = AgenticEditConversationConfig(
3330
+ action="resume"
3331
+ )
3332
+
3333
+ ## web 模式会自己管理对话,所以这里总是设置为新对话
3334
+ if get_run_context().mode == RunMode.WEB:
3335
+ command_infos = {
3336
+ "new":{
3337
+ "args":[query]
3338
+ }
3339
+ }
3340
+
3341
+ task_query = query
3342
+
3343
+ if "new" in command_infos:
3344
+ conversation_config.action = "new"
3345
+ task_query = " ".join(command_infos["new"]["args"])
3346
+
3347
+ if "resume" in command_infos:
3348
+ conversation_config.action = "resume"
3349
+ conversation_config.conversation_id = command_infos["resume"]["args"][0]
3350
+ task_query = " ".join(command_infos["resume"]["args"][1:])
3351
+
3352
+ if "list" in command_infos:
3353
+ conversation_config.action = "list"
3354
+
3355
+
3356
+ if "command" in command_infos:
3357
+ conversation_config.action = "command"
3358
+ task_query = render_command_file_with_variables(command_infos)
3359
+
3360
+ conversation_config.query = task_query
3361
+
3362
+ agent = AgenticEdit(llm=llm,args=args,files=SourceCodeList(sources=sources),
3363
+ conversation_history=conversation_history,
3364
+ memory_config=MemoryConfig(memory=memory,
3365
+ save_memory_func=save_memory), command_config=CommandConfig,
3366
+ conversation_name="current",
3367
+ conversation_config=conversation_config
3368
+ )
3369
+ if pre_commit:
3370
+ agent.apply_pre_changes()
3371
+
3372
+ events = agent.run(AgenticEditRequest(user_input=task_query))
3373
+
3374
+ for event in events:
3375
+ yield event
3376
+
3377
+ # 处理 post_commit 和 PR 功能
3378
+ if post_commit or pr:
3379
+ _handle_post_commit_and_pr(post_commit, pr, query, args, llm)
3380
+
3381
+ completer.refresh_files()
3382
+ return
3383
+
3384
+ args = get_final_config()
3385
+ # 准备请求参数
3386
+ request = AutoCommandRequest(
3387
+ user_input=query
3388
+ )
3389
+
3390
+ # 初始化调优器
3391
+ llm = get_single_llm(args.chat_model or args.model,product_mode=args.product_mode)
3392
+ tuner = CommandAutoTuner(llm,
3393
+ args=args,
3394
+ memory_config=MemoryConfig(memory=memory, save_memory_func=save_memory),
3395
+ command_config=CommandConfig(
3396
+ add_files=add_files,
3397
+ remove_files=remove_files,
3398
+ list_files=list_files,
3399
+ conf=configure,
3400
+ revert=revert,
3401
+ commit=commit,
3402
+ help=help,
3403
+ exclude_dirs=exclude_dirs,
3404
+ exclude_files=exclude_files,
3405
+ ask=ask,
3406
+ chat=chat,
3407
+ coding=coding,
3408
+ design=design,
3409
+ summon=summon,
3410
+ lib=lib_command,
3411
+ mcp=mcp,
3412
+ models=manage_models,
3413
+ index_build=index_build,
3414
+ index_query=index_query,
3415
+ execute_shell_command=execute_shell_command,
3416
+ generate_shell_command=generate_shell_command,
3417
+ conf_export=conf_export,
3418
+ conf_import=conf_import,
3419
+ index_export=index_export,
3420
+ index_import=index_import
3421
+ ))
3422
+
3423
+ # 生成建议
3424
+ response = tuner.analyze(request)
3425
+ printer = Printer()
3426
+ # 显示建议
3427
+ console = Console()
3428
+ console.print(Panel(
3429
+ Markdown(response.reasoning or ""),
3430
+ title=printer.get_message_from_key_with_format("auto_command_reasoning_title"),
3431
+ border_style="blue",
3432
+ padding=(1, 2)
3433
+ ))
3434
+
3435
+ # 处理 post_commit 和 PR 功能
3436
+ if post_commit or pr:
3437
+ _handle_post_commit_and_pr(post_commit, pr, query, args, llm)
3438
+
3439
+ completer.refresh_files()
3440
+
3441
+
3442
+ def render_command_file_with_variables(command_infos: Dict[str, Any]) -> str:
3443
+ """
3444
+ 使用 CommandManager 加载并渲染命令文件
3445
+
3446
+ Args:
3447
+ command_infos: parse_query(query) 的返回结果,包含命令和参数信息
3448
+
3449
+ Returns:
3450
+ str: 渲染后的文件内容
3451
+
3452
+ Raises:
3453
+ ValueError: 当参数不足或文件不存在时
3454
+ Exception: 当渲染过程出现错误时
3455
+ """
3456
+ try:
3457
+ # 获取第一个命令的信息
3458
+ if not command_infos:
3459
+ raise ValueError("command_infos 为空,无法获取命令信息")
3460
+
3461
+ # command 的位置参数作为路径
3462
+ first_command = command_infos["command"]
3463
+
3464
+ # 获取位置参数(文件路径)
3465
+ args = first_command.get("args", [])
3466
+ if not args:
3467
+ raise ValueError("未提供文件路径参数")
3468
+
3469
+ file_path = args[0] # 第一个位置参数作为文件路径
3470
+
3471
+ # 获取关键字参数作为渲染参数
3472
+ kwargs = first_command.get("kwargs", {})
3473
+
3474
+ # 初始化 CommandManager
3475
+ command_manager = CommandManager()
3476
+
3477
+ # 使用 read_command_file_with_render 直接读取并渲染命令文件
3478
+ rendered_content = command_manager.read_command_file_with_render(file_path, kwargs)
3479
+ if rendered_content is None:
3480
+ raise ValueError(f"无法读取或渲染命令文件: {file_path}")
3481
+
3482
+ global_logger.info(f"成功渲染命令文件: {file_path}, 使用参数: {kwargs}")
3483
+ return rendered_content
3484
+
3485
+ except Exception as e:
3486
+ global_logger.error(f"render_command_file_with_variables 执行失败: {str(e)}")
3487
+ raise
@@ -17,7 +17,7 @@ from autocoder.auto_coder_runner import get_final_config, get_single_llm
17
17
  from autocoder.chat_auto_coder_lang import get_message, get_message_with_format
18
18
  from autocoder.rag.token_counter import count_tokens
19
19
  from autocoder.common.printer import Printer
20
- from autocoder.command_parser import CommandParser
20
+ from autocoder.common.ac_style_command_parser import CommandParser
21
21
  from loguru import logger
22
22
 
23
23
  printer = Printer()
@@ -18,6 +18,11 @@ from autocoder.plugins import PluginManager
18
18
  from autocoder.events.event_manager_singleton import gengerate_event_file_path
19
19
  from autocoder.common.global_cancel import global_cancel
20
20
  from autocoder.chat.models_command import handle_models_command
21
+ from autocoder.common.conversations.get_conversation_manager import (
22
+ get_conversation_manager,
23
+ get_conversation_manager_config,
24
+ reset_conversation_manager
25
+ )
21
26
  from autocoder.auto_coder_runner import (
22
27
  auto_command,
23
28
  configure, # Keep configure if it's used elsewhere or by handle_conf_command internally (though we adapted handle_conf_command not to)
@@ -666,7 +671,7 @@ def main():
666
671
  else:
667
672
  execute_shell_command(command)
668
673
  else:
669
- if user_input:
674
+ if user_input and user_input.strip():
670
675
  auto_command(user_input)
671
676
 
672
677
  except KeyboardInterrupt:
@@ -0,0 +1,15 @@
1
+ from .parser import (
2
+ CommandParser,
3
+ parse_query,
4
+ has_command,
5
+ get_command_args,
6
+ get_command_kwargs
7
+ )
8
+
9
+ __all__ = [
10
+ "CommandParser",
11
+ "parse_query",
12
+ "has_command",
13
+ "get_command_args",
14
+ "get_command_kwargs"
15
+ ]
@@ -0,0 +1,7 @@
1
+ from autocoder.common.ac_style_command_parser.parser import CommandParser
2
+
3
+ p = CommandParser()
4
+
5
+ result = p.parse('/command "tdd/hello.md" name="威廉"')
6
+
7
+ print(result)
@@ -1,5 +1,5 @@
1
- from typing import Dict, List, Tuple, Any, Optional
2
1
  import re
2
+ from typing import Dict, List, Tuple, Any, Optional
3
3
 
4
4
 
5
5
  class CommandParser:
@@ -246,35 +246,3 @@ def get_command_kwargs(query: str, command: str) -> Dict[str, str]:
246
246
  if command_info:
247
247
  return command_info['kwargs']
248
248
  return {}
249
-
250
-
251
- # 示例用法
252
- if __name__ == "__main__":
253
- # 测试各种格式的查询
254
- test_queries = [
255
- "/learn hello world /commit 123456",
256
- "/learn /commit 123456",
257
- "/learn /commit commit_id=123456",
258
- "/learn msg=hello /commit commit_id=123456",
259
- "/learn hello key=value /commit",
260
- # 带引号的值
261
- '/learn msg="hello world" /commit message="Fix bug #123"',
262
- "/learn 'quoted arg' key='value with spaces' /commit",
263
- # 路径参数测试
264
- "/learn /path/to/file.txt",
265
- "/commit message='Added /path/to/file.txt'",
266
- "Check /path/to/file.txt and also /another/path/file.md",
267
- "/clone /path/to/repo /checkout branch",
268
- "Use the file at /usr/local/bin/python with /learn"
269
- ]
270
-
271
- for query in test_queries:
272
- print(f"\nQuery: {query}")
273
- result = parse_query(query)
274
- print(f"Parsed: {result}")
275
-
276
- if has_command(query, "commit"):
277
- args = get_command_args(query, "commit")
278
- kwargs = get_command_kwargs(query, "commit")
279
- print(f"Commit args: {args}")
280
- print(f"Commit kwargs: {kwargs}")