""" 日记整理 Agent 主入口 支持命令行调用和外部集成 """ import argparse import asyncio import json import logging import sys from pathlib import Path from typing import Dict, Any, Optional import sys from pathlib import Path from typing import Dict, Any, Optional # Handle imports with both relative and absolute paths try: from .dependency_manager import get_dependency_manager from .agent_core import Agent, SkillResult from .commands.organize_command import OrganizeCommand except ImportError: # Fallback to absolute imports when running as script from dependency_manager import get_dependency_manager from agent_core import Agent, SkillResult from commands.organize_command import OrganizeCommand # Try to import yaml with graceful degradation dependency_manager = get_dependency_manager() yaml = dependency_manager.get_module('yaml') class JournalOrganizerAgent: """日记整理 Agent""" def __init__(self, config_file: Optional[str] = None): """ 初始化 Agent Args: config_file: 配置文件路径 """ self.logger = logging.getLogger("JournalOrganizerAgent") self.config = self._load_config(config_file) self.agent = Agent("JournalOrganizer", self.config) self._register_commands() def _load_config(self, config_file: Optional[str] = None) -> Dict[str, Any]: """ 加载配置文件 Args: config_file: 配置文件路径 Returns: 配置字典 """ if config_file and Path(config_file).exists(): config_path = Path(config_file) with config_path.open("r", encoding="utf-8") as f: if config_path.suffix in [".yaml", ".yml"]: return yaml.safe_load(f) or {} elif config_path.suffix == ".json": return json.load(f) # 尝试从默认位置加载 default_paths = [ Path.home() / ".journal_organizer" / "config.yaml", Path.home() / ".journal_organizer" / "config.json", Path.cwd() / "config.yaml", Path.cwd() / "config.json", ] for path in default_paths: if path.exists(): self.logger.info(f"从 {path} 加载配置") with path.open("r", encoding="utf-8") as f: if path.suffix in [".yaml", ".yml"]: return yaml.safe_load(f) or {} else: return json.load(f) self.logger.warning("未找到配置文件,使用默认配置") return {} def _register_commands(self) -> None: """注册所有命令""" self.agent.register_command(OrganizeCommand()) async def run_command( self, command: str, args: Optional[Dict[str, Any]] = None, options: Optional[Dict[str, Any]] = None, ) -> SkillResult: """ 运行命令 Args: command: 命令名称 args: 命令参数 options: 命令选项 Returns: SkillResult: 执行结果 """ return await self.agent.execute_command(command, args, options) def list_commands(self) -> list: """列出所有可用命令""" return self.agent.list_commands() def get_command_info(self, command: str) -> Dict[str, Any]: """获取命令信息""" if command in self.agent.commands: return self.agent.commands[command].get_info() return {} def get_all_commands_info(self) -> Dict[str, Any]: """获取所有命令信息""" return self.agent.get_commands_info() async def main(): """命令行主函数""" parser = argparse.ArgumentParser( description="Obsidian 智能日记整理 Agent", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" 示例: # 整理今天的日记 python -m journal_organizer organize # 整理指定日期的日记 python -m journal_organizer organize --date 2025-12-31 # 列出所有可用命令 python -m journal_organizer list # 显示命令帮助 python -m journal_organizer help organize # 使用指定配置文件 python -m journal_organizer --config /path/to/config.yaml organize """, ) parser.add_argument("--config", type=str, help="配置文件路径") parser.add_argument( "--log-level", type=str, default="INFO", choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], help="日志级别", ) subparsers = parser.add_subparsers(dest="command", help="命令") # organize 命令 organize_parser = subparsers.add_parser("organize", help="整理日记") organize_parser.add_argument("--date", type=str, help="日期 (YYYY-MM-DD)") organize_parser.add_argument("--vault-path", type=str, help="Obsidian vault 路径") organize_parser.add_argument( "--daily-folder", type=str, default="Daily", help="日记文件夹" ) # list 命令 subparsers.add_parser("list", help="列出所有可用命令") # help 命令 help_parser = subparsers.add_parser("help", help="显示命令帮助") help_parser.add_argument("help_command", nargs="?", help="要查看帮助的命令") # info 命令 subparsers.add_parser("info", help="显示 Agent 信息") # check-deps 命令 subparsers.add_parser("check-deps", help="检查依赖项状态") args = parser.parse_args() # 设置日志 logging.basicConfig( level=getattr(logging, args.log_level), format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", ) # 初始化 Agent agent = JournalOrganizerAgent(args.config) # 处理命令 if not args.command: parser.print_help() return if args.command == "organize": # 构建参数 organize_args = {} if args.date: organize_args["date"] = args.date if args.vault_path: organize_args["vault_path"] = args.vault_path if args.daily_folder: organize_args["daily_folder"] = args.daily_folder result = await agent.run_command("organize", organize_args) print(f"\n{'='*50}") print("执行结果") print("=" * 50) print(result.to_json()) return 0 if result.success else 1 elif args.command == "list": commands = agent.list_commands() print("\n可用命令:") for cmd in commands: print(f" - {cmd}") return 0 elif args.command == "help": if args.help_command: info = agent.get_command_info(args.help_command) if info: print(f"\n命令: {info['name']}") print(f"描述: {info['description']}") if info.get("aliases"): print(f"别名: {', '.join(info['aliases'])}") print("\nSkills:") for skill_name, skill_info in info.get("skills", {}).items(): print(f" - {skill_name}: {skill_info['description']}") else: print(f"未找到命令: {args.help_command}") return 1 else: parser.print_help() return 0 elif args.command == "info": info = agent.get_all_commands_info() print(f"\n{json.dumps(info, ensure_ascii=False, indent=2)}") return 0 elif args.command == "check-deps": print("\n🔍 检查依赖项状态...") print(dependency_manager.get_dependency_status_report()) missing_deps = dependency_manager.get_missing_dependencies() if missing_deps: print(f"\n📋 安装说明:") print(dependency_manager.get_installation_instructions(missing_only=True)) return 1 else: print(f"\n✅ 所有依赖项都已正确安装!") return 0 def run_command_sync( command: str, args: Optional[Dict[str, Any]] = None, config_file: Optional[str] = None, ) -> Dict[str, Any]: """ 同步运行命令(用于外部调用) Args: command: 命令名称 args: 命令参数 config_file: 配置文件路径 Returns: 执行结果字典 """ agent = JournalOrganizerAgent(config_file) result = asyncio.run(agent.run_command(command, args)) return result.to_dict() if __name__ == "__main__": exit_code = asyncio.run(main()) sys.exit(exit_code or 0)