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

@@ -22,6 +22,7 @@ from prompt_toolkit.completion import WordCompleter, Completer, Completion
22
22
  from prompt_toolkit.shortcuts import confirm
23
23
  from autocoder.common import AutoCoderArgs
24
24
  from pydantic import Field, BaseModel
25
+ from autocoder.common.result_manager import ResultManager
25
26
  from autocoder.version import __version__
26
27
  from autocoder.auto_coder import main as auto_coder_main
27
28
  from autocoder.common.command_completer import CommandTextParser
@@ -55,6 +56,7 @@ from autocoder.utils.llms import get_single_llm
55
56
  import pkg_resources
56
57
  from autocoder.common.printer import Printer
57
58
  from autocoder.utils.thread_utils import run_in_thread,run_in_raw_thread
59
+ from autocoder.common.command_completer import CommandCompleter,FileSystemModel as CCFileSystemModel,MemoryConfig as CCMemoryModel
58
60
 
59
61
  class SymbolItem(BaseModel):
60
62
  symbol_name: str
@@ -83,8 +85,8 @@ def parse_arguments():
83
85
  parser.add_argument(
84
86
  "--product_mode",
85
87
  type=str,
86
- default="pro",
87
- help="The mode of the auto-coder.chat, lite/pro default is pro",
88
+ default="lite",
89
+ help="The mode of the auto-coder.chat, lite/pro default is lite",
88
90
  )
89
91
 
90
92
  parser.add_argument("--lite", action="store_true", help="Lite mode")
@@ -138,6 +140,7 @@ commands = [
138
140
  "/design",
139
141
  "/mcp",
140
142
  "/models",
143
+ "/auto",
141
144
  ]
142
145
 
143
146
 
@@ -630,384 +633,6 @@ def get_symbol_list() -> List[SymbolItem]:
630
633
  return list_of_symbols
631
634
 
632
635
 
633
- class CommandCompleter(Completer):
634
- def __init__(self, commands):
635
- self.commands = commands
636
- self.all_file_names = get_all_file_names_in_project()
637
- self.all_files = get_all_file_in_project()
638
- self.all_dir_names = get_all_dir_names_in_project()
639
- self.all_files_with_dot = get_all_file_in_project_with_dot()
640
- self.symbol_list = get_symbol_list()
641
- self.current_file_names = []
642
-
643
- def get_completions(self, document, complete_event):
644
- text = document.text_before_cursor
645
- words = text.split()
646
-
647
- if len(words) > 0:
648
- if words[0] == "/mode":
649
- left_word = text[len("/mode"):]
650
- for mode in ["normal", "auto_detect", "voice_input"]:
651
- if mode.startswith(left_word.strip()):
652
- yield Completion(mode, start_position=-len(left_word.strip()))
653
-
654
- if words[0] == "/add_files":
655
- new_text = text[len("/add_files"):]
656
- parser = CommandTextParser(new_text, words[0])
657
- parser.add_files()
658
- current_word = parser.current_word()
659
-
660
- if parser.last_sub_command() == "/refresh":
661
- return
662
-
663
- for command in parser.get_sub_commands():
664
- if command.startswith(current_word):
665
- yield Completion(command, start_position=-len(current_word))
666
-
667
- if parser.first_sub_command() == "/group" and (
668
- parser.last_sub_command() == "/group"
669
- or parser.last_sub_command() == "/drop"
670
- ):
671
- group_names = memory["current_files"]["groups"].keys()
672
- if "," in current_word:
673
- current_word = current_word.split(",")[-1]
674
-
675
- for group_name in group_names:
676
- if group_name.startswith(current_word):
677
- yield Completion(
678
- group_name, start_position=-len(current_word)
679
- )
680
-
681
- if parser.first_sub_command() != "/group":
682
- if current_word and current_word.startswith("."):
683
- for file_name in self.all_files_with_dot:
684
- if file_name.startswith(current_word):
685
- yield Completion(
686
- file_name, start_position=-
687
- len(current_word)
688
- )
689
- else:
690
- for file_name in self.all_file_names:
691
- if file_name.startswith(current_word):
692
- yield Completion(
693
- file_name, start_position=-
694
- len(current_word)
695
- )
696
- for file_name in self.all_files:
697
- if current_word and current_word in file_name:
698
- yield Completion(
699
- file_name, start_position=-
700
- len(current_word)
701
- )
702
- elif words[0] in ["/chat", "/coding"]:
703
- image_extensions = (
704
- ".png",
705
- ".jpg",
706
- ".jpeg",
707
- ".gif",
708
- ".bmp",
709
- ".tiff",
710
- ".tif",
711
- ".webp",
712
- ".svg",
713
- ".ico",
714
- ".heic",
715
- ".heif",
716
- ".raw",
717
- ".cr2",
718
- ".nef",
719
- ".arw",
720
- ".dng",
721
- ".orf",
722
- ".rw2",
723
- ".pef",
724
- ".srw",
725
- ".eps",
726
- ".ai",
727
- ".psd",
728
- ".xcf",
729
- )
730
- new_text = text[len(words[0]):]
731
- parser = CommandTextParser(new_text, words[0])
732
-
733
- parser.coding()
734
- current_word = parser.current_word()
735
-
736
- if len(new_text.strip()) == 0 or new_text.strip() == "/":
737
- for command in parser.get_sub_commands():
738
- if command.startswith(current_word):
739
- yield Completion(command, start_position=-len(current_word))
740
-
741
- all_tags = parser.tags
742
-
743
- if current_word.startswith("@"):
744
- name = current_word[1:]
745
- target_set = set()
746
-
747
- for file_name in self.current_file_names:
748
- base_file_name = os.path.basename(file_name)
749
- if name in base_file_name:
750
- target_set.add(base_file_name)
751
- path_parts = file_name.split(os.sep)
752
- display_name = (
753
- os.sep.join(path_parts[-3:])
754
- if len(path_parts) > 3
755
- else file_name
756
- )
757
- relative_path = os.path.relpath(
758
- file_name, project_root)
759
- yield Completion(
760
- relative_path,
761
- start_position=-len(name),
762
- display=f"{display_name} (in active files)",
763
- )
764
-
765
- for file_name in self.all_file_names:
766
- if file_name.startswith(name) and file_name not in target_set:
767
- target_set.add(file_name)
768
-
769
- path_parts = file_name.split(os.sep)
770
- display_name = (
771
- os.sep.join(path_parts[-3:])
772
- if len(path_parts) > 3
773
- else file_name
774
- )
775
- relative_path = os.path.relpath(
776
- file_name, project_root)
777
-
778
- yield Completion(
779
- relative_path,
780
- start_position=-len(name),
781
- display=f"{display_name}",
782
- )
783
-
784
- for file_name in self.all_files:
785
- if name in file_name and file_name not in target_set:
786
- path_parts = file_name.split(os.sep)
787
- display_name = (
788
- os.sep.join(path_parts[-3:])
789
- if len(path_parts) > 3
790
- else file_name
791
- )
792
- relative_path = os.path.relpath(
793
- file_name, project_root)
794
- yield Completion(
795
- relative_path,
796
- start_position=-len(name),
797
- display=f"{display_name}",
798
- )
799
-
800
- if current_word.startswith("@@"):
801
- name = current_word[2:]
802
- for symbol in self.symbol_list:
803
- if name in symbol.symbol_name:
804
- file_name = symbol.file_name
805
- path_parts = file_name.split(os.sep)
806
- display_name = (
807
- os.sep.join(path_parts[-3:])
808
- if len(path_parts) > 3
809
- else symbol.symbol_name
810
- )
811
- relative_path = os.path.relpath(
812
- file_name, project_root)
813
- yield Completion(
814
- f"{symbol.symbol_name}(location: {relative_path})",
815
- start_position=-len(name),
816
- display=f"{symbol.symbol_name} ({display_name}/{symbol.symbol_type})",
817
- )
818
-
819
- tags = [tag for tag in parser.tags]
820
-
821
- if current_word.startswith("<"):
822
- name = current_word[1:]
823
- for tag in ["<img>", "</img>"]:
824
- if all_tags and all_tags[-1].start_tag == "<img>":
825
- if tag.startswith(name):
826
- yield Completion(
827
- "</img>", start_position=-len(current_word)
828
- )
829
- elif tag.startswith(name):
830
- yield Completion(tag, start_position=-len(current_word))
831
-
832
- if tags and tags[-1].start_tag == "<img>" and tags[-1].end_tag == "":
833
- raw_file_name = tags[0].content
834
- file_name = raw_file_name.strip()
835
- parent_dir = os.path.dirname(file_name)
836
- file_basename = os.path.basename(file_name)
837
- search_dir = parent_dir if parent_dir else "."
838
- for root, dirs, files in os.walk(search_dir):
839
- # 只处理直接子目录
840
- if root != search_dir:
841
- continue
842
-
843
- # 补全子目录
844
- for dir in dirs:
845
- full_path = os.path.join(root, dir)
846
- if full_path.startswith(file_name):
847
- relative_path = os.path.relpath(
848
- full_path, search_dir)
849
- yield Completion(
850
- relative_path,
851
- start_position=-len(file_basename),
852
- )
853
-
854
- # 补全文件
855
- for file in files:
856
- if file.lower().endswith(
857
- image_extensions
858
- ) and file.startswith(file_basename):
859
- full_path = os.path.join(root, file)
860
- relative_path = os.path.relpath(
861
- full_path, search_dir)
862
- yield Completion(
863
- relative_path,
864
- start_position=-len(file_basename),
865
- )
866
-
867
- # 只处理一层子目录,然后退出循环
868
- break
869
-
870
- elif words[0] == "/remove_files":
871
- new_words = text[len("/remove_files"):].strip().split(",")
872
-
873
- is_at_space = text[-1] == " "
874
- last_word = new_words[-2] if len(new_words) > 1 else ""
875
- current_word = new_words[-1] if new_words else ""
876
-
877
- if is_at_space:
878
- last_word = current_word
879
- current_word = ""
880
-
881
- # /remove_files /all [cursor] or /remove_files /all p[cursor]
882
- if not last_word and not current_word:
883
- if "/all".startswith(current_word):
884
- yield Completion("/all", start_position=-len(current_word))
885
- for file_name in self.current_file_names:
886
- yield Completion(file_name, start_position=-len(current_word))
887
-
888
- # /remove_files /a[cursor] or /remove_files p[cursor]
889
- if current_word:
890
- if "/all".startswith(current_word):
891
- yield Completion("/all", start_position=-len(current_word))
892
- for file_name in self.current_file_names:
893
- if current_word and current_word in file_name:
894
- yield Completion(
895
- file_name, start_position=-len(current_word)
896
- )
897
- elif words[0] == "/exclude_dirs":
898
- new_words = text[len("/exclude_dirs"):].strip().split(",")
899
- current_word = new_words[-1]
900
-
901
- for file_name in self.all_dir_names:
902
- if current_word and current_word in file_name:
903
- yield Completion(file_name, start_position=-len(current_word))
904
-
905
- elif words[0] == "/lib":
906
- new_text = text[len("/lib"):]
907
- parser = CommandTextParser(new_text, words[0])
908
- parser.lib()
909
- current_word = parser.current_word()
910
-
911
- for command in parser.get_sub_commands():
912
- if command.startswith(current_word):
913
- yield Completion(command, start_position=-len(current_word))
914
-
915
- if parser.last_sub_command() in ["/add", "/remove", "/get"]:
916
- for lib_name in memory.get("libs", {}).keys():
917
- if lib_name.startswith(current_word):
918
- yield Completion(
919
- lib_name, start_position=-len(current_word)
920
- )
921
- elif words[0] == "/mcp":
922
- new_text = text[len("/mcp"):]
923
- parser = CommandTextParser(new_text, words[0])
924
- parser.lib()
925
- current_word = parser.current_word()
926
- for command in parser.get_sub_commands():
927
- if command.startswith(current_word):
928
- yield Completion(command, start_position=-len(current_word))
929
- elif words[0] == "/models":
930
- new_text = text[len("/models"):]
931
- parser = CommandTextParser(new_text, words[0])
932
- parser.lib()
933
- current_word = parser.current_word()
934
- for command in parser.get_sub_commands():
935
- if command.startswith(current_word):
936
- yield Completion(command, start_position=-len(current_word))
937
-
938
- elif words[0] == "/coding":
939
- new_text = text[len("/coding"):]
940
- parser = CommandTextParser(new_text, words[0])
941
- parser.lib()
942
- current_word = parser.current_word()
943
- for command in parser.get_sub_commands():
944
- if command.startswith(current_word):
945
- yield Completion(command, start_position=-len(current_word))
946
-
947
- elif words[0] == "/conf":
948
- new_words = text[len("/conf"):].strip().split()
949
- is_at_space = text[-1] == " "
950
- last_word = new_words[-2] if len(new_words) > 1 else ""
951
- current_word = new_words[-1] if new_words else ""
952
- completions = []
953
-
954
- if is_at_space:
955
- last_word = current_word
956
- current_word = ""
957
-
958
- # /conf /drop [curor] or /conf /drop p[cursor]
959
- if last_word == "/drop":
960
- completions = [
961
- field_name
962
- for field_name in memory["conf"].keys()
963
- if field_name.startswith(current_word)
964
- ]
965
- # /conf [curosr]
966
- elif not last_word and not current_word:
967
- completions = [
968
- "/drop"] if "/drop".startswith(current_word) else []
969
- completions += [
970
- field_name + ":"
971
- for field_name in AutoCoderArgs.model_fields.keys()
972
- if field_name.startswith(current_word)
973
- ]
974
- # /conf p[cursor]
975
- elif not last_word and current_word:
976
- completions = [
977
- "/drop"] if "/drop".startswith(current_word) else []
978
- completions += [
979
- field_name + ":"
980
- for field_name in AutoCoderArgs.model_fields.keys()
981
- if field_name.startswith(current_word)
982
- ]
983
-
984
- for completion in completions:
985
- yield Completion(completion, start_position=-len(current_word))
986
-
987
- else:
988
- for command in self.commands:
989
- if command.startswith(text):
990
- yield Completion(command, start_position=-len(text))
991
-
992
- else:
993
- for command in self.commands:
994
- if command.startswith(text):
995
- yield Completion(command, start_position=-len(text))
996
-
997
- def update_current_files(self, files):
998
- self.current_file_names = [f for f in files]
999
-
1000
- def refresh_files(self):
1001
- self.all_file_names = get_all_file_names_in_project()
1002
- self.all_files = get_all_file_in_project()
1003
- self.all_dir_names = get_all_dir_names_in_project()
1004
- self.all_files_with_dot = get_all_file_in_project_with_dot()
1005
- self.symbol_list = get_symbol_list()
1006
-
1007
-
1008
- completer = CommandCompleter(commands)
1009
-
1010
-
1011
636
  def save_memory():
1012
637
  with open(os.path.join(base_persist_dir, "memory.json"), "w") as f:
1013
638
  json.dump(memory, f, indent=2, ensure_ascii=False)
@@ -1023,6 +648,21 @@ def load_memory():
1023
648
  completer.update_current_files(memory["current_files"]["files"])
1024
649
 
1025
650
 
651
+ completer = CommandCompleter(commands,
652
+ file_system_model=CCFileSystemModel(project_root=project_root,
653
+ defaut_exclude_dirs=defaut_exclude_dirs,
654
+ get_all_file_names_in_project=get_all_file_names_in_project,
655
+ get_all_file_in_project=get_all_file_in_project,
656
+ get_all_dir_names_in_project=get_all_dir_names_in_project,
657
+ get_all_file_in_project_with_dot=get_all_file_in_project_with_dot,
658
+ get_symbol_list=get_symbol_list
659
+ ),
660
+ memory_model=CCMemoryModel(memory=memory,
661
+ save_memory_func=save_memory))
662
+
663
+
664
+
665
+
1026
666
  def print_conf(content:Dict[str,Any]):
1027
667
  """Display configuration dictionary in a Rich table format with enhanced visual styling.
1028
668
 
@@ -1069,6 +709,7 @@ def print_conf(content:Dict[str,Any]):
1069
709
  ))
1070
710
 
1071
711
  def revert():
712
+ result_manager = ResultManager()
1072
713
  last_yaml_file = get_last_yaml_file("actions")
1073
714
  if last_yaml_file:
1074
715
  file_path = os.path.join("actions", last_yaml_file)
@@ -1078,16 +719,24 @@ def revert():
1078
719
  s = output.getvalue()
1079
720
  print(s, flush=True)
1080
721
  if "Successfully reverted changes" in s:
722
+ result_manager.append(content=s, meta={"action": "revert","success":False, "input":{
723
+ }})
1081
724
  print(
1082
725
  "Reverted the last chat action successfully. Remove the yaml file {file_path}"
1083
726
  )
1084
727
  os.remove(file_path)
728
+ else:
729
+ result_manager.append(content=s, meta={"action": "revert","success":False, "input":{
730
+ }})
1085
731
  else:
732
+ result_manager.append(content="No previous chat action found to revert.", meta={"action": "revert","success":False, "input":{
733
+ }})
1086
734
  print("No previous chat action found to revert.")
1087
735
 
1088
736
 
1089
737
  def add_files(args: List[str]):
1090
-
738
+
739
+ result_manager = ResultManager()
1091
740
  if "groups" not in memory["current_files"]:
1092
741
  memory["current_files"]["groups"] = {}
1093
742
  if "groups_info" not in memory["current_files"]:
@@ -1102,6 +751,8 @@ def add_files(args: List[str]):
1102
751
 
1103
752
  if not args:
1104
753
  printer.print_in_terminal("add_files_no_args", style="red")
754
+ result_manager.append(content=printer.get_message_from_key("add_files_no_args"),
755
+ meta={"action": "add_files","success":False, "input":{ "args": args}})
1105
756
  return
1106
757
 
1107
758
  if args[0] == "/refresh":
@@ -1111,6 +762,8 @@ def add_files(args: List[str]):
1111
762
  Panel("Refreshed file list.",
1112
763
  title="Files Refreshed", border_style="green")
1113
764
  )
765
+ result_manager.append(content="Files refreshed.",
766
+ meta={"action": "add_files","success":True, "input":{ "args": args}})
1114
767
  return
1115
768
 
1116
769
  if args[0] == "/group":
@@ -1120,6 +773,8 @@ def add_files(args: List[str]):
1120
773
  Panel("No groups defined.", title="Groups",
1121
774
  border_style="yellow")
1122
775
  )
776
+ result_manager.append(content="No groups defined.",
777
+ meta={"action": "add_files","success":False, "input":{ "args": args}})
1123
778
  else:
1124
779
  table = Table(
1125
780
  title="Defined Groups",
@@ -1150,6 +805,8 @@ def add_files(args: List[str]):
1150
805
  end_section=(i == len(groups) - 1),
1151
806
  )
1152
807
  console.print(Panel(table, border_style="blue"))
808
+ result_manager.append(content="Defined groups.",
809
+ meta={"action": "add_files","success":True, "input":{ "args": args}})
1153
810
  elif len(args) >= 2 and args[1] == "/reset":
1154
811
  memory["current_files"]["current_groups"] = []
1155
812
  console.print(
@@ -1159,6 +816,8 @@ def add_files(args: List[str]):
1159
816
  border_style="green",
1160
817
  )
1161
818
  )
819
+ result_manager.append(content="Active group names have been reset. If you want to clear the active files, you should use the command /remove_files /all.",
820
+ meta={"action": "add_files","success":True, "input":{ "args": args}})
1162
821
  elif len(args) >= 3 and args[1] == "/add":
1163
822
  group_name = args[2]
1164
823
  groups[group_name] = memory["current_files"]["files"].copy()
@@ -1169,6 +828,9 @@ def add_files(args: List[str]):
1169
828
  border_style="green",
1170
829
  )
1171
830
  )
831
+ result_manager.append(content=f"Added group '{group_name}' with current files.",
832
+ meta={"action": "add_files","success":True, "input":{ "args": args}})
833
+
1172
834
  elif len(args) >= 3 and args[1] == "/drop":
1173
835
  group_name = args[2]
1174
836
  if group_name in groups:
@@ -1185,6 +847,8 @@ def add_files(args: List[str]):
1185
847
  border_style="green",
1186
848
  )
1187
849
  )
850
+ result_manager.append(content=f"Dropped group '{group_name}'.",
851
+ meta={"action": "add_files","success":True, "input":{ "args": args}})
1188
852
  else:
1189
853
  console.print(
1190
854
  Panel(
@@ -1193,6 +857,8 @@ def add_files(args: List[str]):
1193
857
  border_style="red",
1194
858
  )
1195
859
  )
860
+ result_manager.append(content=f"Group '{group_name}' not found.",
861
+ meta={"action": "add_files","success":False, "input":{ "args": args}})
1196
862
  elif len(args) == 3 and args[1] == "/set":
1197
863
  group_name = args[2]
1198
864
 
@@ -1264,6 +930,8 @@ def add_files(args: List[str]):
1264
930
  border_style="red",
1265
931
  )
1266
932
  )
933
+ result_manager.append(content=f"Group(s) not found: {', '.join(missing_groups)}",
934
+ meta={"action": "add_files","success":False, "input":{ "args": args}})
1267
935
 
1268
936
  if merged_files:
1269
937
  memory["current_files"]["files"] = list(merged_files)
@@ -1299,6 +967,8 @@ def add_files(args: List[str]):
1299
967
  border_style="green",
1300
968
  )
1301
969
  )
970
+ result_manager.append(content=f"Active groups: {', '.join(memory['current_files']['current_groups'])}",
971
+ meta={"action": "add_files","success":True, "input":{ "args": args}})
1302
972
  elif not missing_groups:
1303
973
  console.print(
1304
974
  Panel(
@@ -1306,7 +976,9 @@ def add_files(args: List[str]):
1306
976
  title="No Files Added",
1307
977
  border_style="yellow",
1308
978
  )
1309
- )
979
+ )
980
+ result_manager.append(content="No files in the specified groups.",
981
+ meta={"action": "add_files","success":False, "input":{ "args": args}})
1310
982
  else:
1311
983
  existing_files = memory["current_files"]["files"]
1312
984
  matched_files = find_files_in_project(args)
@@ -1328,9 +1000,13 @@ def add_files(args: List[str]):
1328
1000
  i == len(files_to_add) - 1
1329
1001
  ), # 在最后一行之后不添加分割线
1330
1002
  )
1331
- console.print(Panel(table, border_style="green"))
1003
+ console.print(Panel(table, border_style="green"))
1004
+ result_manager.append(content=f"Added files: {', '.join(files_to_add)}",
1005
+ meta={"action": "add_files","success":True, "input":{ "args": args}})
1332
1006
  else:
1333
1007
  printer.print_in_terminal("add_files_matched", style="yellow")
1008
+ result_manager.append(content=f"No files matched.",
1009
+ meta={"action": "add_files","success":False, "input":{ "args": args}})
1334
1010
 
1335
1011
  completer.update_current_files(memory["current_files"]["files"])
1336
1012
  save_memory()
@@ -1339,11 +1015,14 @@ def add_files(args: List[str]):
1339
1015
  def remove_files(file_names: List[str]):
1340
1016
  project_root = os.getcwd()
1341
1017
  printer = Printer()
1018
+ result_manager = ResultManager()
1342
1019
 
1343
1020
  if "/all" in file_names:
1344
1021
  memory["current_files"]["files"] = []
1345
1022
  memory["current_files"]["current_groups"] = []
1346
1023
  printer.print_in_terminal("remove_files_all", style="green")
1024
+ result_manager.append(content="All files removed.",
1025
+ meta={"action": "remove_files","success":True, "input":{ "file_names": file_names}})
1347
1026
  else:
1348
1027
  removed_files = []
1349
1028
  for file in memory["current_files"]["files"]:
@@ -1366,9 +1045,13 @@ def remove_files(file_names: List[str]):
1366
1045
  console = Console()
1367
1046
  console.print(
1368
1047
  Panel(table, border_style="green",
1369
- title=printer.get_message_from_key("files_removed")))
1048
+ title=printer.get_message_from_key("files_removed")))
1049
+ result_manager.append(content=f"Removed files: {', '.join(removed_files)}",
1050
+ meta={"action": "remove_files","success":True, "input":{ "file_names": file_names}})
1370
1051
  else:
1371
1052
  printer.print_in_terminal("remove_files_none", style="yellow")
1053
+ result_manager.append(content=printer.get_message_from_key("remove_files_none"),
1054
+ meta={"action": "remove_files","success":False, "input":{ "file_names": file_names}})
1372
1055
 
1373
1056
  completer.update_current_files(memory["current_files"]["files"])
1374
1057
  save_memory()
@@ -1890,6 +1573,8 @@ def coding(query: str):
1890
1573
 
1891
1574
  yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
1892
1575
 
1576
+ md5 = hashlib.md5(yaml_content.encode("utf-8")).hexdigest()
1577
+
1893
1578
  execute_file = os.path.join("actions", latest_yaml_file)
1894
1579
  with open(os.path.join(execute_file), "w") as f:
1895
1580
  f.write(yaml_content)
@@ -1897,6 +1582,10 @@ def coding(query: str):
1897
1582
  def execute_chat():
1898
1583
  cmd = ["--file", execute_file]
1899
1584
  auto_coder_main(cmd)
1585
+ result_manager = ResultManager()
1586
+ result_manager.append(content="", meta={"commit_message": f"auto_coder_{latest_yaml_file}_{md5}","action": "coding", "input":{
1587
+ "query": query
1588
+ }})
1900
1589
 
1901
1590
  execute_chat()
1902
1591
  else:
@@ -2178,7 +1867,7 @@ def generate_shell_command(input_text):
2178
1867
  finally:
2179
1868
  os.remove(execute_file)
2180
1869
 
2181
- def manage_models(params, query: str):
1870
+ def manage_models(query: str):
2182
1871
  """
2183
1872
  Handle /models subcommands:
2184
1873
  /models /list - List all models (default + custom)
@@ -2189,7 +1878,8 @@ def manage_models(params, query: str):
2189
1878
  printer = Printer()
2190
1879
  console = Console()
2191
1880
 
2192
- if params.product_mode != "lite":
1881
+ product_mode = memory.get("product_mode", "lite")
1882
+ if product_mode != "lite":
2193
1883
  printer.print_in_terminal("models_lite_only", style="red")
2194
1884
  return
2195
1885
 
@@ -2240,7 +1930,8 @@ def manage_models(params, query: str):
2240
1930
 
2241
1931
  if not subcmd:
2242
1932
  printer.print_in_terminal("models_usage")
2243
-
1933
+
1934
+ result_manager = ResultManager()
2244
1935
  if subcmd == "/list":
2245
1936
  if models_data:
2246
1937
  # Sort models by speed (average_speed)
@@ -2277,8 +1968,21 @@ def manage_models(params, query: str):
2277
1968
  f"{m.get('average_speed', 0.0):.3f}"
2278
1969
  )
2279
1970
  console.print(table)
1971
+ result_manager.add_result(content=json.dumps(sorted_models,ensure_ascii=False),meta={
1972
+ "action": "models",
1973
+ "input": {
1974
+ "query": query
1975
+ }
1976
+ })
1977
+
2280
1978
  else:
2281
1979
  printer.print_in_terminal("models_no_models", style="yellow")
1980
+ result_manager.add_result(content="No models found",meta={
1981
+ "action": "models",
1982
+ "input": {
1983
+ "query": query
1984
+ }
1985
+ })
2282
1986
 
2283
1987
  elif subcmd == "/input_price":
2284
1988
  args = query.strip().split()
@@ -2288,11 +1992,35 @@ def manage_models(params, query: str):
2288
1992
  price = float(args[1])
2289
1993
  if models_module.update_model_input_price(name, price):
2290
1994
  printer.print_in_terminal("models_input_price_updated", style="green", name=name, price=price)
1995
+ result_manager.add_result(content=f"models_input_price_updated: {name} {price}",meta={
1996
+ "action": "models",
1997
+ "input": {
1998
+ "query": query
1999
+ }
2000
+ })
2291
2001
  else:
2292
2002
  printer.print_in_terminal("models_not_found", style="red", name=name)
2003
+ result_manager.add_result(content=f"models_not_found: {name}",meta={
2004
+ "action": "models",
2005
+ "input": {
2006
+ "query": query
2007
+ }
2008
+ })
2293
2009
  except ValueError as e:
2010
+ result_manager.add_result(content=f"models_invalid_price: {str(e)}",meta={
2011
+ "action": "models",
2012
+ "input": {
2013
+ "query": query
2014
+ }
2015
+ })
2294
2016
  printer.print_in_terminal("models_invalid_price", style="red", error=str(e))
2295
2017
  else:
2018
+ result_manager.add_result(content=printer.get_message_from_key("models_input_price_usage"),meta={
2019
+ "action": "models",
2020
+ "input": {
2021
+ "query": query
2022
+ }
2023
+ })
2296
2024
  printer.print_in_terminal("models_input_price_usage", style="red")
2297
2025
 
2298
2026
  elif subcmd == "/output_price":
@@ -2303,11 +2031,35 @@ def manage_models(params, query: str):
2303
2031
  price = float(args[1])
2304
2032
  if models_module.update_model_output_price(name, price):
2305
2033
  printer.print_in_terminal("models_output_price_updated", style="green", name=name, price=price)
2034
+ result_manager.add_result(content=f"models_output_price_updated: {name} {price}",meta={
2035
+ "action": "models",
2036
+ "input": {
2037
+ "query": query
2038
+ }
2039
+ })
2306
2040
  else:
2307
2041
  printer.print_in_terminal("models_not_found", style="red", name=name)
2042
+ result_manager.add_result(content=f"models_not_found: {name}",meta={
2043
+ "action": "models",
2044
+ "input": {
2045
+ "query": query
2046
+ }
2047
+ })
2308
2048
  except ValueError as e:
2309
2049
  printer.print_in_terminal("models_invalid_price", style="red", error=str(e))
2050
+ result_manager.add_result(content=f"models_invalid_price: {str(e)}",meta={
2051
+ "action": "models",
2052
+ "input": {
2053
+ "query": query
2054
+ }
2055
+ })
2310
2056
  else:
2057
+ result_manager.add_result(content=printer.get_message_from_key("models_output_price_usage"),meta={
2058
+ "action": "models",
2059
+ "input": {
2060
+ "query": query
2061
+ }
2062
+ })
2311
2063
  printer.print_in_terminal("models_output_price_usage", style="red")
2312
2064
 
2313
2065
  elif subcmd == "/speed":
@@ -2318,11 +2070,35 @@ def manage_models(params, query: str):
2318
2070
  speed = float(args[1])
2319
2071
  if models_module.update_model_speed(name, speed):
2320
2072
  printer.print_in_terminal("models_speed_updated", style="green", name=name, speed=speed)
2073
+ result_manager.add_result(content=f"models_speed_updated: {name} {speed}",meta={
2074
+ "action": "models",
2075
+ "input": {
2076
+ "query": query
2077
+ }
2078
+ })
2321
2079
  else:
2322
2080
  printer.print_in_terminal("models_not_found", style="red", name=name)
2081
+ result_manager.add_result(content=f"models_not_found: {name}",meta={
2082
+ "action": "models",
2083
+ "input": {
2084
+ "query": query
2085
+ }
2086
+ })
2323
2087
  except ValueError as e:
2324
2088
  printer.print_in_terminal("models_invalid_speed", style="red", error=str(e))
2089
+ result_manager.add_result(content=f"models_invalid_speed: {str(e)}",meta={
2090
+ "action": "models",
2091
+ "input": {
2092
+ "query": query
2093
+ }
2094
+ })
2325
2095
  else:
2096
+ result_manager.add_result(content=printer.get_message_from_key("models_speed_usage"),meta={
2097
+ "action": "models",
2098
+ "input": {
2099
+ "query": query
2100
+ }
2101
+ })
2326
2102
  printer.print_in_terminal("models_speed_usage", style="red")
2327
2103
 
2328
2104
  elif subcmd == "/speed-test":
@@ -2343,7 +2119,14 @@ def manage_models(params, query: str):
2343
2119
  if args and args[0].isdigit():
2344
2120
  test_rounds = int(args[0])
2345
2121
 
2346
- render_speed_test_in_terminal(params.product_mode, test_rounds,enable_long_context=enable_long_context)
2122
+ render_speed_test_in_terminal(product_mode, test_rounds,enable_long_context=enable_long_context)
2123
+ ## 等待优化,获取明细数据
2124
+ result_manager.add_result(content="models test success",meta={
2125
+ "action": "models",
2126
+ "input": {
2127
+ "query": query
2128
+ }
2129
+ })
2347
2130
 
2348
2131
  elif subcmd == "/add":
2349
2132
  # Support both simplified and legacy formats
@@ -2353,11 +2136,29 @@ def manage_models(params, query: str):
2353
2136
  name, api_key = args[0], args[1]
2354
2137
  result = models_module.update_model_with_api_key(name, api_key)
2355
2138
  if result:
2139
+ result_manager.add_result(content=f"models_added: {name}",meta={
2140
+ "action": "models",
2141
+ "input": {
2142
+ "query": query
2143
+ }
2144
+ })
2356
2145
  printer.print_in_terminal("models_added", style="green", name=name)
2357
2146
  else:
2147
+ result_manager.add_result(content=f"models_add_failed: {name}",meta={
2148
+ "action": "models",
2149
+ "input": {
2150
+ "query": query
2151
+ }
2152
+ })
2358
2153
  printer.print_in_terminal("models_add_failed", style="red", name=name)
2359
2154
  else:
2360
2155
  printer.print_in_terminal("models_add_usage", style="red")
2156
+ result_manager.add_result(content=printer.get_message_from_key("models_add_usage"),meta={
2157
+ "action": "models",
2158
+ "input": {
2159
+ "query": query
2160
+ }
2161
+ })
2361
2162
 
2362
2163
  elif subcmd == "/add_model":
2363
2164
  # Parse key=value pairs: /models /add_model name=abc base_url=http://xx ...
@@ -2379,6 +2180,12 @@ def manage_models(params, query: str):
2379
2180
  # Check duplication
2380
2181
  if any(m["name"] == data_dict["name"] for m in models_data):
2381
2182
  printer.print_in_terminal("models_add_model_exists", style="yellow", name=data_dict["name"])
2183
+ result_manager.add_result(content=printer.get_message_from_key("models_add_model_exists",name=data_dict["name"]),meta={
2184
+ "action": "models",
2185
+ "input": {
2186
+ "query": query
2187
+ }
2188
+ })
2382
2189
  return
2383
2190
 
2384
2191
  # Create model with defaults
@@ -2395,22 +2202,51 @@ def manage_models(params, query: str):
2395
2202
  models_data.append(final_model)
2396
2203
  models_module.save_models(models_data)
2397
2204
  printer.print_in_terminal("models_add_model_success", style="green", name=data_dict["name"])
2205
+ result_manager.add_result(content=f"models_add_model_success: {data_dict['name']}",meta={
2206
+ "action": "models",
2207
+ "input": {
2208
+ "query": query
2209
+ }
2210
+ })
2398
2211
 
2399
2212
  elif subcmd == "/remove":
2400
2213
  args = query.strip().split(" ")
2401
2214
  if len(args) < 1:
2402
2215
  printer.print_in_terminal("models_add_usage", style="red")
2216
+ result_manager.add_result(content=printer.get_message_from_key("models_add_usage"),meta={
2217
+ "action": "models",
2218
+ "input": {
2219
+ "query": query
2220
+ }
2221
+ })
2403
2222
  return
2404
2223
  name = args[0]
2405
2224
  filtered_models = [m for m in models_data if m["name"] != name]
2406
2225
  if len(filtered_models) == len(models_data):
2407
2226
  printer.print_in_terminal("models_add_model_remove", style="yellow", name=name)
2227
+ result_manager.add_result(content=printer.get_message_from_key("models_add_model_remove",name=name),meta={
2228
+ "action": "models",
2229
+ "input": {
2230
+ "query": query
2231
+ }
2232
+ })
2408
2233
  return
2409
2234
  models_module.save_models(filtered_models)
2410
2235
  printer.print_in_terminal("models_add_model_removed", style="green", name=name)
2411
-
2236
+ result_manager.add_result(content=printer.get_message_from_key("models_add_model_removed",name=name),meta={
2237
+ "action": "models",
2238
+ "input": {
2239
+ "query": query
2240
+ }
2241
+ })
2412
2242
  else:
2413
2243
  printer.print_in_terminal("models_unknown_subcmd", style="yellow", subcmd=subcmd)
2244
+ result_manager.add_result(content=printer.get_message_from_key("models_unknown_subcmd",subcmd=subcmd),meta={
2245
+ "action": "models",
2246
+ "input": {
2247
+ "query": query
2248
+ }
2249
+ })
2414
2250
 
2415
2251
  def exclude_dirs(dir_names: List[str]):
2416
2252
  new_dirs = dir_names
@@ -2452,6 +2288,41 @@ def index_build():
2452
2288
  os.remove(yaml_file)
2453
2289
 
2454
2290
 
2291
+ def get_final_config()->AutoCoderArgs:
2292
+ conf = memory.get("conf", {})
2293
+ yaml_config = {
2294
+ "include_file": ["./base/base.yml"],
2295
+ "auto_merge": conf.get("auto_merge", "editblock"),
2296
+ "human_as_model": conf.get("human_as_model", "false") == "true",
2297
+ "skip_build_index": conf.get("skip_build_index", "true") == "true",
2298
+ "skip_confirm": conf.get("skip_confirm", "true") == "true",
2299
+ "silence": conf.get("silence", "true") == "true",
2300
+ "include_project_structure": conf.get("include_project_structure", "true")
2301
+ == "true",
2302
+ }
2303
+ for key, value in conf.items():
2304
+ converted_value = convert_config_value(key, value)
2305
+ if converted_value is not None:
2306
+ yaml_config[key] = converted_value
2307
+
2308
+ temp_yaml = os.path.join("actions", f"{uuid.uuid4()}.yml")
2309
+ try:
2310
+ with open(temp_yaml, "w") as f:
2311
+ f.write(convert_yaml_config_to_str(yaml_config=yaml_config))
2312
+ args = convert_yaml_to_config(temp_yaml)
2313
+ finally:
2314
+ if os.path.exists(temp_yaml):
2315
+ os.remove(temp_yaml)
2316
+ return args
2317
+
2318
+ def help(query: str):
2319
+ from autocoder.common.auto_configure import ConfigAutoTuner,MemoryConfig,AutoConfigRequest
2320
+ args = get_final_config()
2321
+ product_mode = memory.get("product_mode", "lite")
2322
+ llm = get_single_llm(args.chat_model or args.model, product_mode=product_mode)
2323
+ auto_config_tuner = ConfigAutoTuner(llm=llm, memory_config=MemoryConfig(memory=memory, save_memory_func=save_memory))
2324
+ auto_config_tuner.tune(AutoConfigRequest(query=query))
2325
+
2455
2326
  @run_in_raw_thread()
2456
2327
  def index_query(query: str):
2457
2328
  conf = memory.get("conf", {})
@@ -2634,6 +2505,58 @@ def lib_command(args: List[str]):
2634
2505
  else:
2635
2506
  console.print(f"Unknown subcommand: {subcommand}")
2636
2507
 
2508
+ @run_in_raw_thread()
2509
+ def auto_command(params,query: str):
2510
+ """处理/auto指令"""
2511
+ from autocoder.commands.auto_command import CommandAutoTuner, AutoCommandRequest, CommandConfig, MemoryConfig
2512
+ args = get_final_config()
2513
+ # help(query)
2514
+
2515
+ # 准备请求参数
2516
+ request = AutoCommandRequest(
2517
+ user_input=query
2518
+ )
2519
+
2520
+ # 初始化调优器
2521
+ llm = get_single_llm(args.chat_model or args.model,product_mode=args.product_mode)
2522
+ tuner = CommandAutoTuner(llm,
2523
+ args=args,
2524
+ memory_config=MemoryConfig(memory=memory, save_memory_func=save_memory),
2525
+ command_config=CommandConfig(
2526
+ add_files=add_files,
2527
+ remove_files=remove_files,
2528
+ list_files=list_files,
2529
+ conf=configure,
2530
+ revert=revert,
2531
+ commit=commit,
2532
+ help=help,
2533
+ exclude_dirs=exclude_dirs,
2534
+ ask=ask,
2535
+ chat=chat,
2536
+ coding=coding,
2537
+ design=design,
2538
+ summon=summon,
2539
+ lib=lib_command,
2540
+ mcp=mcp,
2541
+ models=manage_models,
2542
+ index_build=index_build,
2543
+ index_query=index_query,
2544
+ execute_shell_command=execute_shell_command,
2545
+ generate_shell_command=generate_shell_command
2546
+ ))
2547
+
2548
+ # 生成建议
2549
+ response = tuner.analyze(request)
2550
+
2551
+ # 显示建议
2552
+ console = Console()
2553
+ console.print(Panel(
2554
+ Markdown(response.reasoning or ""),
2555
+ title="Reasoning",
2556
+ border_style="blue",
2557
+ padding=(1, 2)
2558
+ ))
2559
+
2637
2560
 
2638
2561
  def main():
2639
2562
  from autocoder.rag.variable_holder import VariableHolder
@@ -2691,7 +2614,7 @@ def main():
2691
2614
  @kb.add("c-k")
2692
2615
  def _(event):
2693
2616
  if "mode" not in memory:
2694
- memory["mode"] = "normal"
2617
+ memory["mode"] = "auto_detect"
2695
2618
 
2696
2619
  current_mode = memory["mode"]
2697
2620
  if current_mode == "normal":
@@ -2715,7 +2638,7 @@ def main():
2715
2638
 
2716
2639
  def get_bottom_toolbar():
2717
2640
  if "mode" not in memory:
2718
- memory["mode"] = "normal"
2641
+ memory["mode"] = "auto_detect"
2719
2642
  mode = memory["mode"]
2720
2643
  human_as_model = memory["conf"].get("human_as_model", "false")
2721
2644
  if mode not in MODES:
@@ -2777,7 +2700,7 @@ def main():
2777
2700
  new_prompt = ""
2778
2701
 
2779
2702
  if "mode" not in memory:
2780
- memory["mode"] = "normal"
2703
+ memory["mode"] = "auto_detect"
2781
2704
 
2782
2705
  # 处理 user_input 的空格
2783
2706
  if user_input:
@@ -2790,11 +2713,8 @@ def main():
2790
2713
  and user_input
2791
2714
  and not user_input.startswith("/")
2792
2715
  ):
2793
- shell_script = generate_shell_command(user_input)
2794
- if confirm(get_message("confirm_execute")):
2795
- execute_shell_command(shell_script)
2796
- else:
2797
- continue
2716
+ auto_command(ARGS,user_input)
2717
+
2798
2718
  elif memory["mode"] == "voice_input" and not user_input.startswith("/"):
2799
2719
  text = voice_input()
2800
2720
  new_prompt = "/coding " + text
@@ -2825,7 +2745,7 @@ def main():
2825
2745
  if not query:
2826
2746
  print("Please enter your query.")
2827
2747
  else:
2828
- manage_models(ARGS,query)
2748
+ manage_models(query)
2829
2749
 
2830
2750
  elif user_input.startswith("/mode"):
2831
2751
  conf = user_input[len("/mode"):].strip()
@@ -2846,7 +2766,12 @@ def main():
2846
2766
  query = user_input[len("/commit"):].strip()
2847
2767
  commit(query)
2848
2768
  elif user_input.startswith("/help"):
2849
- show_help()
2769
+ query = user_input[len("/help"):].strip()
2770
+ if not query:
2771
+ show_help()
2772
+ else:
2773
+ help(query)
2774
+
2850
2775
  elif user_input.startswith("/exclude_dirs"):
2851
2776
  dir_names = user_input[len(
2852
2777
  "/exclude_dirs"):].strip().split(",")
@@ -2899,6 +2824,9 @@ def main():
2899
2824
  else:
2900
2825
  mcp(query)
2901
2826
 
2827
+ elif user_input.startswith("/auto"):
2828
+ query = user_input[len("/auto"):].strip()
2829
+ auto_command(ARGS,query)
2902
2830
  elif user_input.startswith("/debug"):
2903
2831
  code = user_input[len("/debug"):].strip()
2904
2832
  try: