auto-coder 0.1.259__py3-none-any.whl → 0.1.261__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 (36) hide show
  1. {auto_coder-0.1.259.dist-info → auto_coder-0.1.261.dist-info}/METADATA +1 -1
  2. {auto_coder-0.1.259.dist-info → auto_coder-0.1.261.dist-info}/RECORD +36 -27
  3. autocoder/agent/auto_review_commit.py +51 -24
  4. autocoder/auto_coder.py +24 -1
  5. autocoder/chat_auto_coder.py +377 -399
  6. autocoder/chat_auto_coder_lang.py +20 -0
  7. autocoder/commands/__init__.py +0 -0
  8. autocoder/commands/auto_command.py +1174 -0
  9. autocoder/commands/tools.py +533 -0
  10. autocoder/common/__init__.py +8 -0
  11. autocoder/common/auto_coder_lang.py +61 -8
  12. autocoder/common/auto_configure.py +304 -0
  13. autocoder/common/code_auto_merge.py +2 -2
  14. autocoder/common/code_auto_merge_diff.py +2 -2
  15. autocoder/common/code_auto_merge_editblock.py +2 -2
  16. autocoder/common/code_auto_merge_strict_diff.py +2 -2
  17. autocoder/common/code_modification_ranker.py +8 -7
  18. autocoder/common/command_completer.py +557 -0
  19. autocoder/common/conf_validator.py +245 -0
  20. autocoder/common/conversation_pruner.py +131 -0
  21. autocoder/common/git_utils.py +82 -1
  22. autocoder/common/index_import_export.py +101 -0
  23. autocoder/common/result_manager.py +115 -0
  24. autocoder/common/shells.py +22 -6
  25. autocoder/common/utils_code_auto_generate.py +2 -2
  26. autocoder/dispacher/actions/action.py +45 -4
  27. autocoder/dispacher/actions/plugins/action_regex_project.py +13 -1
  28. autocoder/index/filter/quick_filter.py +22 -7
  29. autocoder/utils/auto_coder_utils/chat_stream_out.py +13 -6
  30. autocoder/utils/project_structure.py +15 -0
  31. autocoder/utils/thread_utils.py +4 -0
  32. autocoder/version.py +1 -1
  33. {auto_coder-0.1.259.dist-info → auto_coder-0.1.261.dist-info}/LICENSE +0 -0
  34. {auto_coder-0.1.259.dist-info → auto_coder-0.1.261.dist-info}/WHEEL +0 -0
  35. {auto_coder-0.1.259.dist-info → auto_coder-0.1.261.dist-info}/entry_points.txt +0 -0
  36. {auto_coder-0.1.259.dist-info → auto_coder-0.1.261.dist-info}/top_level.txt +0 -0
@@ -1,3 +1,4 @@
1
+ from itertools import product
1
2
  from prompt_toolkit.formatted_text import HTML
2
3
  from prompt_toolkit.shortcuts import radiolist_dialog
3
4
  from prompt_toolkit import prompt
@@ -22,6 +23,7 @@ from prompt_toolkit.completion import WordCompleter, Completer, Completion
22
23
  from prompt_toolkit.shortcuts import confirm
23
24
  from autocoder.common import AutoCoderArgs
24
25
  from pydantic import Field, BaseModel
26
+ from autocoder.common.result_manager import ResultManager
25
27
  from autocoder.version import __version__
26
28
  from autocoder.auto_coder import main as auto_coder_main
27
29
  from autocoder.common.command_completer import CommandTextParser
@@ -55,6 +57,8 @@ from autocoder.utils.llms import get_single_llm
55
57
  import pkg_resources
56
58
  from autocoder.common.printer import Printer
57
59
  from autocoder.utils.thread_utils import run_in_thread,run_in_raw_thread
60
+ from autocoder.common.command_completer import CommandCompleter,FileSystemModel as CCFileSystemModel,MemoryConfig as CCMemoryModel
61
+ from autocoder.common.conf_validator import ConfigValidator
58
62
 
59
63
  class SymbolItem(BaseModel):
60
64
  symbol_name: str
@@ -83,8 +87,8 @@ def parse_arguments():
83
87
  parser.add_argument(
84
88
  "--product_mode",
85
89
  type=str,
86
- default="pro",
87
- help="The mode of the auto-coder.chat, lite/pro default is pro",
90
+ default="lite",
91
+ help="The mode of the auto-coder.chat, lite/pro default is lite",
88
92
  )
89
93
 
90
94
  parser.add_argument("--lite", action="store_true", help="Lite mode")
@@ -127,6 +131,8 @@ commands = [
127
131
  "/revert",
128
132
  "/index/query",
129
133
  "/index/build",
134
+ "/index/export",
135
+ "/index/import",
130
136
  "/exclude_dirs",
131
137
  "/help",
132
138
  "/shell",
@@ -138,6 +144,7 @@ commands = [
138
144
  "/design",
139
145
  "/mcp",
140
146
  "/models",
147
+ "/auto",
141
148
  ]
142
149
 
143
150
 
@@ -581,6 +588,9 @@ def configure(conf: str, skip_print=False):
581
588
  if not value:
582
589
  printer.print_in_terminal("config_value_empty", style="red")
583
590
  return
591
+ product_mode = memory["conf"].get("product_mode",None)
592
+ if product_mode:
593
+ ConfigValidator.validate(key, value, product_mode)
584
594
  memory["conf"][key] = value
585
595
  save_memory()
586
596
  if not skip_print:
@@ -630,384 +640,6 @@ def get_symbol_list() -> List[SymbolItem]:
630
640
  return list_of_symbols
631
641
 
632
642
 
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
643
  def save_memory():
1012
644
  with open(os.path.join(base_persist_dir, "memory.json"), "w") as f:
1013
645
  json.dump(memory, f, indent=2, ensure_ascii=False)
@@ -1023,6 +655,21 @@ def load_memory():
1023
655
  completer.update_current_files(memory["current_files"]["files"])
1024
656
 
1025
657
 
658
+ completer = CommandCompleter(commands,
659
+ file_system_model=CCFileSystemModel(project_root=project_root,
660
+ defaut_exclude_dirs=defaut_exclude_dirs,
661
+ get_all_file_names_in_project=get_all_file_names_in_project,
662
+ get_all_file_in_project=get_all_file_in_project,
663
+ get_all_dir_names_in_project=get_all_dir_names_in_project,
664
+ get_all_file_in_project_with_dot=get_all_file_in_project_with_dot,
665
+ get_symbol_list=get_symbol_list
666
+ ),
667
+ memory_model=CCMemoryModel(memory=memory,
668
+ save_memory_func=save_memory))
669
+
670
+
671
+
672
+
1026
673
  def print_conf(content:Dict[str,Any]):
1027
674
  """Display configuration dictionary in a Rich table format with enhanced visual styling.
1028
675
 
@@ -1069,6 +716,7 @@ def print_conf(content:Dict[str,Any]):
1069
716
  ))
1070
717
 
1071
718
  def revert():
719
+ result_manager = ResultManager()
1072
720
  last_yaml_file = get_last_yaml_file("actions")
1073
721
  if last_yaml_file:
1074
722
  file_path = os.path.join("actions", last_yaml_file)
@@ -1078,16 +726,24 @@ def revert():
1078
726
  s = output.getvalue()
1079
727
  print(s, flush=True)
1080
728
  if "Successfully reverted changes" in s:
729
+ result_manager.append(content=s, meta={"action": "revert","success":False, "input":{
730
+ }})
1081
731
  print(
1082
732
  "Reverted the last chat action successfully. Remove the yaml file {file_path}"
1083
733
  )
1084
734
  os.remove(file_path)
735
+ else:
736
+ result_manager.append(content=s, meta={"action": "revert","success":False, "input":{
737
+ }})
1085
738
  else:
739
+ result_manager.append(content="No previous chat action found to revert.", meta={"action": "revert","success":False, "input":{
740
+ }})
1086
741
  print("No previous chat action found to revert.")
1087
742
 
1088
743
 
1089
744
  def add_files(args: List[str]):
1090
-
745
+
746
+ result_manager = ResultManager()
1091
747
  if "groups" not in memory["current_files"]:
1092
748
  memory["current_files"]["groups"] = {}
1093
749
  if "groups_info" not in memory["current_files"]:
@@ -1102,6 +758,8 @@ def add_files(args: List[str]):
1102
758
 
1103
759
  if not args:
1104
760
  printer.print_in_terminal("add_files_no_args", style="red")
761
+ result_manager.append(content=printer.get_message_from_key("add_files_no_args"),
762
+ meta={"action": "add_files","success":False, "input":{ "args": args}})
1105
763
  return
1106
764
 
1107
765
  if args[0] == "/refresh":
@@ -1111,6 +769,8 @@ def add_files(args: List[str]):
1111
769
  Panel("Refreshed file list.",
1112
770
  title="Files Refreshed", border_style="green")
1113
771
  )
772
+ result_manager.append(content="Files refreshed.",
773
+ meta={"action": "add_files","success":True, "input":{ "args": args}})
1114
774
  return
1115
775
 
1116
776
  if args[0] == "/group":
@@ -1120,6 +780,8 @@ def add_files(args: List[str]):
1120
780
  Panel("No groups defined.", title="Groups",
1121
781
  border_style="yellow")
1122
782
  )
783
+ result_manager.append(content="No groups defined.",
784
+ meta={"action": "add_files","success":False, "input":{ "args": args}})
1123
785
  else:
1124
786
  table = Table(
1125
787
  title="Defined Groups",
@@ -1150,6 +812,8 @@ def add_files(args: List[str]):
1150
812
  end_section=(i == len(groups) - 1),
1151
813
  )
1152
814
  console.print(Panel(table, border_style="blue"))
815
+ result_manager.append(content="Defined groups.",
816
+ meta={"action": "add_files","success":True, "input":{ "args": args}})
1153
817
  elif len(args) >= 2 and args[1] == "/reset":
1154
818
  memory["current_files"]["current_groups"] = []
1155
819
  console.print(
@@ -1159,6 +823,8 @@ def add_files(args: List[str]):
1159
823
  border_style="green",
1160
824
  )
1161
825
  )
826
+ 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.",
827
+ meta={"action": "add_files","success":True, "input":{ "args": args}})
1162
828
  elif len(args) >= 3 and args[1] == "/add":
1163
829
  group_name = args[2]
1164
830
  groups[group_name] = memory["current_files"]["files"].copy()
@@ -1169,6 +835,9 @@ def add_files(args: List[str]):
1169
835
  border_style="green",
1170
836
  )
1171
837
  )
838
+ result_manager.append(content=f"Added group '{group_name}' with current files.",
839
+ meta={"action": "add_files","success":True, "input":{ "args": args}})
840
+
1172
841
  elif len(args) >= 3 and args[1] == "/drop":
1173
842
  group_name = args[2]
1174
843
  if group_name in groups:
@@ -1185,6 +854,8 @@ def add_files(args: List[str]):
1185
854
  border_style="green",
1186
855
  )
1187
856
  )
857
+ result_manager.append(content=f"Dropped group '{group_name}'.",
858
+ meta={"action": "add_files","success":True, "input":{ "args": args}})
1188
859
  else:
1189
860
  console.print(
1190
861
  Panel(
@@ -1193,6 +864,8 @@ def add_files(args: List[str]):
1193
864
  border_style="red",
1194
865
  )
1195
866
  )
867
+ result_manager.append(content=f"Group '{group_name}' not found.",
868
+ meta={"action": "add_files","success":False, "input":{ "args": args}})
1196
869
  elif len(args) == 3 and args[1] == "/set":
1197
870
  group_name = args[2]
1198
871
 
@@ -1264,6 +937,8 @@ def add_files(args: List[str]):
1264
937
  border_style="red",
1265
938
  )
1266
939
  )
940
+ result_manager.append(content=f"Group(s) not found: {', '.join(missing_groups)}",
941
+ meta={"action": "add_files","success":False, "input":{ "args": args}})
1267
942
 
1268
943
  if merged_files:
1269
944
  memory["current_files"]["files"] = list(merged_files)
@@ -1299,6 +974,8 @@ def add_files(args: List[str]):
1299
974
  border_style="green",
1300
975
  )
1301
976
  )
977
+ result_manager.append(content=f"Active groups: {', '.join(memory['current_files']['current_groups'])}",
978
+ meta={"action": "add_files","success":True, "input":{ "args": args}})
1302
979
  elif not missing_groups:
1303
980
  console.print(
1304
981
  Panel(
@@ -1306,7 +983,9 @@ def add_files(args: List[str]):
1306
983
  title="No Files Added",
1307
984
  border_style="yellow",
1308
985
  )
1309
- )
986
+ )
987
+ result_manager.append(content="No files in the specified groups.",
988
+ meta={"action": "add_files","success":False, "input":{ "args": args}})
1310
989
  else:
1311
990
  existing_files = memory["current_files"]["files"]
1312
991
  matched_files = find_files_in_project(args)
@@ -1328,9 +1007,13 @@ def add_files(args: List[str]):
1328
1007
  i == len(files_to_add) - 1
1329
1008
  ), # 在最后一行之后不添加分割线
1330
1009
  )
1331
- console.print(Panel(table, border_style="green"))
1010
+ console.print(Panel(table, border_style="green"))
1011
+ result_manager.append(content=f"Added files: {', '.join(files_to_add)}",
1012
+ meta={"action": "add_files","success":True, "input":{ "args": args}})
1332
1013
  else:
1333
1014
  printer.print_in_terminal("add_files_matched", style="yellow")
1015
+ result_manager.append(content=f"No files matched.",
1016
+ meta={"action": "add_files","success":False, "input":{ "args": args}})
1334
1017
 
1335
1018
  completer.update_current_files(memory["current_files"]["files"])
1336
1019
  save_memory()
@@ -1339,11 +1022,14 @@ def add_files(args: List[str]):
1339
1022
  def remove_files(file_names: List[str]):
1340
1023
  project_root = os.getcwd()
1341
1024
  printer = Printer()
1025
+ result_manager = ResultManager()
1342
1026
 
1343
1027
  if "/all" in file_names:
1344
1028
  memory["current_files"]["files"] = []
1345
1029
  memory["current_files"]["current_groups"] = []
1346
1030
  printer.print_in_terminal("remove_files_all", style="green")
1031
+ result_manager.append(content="All files removed.",
1032
+ meta={"action": "remove_files","success":True, "input":{ "file_names": file_names}})
1347
1033
  else:
1348
1034
  removed_files = []
1349
1035
  for file in memory["current_files"]["files"]:
@@ -1366,9 +1052,13 @@ def remove_files(file_names: List[str]):
1366
1052
  console = Console()
1367
1053
  console.print(
1368
1054
  Panel(table, border_style="green",
1369
- title=printer.get_message_from_key("files_removed")))
1055
+ title=printer.get_message_from_key("files_removed")))
1056
+ result_manager.append(content=f"Removed files: {', '.join(removed_files)}",
1057
+ meta={"action": "remove_files","success":True, "input":{ "file_names": file_names}})
1370
1058
  else:
1371
1059
  printer.print_in_terminal("remove_files_none", style="yellow")
1060
+ result_manager.append(content=printer.get_message_from_key("remove_files_none"),
1061
+ meta={"action": "remove_files","success":False, "input":{ "file_names": file_names}})
1372
1062
 
1373
1063
  completer.update_current_files(memory["current_files"]["files"])
1374
1064
  save_memory()
@@ -1890,6 +1580,8 @@ def coding(query: str):
1890
1580
 
1891
1581
  yaml_content = convert_yaml_config_to_str(yaml_config=yaml_config)
1892
1582
 
1583
+ md5 = hashlib.md5(yaml_content.encode("utf-8")).hexdigest()
1584
+
1893
1585
  execute_file = os.path.join("actions", latest_yaml_file)
1894
1586
  with open(os.path.join(execute_file), "w") as f:
1895
1587
  f.write(yaml_content)
@@ -1897,6 +1589,10 @@ def coding(query: str):
1897
1589
  def execute_chat():
1898
1590
  cmd = ["--file", execute_file]
1899
1591
  auto_coder_main(cmd)
1592
+ result_manager = ResultManager()
1593
+ result_manager.append(content="", meta={"commit_message": f"auto_coder_{latest_yaml_file}_{md5}","action": "coding", "input":{
1594
+ "query": query
1595
+ }})
1900
1596
 
1901
1597
  execute_chat()
1902
1598
  else:
@@ -2174,11 +1870,18 @@ def generate_shell_command(input_text):
2174
1870
  auto_coder_main(["agent", "generate_command", "--file", execute_file])
2175
1871
  with open(os.path.join(".auto-coder", "exchange.txt"), "r") as f:
2176
1872
  shell_script = f.read()
1873
+ result_manager = ResultManager()
1874
+ result_manager.add_result(content=shell_script,meta={
1875
+ "action": "generate_shell_command",
1876
+ "input": {
1877
+ "query": input_text
1878
+ }
1879
+ })
2177
1880
  return shell_script
2178
1881
  finally:
2179
1882
  os.remove(execute_file)
2180
1883
 
2181
- def manage_models(params, query: str):
1884
+ def manage_models(query: str):
2182
1885
  """
2183
1886
  Handle /models subcommands:
2184
1887
  /models /list - List all models (default + custom)
@@ -2189,7 +1892,8 @@ def manage_models(params, query: str):
2189
1892
  printer = Printer()
2190
1893
  console = Console()
2191
1894
 
2192
- if params.product_mode != "lite":
1895
+ product_mode = memory.get("product_mode", "lite")
1896
+ if product_mode != "lite":
2193
1897
  printer.print_in_terminal("models_lite_only", style="red")
2194
1898
  return
2195
1899
 
@@ -2240,7 +1944,8 @@ def manage_models(params, query: str):
2240
1944
 
2241
1945
  if not subcmd:
2242
1946
  printer.print_in_terminal("models_usage")
2243
-
1947
+
1948
+ result_manager = ResultManager()
2244
1949
  if subcmd == "/list":
2245
1950
  if models_data:
2246
1951
  # Sort models by speed (average_speed)
@@ -2277,8 +1982,21 @@ def manage_models(params, query: str):
2277
1982
  f"{m.get('average_speed', 0.0):.3f}"
2278
1983
  )
2279
1984
  console.print(table)
1985
+ result_manager.add_result(content=json.dumps(sorted_models,ensure_ascii=False),meta={
1986
+ "action": "models",
1987
+ "input": {
1988
+ "query": query
1989
+ }
1990
+ })
1991
+
2280
1992
  else:
2281
1993
  printer.print_in_terminal("models_no_models", style="yellow")
1994
+ result_manager.add_result(content="No models found",meta={
1995
+ "action": "models",
1996
+ "input": {
1997
+ "query": query
1998
+ }
1999
+ })
2282
2000
 
2283
2001
  elif subcmd == "/input_price":
2284
2002
  args = query.strip().split()
@@ -2288,11 +2006,35 @@ def manage_models(params, query: str):
2288
2006
  price = float(args[1])
2289
2007
  if models_module.update_model_input_price(name, price):
2290
2008
  printer.print_in_terminal("models_input_price_updated", style="green", name=name, price=price)
2009
+ result_manager.add_result(content=f"models_input_price_updated: {name} {price}",meta={
2010
+ "action": "models",
2011
+ "input": {
2012
+ "query": query
2013
+ }
2014
+ })
2291
2015
  else:
2292
2016
  printer.print_in_terminal("models_not_found", style="red", name=name)
2017
+ result_manager.add_result(content=f"models_not_found: {name}",meta={
2018
+ "action": "models",
2019
+ "input": {
2020
+ "query": query
2021
+ }
2022
+ })
2293
2023
  except ValueError as e:
2024
+ result_manager.add_result(content=f"models_invalid_price: {str(e)}",meta={
2025
+ "action": "models",
2026
+ "input": {
2027
+ "query": query
2028
+ }
2029
+ })
2294
2030
  printer.print_in_terminal("models_invalid_price", style="red", error=str(e))
2295
2031
  else:
2032
+ result_manager.add_result(content=printer.get_message_from_key("models_input_price_usage"),meta={
2033
+ "action": "models",
2034
+ "input": {
2035
+ "query": query
2036
+ }
2037
+ })
2296
2038
  printer.print_in_terminal("models_input_price_usage", style="red")
2297
2039
 
2298
2040
  elif subcmd == "/output_price":
@@ -2303,11 +2045,35 @@ def manage_models(params, query: str):
2303
2045
  price = float(args[1])
2304
2046
  if models_module.update_model_output_price(name, price):
2305
2047
  printer.print_in_terminal("models_output_price_updated", style="green", name=name, price=price)
2048
+ result_manager.add_result(content=f"models_output_price_updated: {name} {price}",meta={
2049
+ "action": "models",
2050
+ "input": {
2051
+ "query": query
2052
+ }
2053
+ })
2306
2054
  else:
2307
2055
  printer.print_in_terminal("models_not_found", style="red", name=name)
2056
+ result_manager.add_result(content=f"models_not_found: {name}",meta={
2057
+ "action": "models",
2058
+ "input": {
2059
+ "query": query
2060
+ }
2061
+ })
2308
2062
  except ValueError as e:
2309
2063
  printer.print_in_terminal("models_invalid_price", style="red", error=str(e))
2064
+ result_manager.add_result(content=f"models_invalid_price: {str(e)}",meta={
2065
+ "action": "models",
2066
+ "input": {
2067
+ "query": query
2068
+ }
2069
+ })
2310
2070
  else:
2071
+ result_manager.add_result(content=printer.get_message_from_key("models_output_price_usage"),meta={
2072
+ "action": "models",
2073
+ "input": {
2074
+ "query": query
2075
+ }
2076
+ })
2311
2077
  printer.print_in_terminal("models_output_price_usage", style="red")
2312
2078
 
2313
2079
  elif subcmd == "/speed":
@@ -2318,11 +2084,35 @@ def manage_models(params, query: str):
2318
2084
  speed = float(args[1])
2319
2085
  if models_module.update_model_speed(name, speed):
2320
2086
  printer.print_in_terminal("models_speed_updated", style="green", name=name, speed=speed)
2087
+ result_manager.add_result(content=f"models_speed_updated: {name} {speed}",meta={
2088
+ "action": "models",
2089
+ "input": {
2090
+ "query": query
2091
+ }
2092
+ })
2321
2093
  else:
2322
2094
  printer.print_in_terminal("models_not_found", style="red", name=name)
2095
+ result_manager.add_result(content=f"models_not_found: {name}",meta={
2096
+ "action": "models",
2097
+ "input": {
2098
+ "query": query
2099
+ }
2100
+ })
2323
2101
  except ValueError as e:
2324
2102
  printer.print_in_terminal("models_invalid_speed", style="red", error=str(e))
2103
+ result_manager.add_result(content=f"models_invalid_speed: {str(e)}",meta={
2104
+ "action": "models",
2105
+ "input": {
2106
+ "query": query
2107
+ }
2108
+ })
2325
2109
  else:
2110
+ result_manager.add_result(content=printer.get_message_from_key("models_speed_usage"),meta={
2111
+ "action": "models",
2112
+ "input": {
2113
+ "query": query
2114
+ }
2115
+ })
2326
2116
  printer.print_in_terminal("models_speed_usage", style="red")
2327
2117
 
2328
2118
  elif subcmd == "/speed-test":
@@ -2343,7 +2133,14 @@ def manage_models(params, query: str):
2343
2133
  if args and args[0].isdigit():
2344
2134
  test_rounds = int(args[0])
2345
2135
 
2346
- render_speed_test_in_terminal(params.product_mode, test_rounds,enable_long_context=enable_long_context)
2136
+ render_speed_test_in_terminal(product_mode, test_rounds,enable_long_context=enable_long_context)
2137
+ ## 等待优化,获取明细数据
2138
+ result_manager.add_result(content="models test success",meta={
2139
+ "action": "models",
2140
+ "input": {
2141
+ "query": query
2142
+ }
2143
+ })
2347
2144
 
2348
2145
  elif subcmd == "/add":
2349
2146
  # Support both simplified and legacy formats
@@ -2353,11 +2150,29 @@ def manage_models(params, query: str):
2353
2150
  name, api_key = args[0], args[1]
2354
2151
  result = models_module.update_model_with_api_key(name, api_key)
2355
2152
  if result:
2153
+ result_manager.add_result(content=f"models_added: {name}",meta={
2154
+ "action": "models",
2155
+ "input": {
2156
+ "query": query
2157
+ }
2158
+ })
2356
2159
  printer.print_in_terminal("models_added", style="green", name=name)
2357
2160
  else:
2161
+ result_manager.add_result(content=f"models_add_failed: {name}",meta={
2162
+ "action": "models",
2163
+ "input": {
2164
+ "query": query
2165
+ }
2166
+ })
2358
2167
  printer.print_in_terminal("models_add_failed", style="red", name=name)
2359
2168
  else:
2360
2169
  printer.print_in_terminal("models_add_usage", style="red")
2170
+ result_manager.add_result(content=printer.get_message_from_key("models_add_usage"),meta={
2171
+ "action": "models",
2172
+ "input": {
2173
+ "query": query
2174
+ }
2175
+ })
2361
2176
 
2362
2177
  elif subcmd == "/add_model":
2363
2178
  # Parse key=value pairs: /models /add_model name=abc base_url=http://xx ...
@@ -2379,6 +2194,12 @@ def manage_models(params, query: str):
2379
2194
  # Check duplication
2380
2195
  if any(m["name"] == data_dict["name"] for m in models_data):
2381
2196
  printer.print_in_terminal("models_add_model_exists", style="yellow", name=data_dict["name"])
2197
+ result_manager.add_result(content=printer.get_message_from_key("models_add_model_exists",name=data_dict["name"]),meta={
2198
+ "action": "models",
2199
+ "input": {
2200
+ "query": query
2201
+ }
2202
+ })
2382
2203
  return
2383
2204
 
2384
2205
  # Create model with defaults
@@ -2395,22 +2216,51 @@ def manage_models(params, query: str):
2395
2216
  models_data.append(final_model)
2396
2217
  models_module.save_models(models_data)
2397
2218
  printer.print_in_terminal("models_add_model_success", style="green", name=data_dict["name"])
2219
+ result_manager.add_result(content=f"models_add_model_success: {data_dict['name']}",meta={
2220
+ "action": "models",
2221
+ "input": {
2222
+ "query": query
2223
+ }
2224
+ })
2398
2225
 
2399
2226
  elif subcmd == "/remove":
2400
2227
  args = query.strip().split(" ")
2401
2228
  if len(args) < 1:
2402
2229
  printer.print_in_terminal("models_add_usage", style="red")
2230
+ result_manager.add_result(content=printer.get_message_from_key("models_add_usage"),meta={
2231
+ "action": "models",
2232
+ "input": {
2233
+ "query": query
2234
+ }
2235
+ })
2403
2236
  return
2404
2237
  name = args[0]
2405
2238
  filtered_models = [m for m in models_data if m["name"] != name]
2406
2239
  if len(filtered_models) == len(models_data):
2407
2240
  printer.print_in_terminal("models_add_model_remove", style="yellow", name=name)
2241
+ result_manager.add_result(content=printer.get_message_from_key("models_add_model_remove",name=name),meta={
2242
+ "action": "models",
2243
+ "input": {
2244
+ "query": query
2245
+ }
2246
+ })
2408
2247
  return
2409
2248
  models_module.save_models(filtered_models)
2410
2249
  printer.print_in_terminal("models_add_model_removed", style="green", name=name)
2411
-
2250
+ result_manager.add_result(content=printer.get_message_from_key("models_add_model_removed",name=name),meta={
2251
+ "action": "models",
2252
+ "input": {
2253
+ "query": query
2254
+ }
2255
+ })
2412
2256
  else:
2413
2257
  printer.print_in_terminal("models_unknown_subcmd", style="yellow", subcmd=subcmd)
2258
+ result_manager.add_result(content=printer.get_message_from_key("models_unknown_subcmd",subcmd=subcmd),meta={
2259
+ "action": "models",
2260
+ "input": {
2261
+ "query": query
2262
+ }
2263
+ })
2414
2264
 
2415
2265
  def exclude_dirs(dir_names: List[str]):
2416
2266
  new_dirs = dir_names
@@ -2452,6 +2302,63 @@ def index_build():
2452
2302
  os.remove(yaml_file)
2453
2303
 
2454
2304
 
2305
+ def get_final_config()->AutoCoderArgs:
2306
+ conf = memory.get("conf", {})
2307
+ yaml_config = {
2308
+ "include_file": ["./base/base.yml"],
2309
+ "auto_merge": conf.get("auto_merge", "editblock"),
2310
+ "human_as_model": conf.get("human_as_model", "false") == "true",
2311
+ "skip_build_index": conf.get("skip_build_index", "true") == "true",
2312
+ "skip_confirm": conf.get("skip_confirm", "true") == "true",
2313
+ "silence": conf.get("silence", "true") == "true",
2314
+ "include_project_structure": conf.get("include_project_structure", "true")
2315
+ == "true",
2316
+ }
2317
+ for key, value in conf.items():
2318
+ converted_value = convert_config_value(key, value)
2319
+ if converted_value is not None:
2320
+ yaml_config[key] = converted_value
2321
+
2322
+ temp_yaml = os.path.join("actions", f"{uuid.uuid4()}.yml")
2323
+ try:
2324
+ with open(temp_yaml, "w") as f:
2325
+ f.write(convert_yaml_config_to_str(yaml_config=yaml_config))
2326
+ args = convert_yaml_to_config(temp_yaml)
2327
+ finally:
2328
+ if os.path.exists(temp_yaml):
2329
+ os.remove(temp_yaml)
2330
+ return args
2331
+
2332
+ def help(query: str):
2333
+ from autocoder.common.auto_configure import ConfigAutoTuner,MemoryConfig,AutoConfigRequest
2334
+ args = get_final_config()
2335
+ product_mode = memory.get("product_mode", "lite")
2336
+ llm = get_single_llm(args.chat_model or args.model, product_mode=product_mode)
2337
+ auto_config_tuner = ConfigAutoTuner(llm=llm, memory_config=MemoryConfig(memory=memory, save_memory_func=save_memory))
2338
+ auto_config_tuner.tune(AutoConfigRequest(query=query))
2339
+
2340
+ @run_in_raw_thread()
2341
+ def index_export(export_path: str):
2342
+ from autocoder.common.index_import_export import export_index
2343
+ from autocoder.common.printer import Printer
2344
+ printer = Printer()
2345
+ project_root = os.getcwd()
2346
+ if export_index(project_root, export_path):
2347
+ printer.print_in_terminal("index_export_success", path=export_path)
2348
+ else:
2349
+ printer.print_in_terminal("index_export_fail", path=export_path)
2350
+
2351
+ @run_in_raw_thread()
2352
+ def index_import(import_path: str):
2353
+ from autocoder.common.index_import_export import import_index
2354
+ from autocoder.common.printer import Printer
2355
+ printer = Printer()
2356
+ project_root = os.getcwd()
2357
+ if import_index(project_root, import_path):
2358
+ printer.print_in_terminal("index_import_success", path=import_path)
2359
+ else:
2360
+ printer.print_in_terminal("index_import_fail", path=import_path)
2361
+
2455
2362
  @run_in_raw_thread()
2456
2363
  def index_query(query: str):
2457
2364
  conf = memory.get("conf", {})
@@ -2634,6 +2541,58 @@ def lib_command(args: List[str]):
2634
2541
  else:
2635
2542
  console.print(f"Unknown subcommand: {subcommand}")
2636
2543
 
2544
+ @run_in_raw_thread()
2545
+ def auto_command(params,query: str):
2546
+ """处理/auto指令"""
2547
+ from autocoder.commands.auto_command import CommandAutoTuner, AutoCommandRequest, CommandConfig, MemoryConfig
2548
+ args = get_final_config()
2549
+ # help(query)
2550
+
2551
+ # 准备请求参数
2552
+ request = AutoCommandRequest(
2553
+ user_input=query
2554
+ )
2555
+
2556
+ # 初始化调优器
2557
+ llm = get_single_llm(args.chat_model or args.model,product_mode=args.product_mode)
2558
+ tuner = CommandAutoTuner(llm,
2559
+ args=args,
2560
+ memory_config=MemoryConfig(memory=memory, save_memory_func=save_memory),
2561
+ command_config=CommandConfig(
2562
+ add_files=add_files,
2563
+ remove_files=remove_files,
2564
+ list_files=list_files,
2565
+ conf=configure,
2566
+ revert=revert,
2567
+ commit=commit,
2568
+ help=help,
2569
+ exclude_dirs=exclude_dirs,
2570
+ ask=ask,
2571
+ chat=chat,
2572
+ coding=coding,
2573
+ design=design,
2574
+ summon=summon,
2575
+ lib=lib_command,
2576
+ mcp=mcp,
2577
+ models=manage_models,
2578
+ index_build=index_build,
2579
+ index_query=index_query,
2580
+ execute_shell_command=execute_shell_command,
2581
+ generate_shell_command=generate_shell_command
2582
+ ))
2583
+
2584
+ # 生成建议
2585
+ response = tuner.analyze(request)
2586
+ printer = Printer()
2587
+ # 显示建议
2588
+ console = Console()
2589
+ console.print(Panel(
2590
+ Markdown(response.reasoning or ""),
2591
+ title=printer.get_message_from_key_with_format("auto_command_reasoning_title"),
2592
+ border_style="blue",
2593
+ padding=(1, 2)
2594
+ ))
2595
+
2637
2596
 
2638
2597
  def main():
2639
2598
  from autocoder.rag.variable_holder import VariableHolder
@@ -2691,7 +2650,7 @@ def main():
2691
2650
  @kb.add("c-k")
2692
2651
  def _(event):
2693
2652
  if "mode" not in memory:
2694
- memory["mode"] = "normal"
2653
+ memory["mode"] = "auto_detect"
2695
2654
 
2696
2655
  current_mode = memory["mode"]
2697
2656
  if current_mode == "normal":
@@ -2715,7 +2674,7 @@ def main():
2715
2674
 
2716
2675
  def get_bottom_toolbar():
2717
2676
  if "mode" not in memory:
2718
- memory["mode"] = "normal"
2677
+ memory["mode"] = "auto_detect"
2719
2678
  mode = memory["mode"]
2720
2679
  human_as_model = memory["conf"].get("human_as_model", "false")
2721
2680
  if mode not in MODES:
@@ -2777,7 +2736,7 @@ def main():
2777
2736
  new_prompt = ""
2778
2737
 
2779
2738
  if "mode" not in memory:
2780
- memory["mode"] = "normal"
2739
+ memory["mode"] = "auto_detect"
2781
2740
 
2782
2741
  # 处理 user_input 的空格
2783
2742
  if user_input:
@@ -2790,11 +2749,8 @@ def main():
2790
2749
  and user_input
2791
2750
  and not user_input.startswith("/")
2792
2751
  ):
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
2752
+ auto_command(ARGS,user_input)
2753
+
2798
2754
  elif memory["mode"] == "voice_input" and not user_input.startswith("/"):
2799
2755
  text = voice_input()
2800
2756
  new_prompt = "/coding " + text
@@ -2816,6 +2772,20 @@ def main():
2816
2772
 
2817
2773
  elif user_input.startswith("/index/build"):
2818
2774
  index_build()
2775
+
2776
+ elif user_input.startswith("/index/export"):
2777
+ export_path = user_input[len("/index/export"):].strip()
2778
+ if not export_path:
2779
+ print("Please specify the export path")
2780
+ else:
2781
+ index_export(export_path)
2782
+
2783
+ elif user_input.startswith("/index/import"):
2784
+ import_path = user_input[len("/index/import"):].strip()
2785
+ if not import_path:
2786
+ print("Please specify the import path")
2787
+ else:
2788
+ index_import(import_path)
2819
2789
 
2820
2790
  elif user_input.startswith("/list_files"):
2821
2791
  list_files()
@@ -2825,7 +2795,7 @@ def main():
2825
2795
  if not query:
2826
2796
  print("Please enter your query.")
2827
2797
  else:
2828
- manage_models(ARGS,query)
2798
+ manage_models(query)
2829
2799
 
2830
2800
  elif user_input.startswith("/mode"):
2831
2801
  conf = user_input[len("/mode"):].strip()
@@ -2846,7 +2816,12 @@ def main():
2846
2816
  query = user_input[len("/commit"):].strip()
2847
2817
  commit(query)
2848
2818
  elif user_input.startswith("/help"):
2849
- show_help()
2819
+ query = user_input[len("/help"):].strip()
2820
+ if not query:
2821
+ show_help()
2822
+ else:
2823
+ help(query)
2824
+
2850
2825
  elif user_input.startswith("/exclude_dirs"):
2851
2826
  dir_names = user_input[len(
2852
2827
  "/exclude_dirs"):].strip().split(",")
@@ -2899,6 +2874,9 @@ def main():
2899
2874
  else:
2900
2875
  mcp(query)
2901
2876
 
2877
+ elif user_input.startswith("/auto"):
2878
+ query = user_input[len("/auto"):].strip()
2879
+ auto_command(ARGS,query)
2902
2880
  elif user_input.startswith("/debug"):
2903
2881
  code = user_input[len("/debug"):].strip()
2904
2882
  try: