"""xiaoli-cli 7.0.0 主类""" import os, sys, json, importlib from datetime import datetime from .types import ToolCall, ToolResult from .history import History from .safety import Safety from .display import Display from .engine import EngineAdapter from .runner import Runner class XiaoliCLI: def __init__(self): self.shared_conversation_history = [] self.max_history = 999999 self.current_engine = None self.engines = {} self.engine_name = "openai" self.liugin_manager = None self.tools = [] self.liugin_commands = {} self.chat_dir = "chat_history" self.memory_manager = None self._load_config() self._load_engines() self._load_plugins() self._init_memory() self._init_core() self._register_commands() print("xiaoli-cli 7.0.0 初始化完成") def _load_config(self): try: from xcli_core.config import get_system_config self.engine_name = get_system_config("default_engine", "openai") except Exception: self.engine_name = "openai" def _load_engines(self): print("正在加载AI引擎...") d = os.path.join(os.path.dirname(os.path.dirname(__file__)), "ai_engines") if not os.path.isdir(d): return for fn in os.listdir(d): if fn.endswith("_engine.py") and not fn.startswith("__"): mn = fn[:-3] try: mod = importlib.import_module(f"ai_engines.{mn}") for an in dir(mod): obj = getattr(mod, an) if isinstance(obj, type) and an.endswith("AI") and hasattr(obj, "generate_response"): eng = obj() eng.cli = self eng.shared_conversation_history = self.shared_conversation_history self.engines[eng.name] = eng print(f" 已加载: {eng.name} ({an})") except Exception as e: print(f" [ERROR] {mn}: {e}") if self.engine_name not in self.engines and self.engines: self.engine_name = list(self.engines.keys())[0] self.current_engine = self.engines.get(self.engine_name) print(f" 共 {len(self.engines)} 个引擎,当前: {self.engine_name}") def _load_plugins(self): print("正在加载插件...") try: from unified_tool_manager import UnifiedToolManager self.liugin_manager = UnifiedToolManager(self) pd = os.path.join(os.path.dirname(os.path.dirname(__file__)), "plugins") sd = os.path.join(os.path.dirname(os.path.dirname(__file__)), "skills") self.liugin_manager.initialize(pd, sd) self.tools = self.liugin_manager.tools if hasattr(self.liugin_manager, "tools") else [] print(f" 已加载 {len(self.tools)} 个插件") except Exception as e: print(f" [ERROR] 插件加载失败: {e}") import traceback; traceback.print_exc() def _init_memory(self): try: from xcli_core.memory import MemoryManager self.memory_manager = MemoryManager(self) except Exception: pass def _init_core(self): self.history = History(self.max_history) self.safety = Safety(self, lambda: self.current_engine) self.display = Display() self.engine_adapter = EngineAdapter(self.current_engine) self.runner = Runner(self.engine_adapter, self.liugin_manager, self.history, self.safety, self.display) self.runner.set_prompt_builder(self._build_prompt) def _build_prompt(self): p = '你叫小狸,是一个强大的智能编程助手。\n\n' p += '【工具调用】\n当你需要执行操作时,直接调用工具。\n工具执行后,根据结果继续回复或调用下一个工具。\n\n' p += '【补充回复】\n如果你需要补充之前的回复,输出:\n' p += '{"action": "continue", "content": "要补充的内容"}\n\n' p += '【核心工具】\n' p += '1. code_editor - 代码编辑\n2. code_search - 代码搜索\n3. git_tools - Git\n' p += '4. cmd_executor - Shell命令\n5. file_manager - 文件管理\n6. browser_auto - 浏览器\n' p += '7. sub_agent - 子Agent\n8. auto_engineer - 工程化\n9. network_tools - 网络\n' p += '10. ai_search - 网页内容 (url/search/web)\n\n' p += '说话时可适当加nyan表现可爱性格。' return p def register_liugin_command(self, name, handler): self.liugin_commands[name] = handler def process_conversation(self, user_input): return self.runner.run(user_input) def switch_engine(self, name): if name in self.engines: self.engine_name = name self.current_engine = self.engines[name] self.current_engine.shared_conversation_history = self.shared_conversation_history self.engine_adapter = EngineAdapter(self.current_engine) self.runner.set_engine(self.engine_adapter) self.safety._engine = lambda: self.current_engine try: from xcli_core.config import set_system_config set_system_config("default_engine", name) except Exception: pass return f"已切换到: {name}" return f"引擎 {name} 不存在" def _register_commands(self): self._commands = { "/help": self._cmd_help, "/engine": self._cmd_engine, "/mode": self._cmd_mode, "/clear": self._cmd_clear, "/history": self._cmd_history, "/tools": self._cmd_tools, "/exit": self._cmd_exit, "/quit": self._cmd_exit, "/save": self._cmd_save, "/config": self._cmd_config, "/new": self._cmd_new, } def handle_command(self, user_input): parts = user_input.strip().split(None, 1) cmd = parts[0].lower().lstrip("/") args = parts[1] if len(parts) > 1 else "" h = self._commands.get("/" + cmd) if h: return h(args) if cmd in self.liugin_commands: try: r = self.liugin_commands[cmd](args) if r: print(r) except Exception as e: print(f"命令失败: {e}") return "" return None def _cmd_help(self, a): return "\n".join(["/help - 帮助", "/engine [list|] - 引擎", "/mode [0-3] - 安全模式", "/clear - 清空历史", "/history - 历史", "/tools - 工具", "/save - 保存", "/new - 新对话", "/config - 配置", "/exit - 退出"]) def _cmd_engine(self, a): if not a or a == "list": lines = [f"当前: {self.engine_name}", "可用:"] for n in self.engines: lines.append(f" {n}{' <==' if n == self.engine_name else ''}") return "\n".join(lines) return self.switch_engine(a) def _cmd_mode(self, a): if not a: return f"当前模式: {self.safety.get_mode()}" try: m = int(a) self.safety.set_mode(m) names = {0: "无限制", 1: "普通", 2: "人工", 3: "计划"} return f"已切换: {names.get(m, '未知')}" except ValueError: return "用法: /mode [0-3]" def _cmd_clear(self, a): self.history.clear() self.shared_conversation_history.clear() return "已清空" def _cmd_history(self, a): msgs = self.history.snapshot() if not msgs: return "无历史" lines = [f"共 {len(msgs)} 条:"] for m in msgs[-10:]: c = m.content[:50] + "..." if len(m.content) > 50 else m.content lines.append(f" [{m.role}] {c}") return "\n".join(lines) def _cmd_tools(self, a): if not self.tools: return "无工具" lines = [f"共 {len(self.tools)} 个:"] for t in self.tools: lines.append(f" {t.get('name','')}: {t.get('description','')[:60]}") return "\n".join(lines) def _cmd_save(self, a): try: os.makedirs(self.chat_dir, exist_ok=True) ts = datetime.now().strftime("%Y%m%d_%H%M%S") fp = os.path.join(self.chat_dir, f"{ts}_chat.json") msgs = self.history.snapshot() data = [{"role": m.role, "content": m.content} for m in msgs] with open(fp, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) return f"已保存: {fp}" except Exception as e: return f"失败: {e}" def _cmd_new(self, a): self._cmd_save("") self.history.clear() self.shared_conversation_history.clear() return "新对话" def _cmd_config(self, a): try: with open("config.json", "r", encoding="utf-8") as f: return json.dumps(json.load(f), ensure_ascii=False, indent=2)[:1000] except Exception as e: return f"失败: {e}" def _cmd_exit(self, a): self._cmd_save("") return "__EXIT__" def run(self): try: from colorama import init, Fore, Style init() except ImportError: class _D: CYAN = GREEN = YELLOW = RESET_ALL = '' Fore = Style = _D() print() print(f" {Fore.CYAN}+========================================+{Style.RESET_ALL}") print(f" {Fore.CYAN}| xiaoli-cli 7.0.0 (重构版) |{Style.RESET_ALL}") print(f" {Fore.CYAN}| 小狸 AI 编程助手 |{Style.RESET_ALL}") print(f" {Fore.CYAN}+========================================+{Style.RESET_ALL}") print() print(f" 引擎: {Fore.GREEN}{self.engine_name}{Style.RESET_ALL if hasattr(Style,'RESET_ALL') else ''}") print(f" 工具: {Fore.GREEN}{len(self.tools)}{Style.RESET_ALL if hasattr(Style,'RESET_ALL') else ''} 个插件") print(f" FC: {'支持' if self.engine_adapter.supports_fc() else '不支持'}") print(f" 输入 /help 查看命令") print() while True: try: ui = input(f"{Fore.CYAN}[用户ID: xiaoli]{Style.RESET_ALL if hasattr(Style,'RESET_ALL') else ''} ").strip() if not ui: continue if ui.startswith("/"): r = self.handle_command(ui) if r == "__EXIT__": print(f" {Fore.YELLOW}再见~{Style.RESET_ALL if hasattr(Style,'RESET_ALL') else ''}") break if r: print(r) continue self.process_conversation(ui) print() except KeyboardInterrupt: print(f"\n {Fore.YELLOW}(Ctrl+C, /exit退出){Style.RESET_ALL if hasattr(Style,'RESET_ALL') else ''}") except EOFError: break except Exception as e: print(f" {Fore.RED}错误: {e}{Style.RESET_ALL if hasattr(Style,'RESET_ALL') else ''}")