""" 日记整理命令 负责协调各个 Skill 完成日记的分析和整理 """ from datetime import datetime from pathlib import Path from typing import Dict, Any, Optional, List # Handle imports with both relative and absolute paths try: from ..agent_core import Command, SkillResult, CommandContext from ..date_validation import validate_date_input from ..input_validation import command_input_validator from ..config_validation import ClaudeAPIConfig from ..skills.claude_skill import ClaudeAnalyzeSkill, ClaudeTransformSkill from ..skills.obsidian_skill import ( ObsidianReadSkill, ObsidianWriteSkill, ObsidianAppendSkill, ObsidianListFilesSkill, ) except ImportError: # Fallback to absolute imports when running as script from agent_core import Command, SkillResult, CommandContext from date_validation import validate_date_input from input_validation import command_input_validator from config_validation import ClaudeAPIConfig from skills.claude_skill import ClaudeAnalyzeSkill, ClaudeTransformSkill from skills.obsidian_skill import ( ObsidianReadSkill, ObsidianWriteSkill, ObsidianAppendSkill, ObsidianListFilesSkill, ) class OrganizeCommand(Command): """日记整理命令""" def __init__(self) -> None: super().__init__( name="organize", description="分析和整理日记内容,提取经验和要点", aliases=["org", "organize-journal"], ) # 注册 Skills self.register_skill(ObsidianReadSkill()) self.register_skill(ObsidianWriteSkill()) self.register_skill(ObsidianAppendSkill()) self.register_skill(ClaudeAnalyzeSkill()) self.register_skill(ClaudeTransformSkill()) async def execute(self, context: CommandContext) -> SkillResult: """ 执行日记整理命令 Args: context: 命令执行上下文 - args: - date: 日期(格式: YYYY-MM-DD,默认今天) - vault_path: Obsidian vault 路径 - daily_folder: 日记文件夹(默认: Daily) - config: 系统配置 Returns: SkillResult: 执行结果 """ try: # Validate and sanitize input arguments validated_args = command_input_validator.validate_organize_command_input( context.args ) # 获取参数 date_str: Optional[str] = validated_args.get("date") vault_path: Optional[str] = validated_args.get("vault_path") daily_folder: str = validated_args.get("daily_folder", "Daily") # 从配置中获取信息 config: Dict[str, Any] = context.config or {} obsidian_config: Dict[str, Any] = config.get("obsidian", {}) claude_config: Dict[str, Any] = config.get("claude", {}) output_config: Dict[str, Any] = config.get("output", {}) analysis_config: Dict[str, Any] = config.get("analysis", {}) # 如果没有提供日期,使用今天 if not date_str: date_str = datetime.now().strftime("%Y-%m-%d") else: # Validate the provided date validated_date = validate_date_input( date_str, field_name="date", required=True, format_hint="iso_date" ) date_str = validated_date.strftime("%Y-%m-%d") # 获取必要的配置 api_url: str = obsidian_config.get("rest_api", {}).get( "url", "https://localhost:27123" ) api_key: Optional[str] = obsidian_config.get("rest_api", {}).get("api_key") # Create enhanced Claude API configuration try: claude_api_config = ClaudeAPIConfig(**claude_config) except Exception as e: return SkillResult( success=False, error=f"Claude API 配置无效: {str(e)}", message="请检查 Claude API 配置", ) if not api_key or not claude_api_config.api_key: return SkillResult( success=False, error="缺少必要的配置", message="请配置 Obsidian API 密钥和 Claude API 密钥", ) self.logger.info(f"开始整理日期 {date_str} 的日记") # 步骤 1: 读取日记 daily_note_path = str(Path(daily_folder) / f"{date_str}.md") self.logger.info(f"步骤 1: 读取日记 {daily_note_path}") read_skill = self.skills["obsidian_read"] read_result: SkillResult = await read_skill.execute( context, file_path=daily_note_path, vault_path=vault_path, api_url=api_url, api_key=api_key, ) if not read_result.success: return read_result journal_content: str = read_result.data["content"] # 步骤 2: 使用 Claude 分析日记 self.logger.info("步骤 2: 使用 Claude 分析日记内容") analyze_skill = self.skills["claude_analyze"] categories: List[str] = analysis_config.get("categories", []) analyze_result: SkillResult = await analyze_skill.execute( context, journal_content=journal_content, claude_config=claude_api_config, categories=categories, ) if not analyze_result.success: return analyze_result analysis_data: Dict[str, Any] = analyze_result.data["analysis"] # 步骤 3: 整理分析结果到各个位置 self.logger.info("步骤 3: 整理分析结果") write_results: Dict[str, SkillResult] = {} write_skill = self.skills["obsidian_write"] # 处理经验 if "experiences" in analysis_data and analysis_data["experiences"]: experiences_content: str = self._format_experiences( analysis_data["experiences"], date_str ) experiences_path = str( Path( output_config.get("experiences_folder", "Knowledge/Experiences") ) / f"{date_str}.md" ) exp_result: SkillResult = await write_skill.execute( context, file_path=experiences_path, content=experiences_content, api_url=api_url, api_key=api_key, overwrite=True, ) write_results["experiences"] = exp_result # 处理经验教训 if "lessons_learned" in analysis_data and analysis_data["lessons_learned"]: lessons_content: str = self._format_lessons( analysis_data["lessons_learned"], date_str ) lessons_path = str( Path(output_config.get("lessons_folder", "Knowledge/Lessons")) / f"{date_str}.md" ) lessons_result: SkillResult = await write_skill.execute( context, file_path=lessons_path, content=lessons_content, api_url=api_url, api_key=api_key, overwrite=True, ) write_results["lessons"] = lessons_result # 处理待办事项 if "action_items" in analysis_data and analysis_data["action_items"]: tasks_content: str = self._format_tasks( analysis_data["action_items"], date_str ) tasks_path = str( Path(output_config.get("tasks_folder", "Tasks/Daily")) / f"{date_str}.md" ) tasks_result: SkillResult = await write_skill.execute( context, file_path=tasks_path, content=tasks_content, api_url=api_url, api_key=api_key, overwrite=True, ) write_results["tasks"] = tasks_result # 处理问题 if "problems" in analysis_data and analysis_data["problems"]: problems_content: str = self._format_problems( analysis_data["problems"], date_str ) problems_path = str( Path(output_config.get("problems_folder", "Knowledge/Problems")) / f"{date_str}.md" ) problems_result: SkillResult = await write_skill.execute( context, file_path=problems_path, content=problems_content, api_url=api_url, api_key=api_key, overwrite=True, ) write_results["problems"] = problems_result # 处理成就 if "achievements" in analysis_data and analysis_data["achievements"]: achievements_content: str = self._format_achievements( analysis_data["achievements"], date_str ) achievements_path = str( Path( output_config.get( "achievements_folder", "Knowledge/Achievements" ) ) / f"{date_str}.md" ) achievements_result: SkillResult = await write_skill.execute( context, file_path=achievements_path, content=achievements_content, api_url=api_url, api_key=api_key, overwrite=True, ) write_results["achievements"] = achievements_result # 处理改进建议 if "improvements" in analysis_data and analysis_data["improvements"]: improvements_content: str = self._format_improvements( analysis_data["improvements"], date_str ) improvements_path = str( Path( output_config.get( "improvements_folder", "Knowledge/Improvements" ) ) / f"{date_str}.md" ) improvements_result: SkillResult = await write_skill.execute( context, file_path=improvements_path, content=improvements_content, api_url=api_url, api_key=api_key, overwrite=True, ) write_results["improvements"] = improvements_result # 统计结果 successful_writes: int = sum(1 for r in write_results.values() if r.success) return SkillResult( success=True, data={ "date": date_str, "journal_file": daily_note_path, "analysis": analysis_data, "write_results": {k: v.success for k, v in write_results.items()}, "successful_writes": successful_writes, "total_writes": len(write_results), "organized_at": datetime.now().isoformat(), }, message=f"成功整理日记 {date_str},已生成 {successful_writes} 个文件", ) except Exception as e: self.logger.error(f"执行日记整理命令时出错: {str(e)}") return SkillResult(success=False, error=str(e), message="日记整理异常") def _format_experiences( self, experiences: List[Dict[str, Any]], date_str: str ) -> str: """格式化经验内容""" content: str = f"# 经验总结 - {date_str}\n\n" content += f"*生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n\n" for i, exp in enumerate(experiences, 1): content += f"## {i}. {exp.get('title', '经验')}\n\n" content += f"**分类**: {exp.get('category', '未分类')}\n" content += f"**优先级**: {exp.get('priority', 'medium')}\n\n" content += f"{exp.get('content', '')}\n\n" content += f"\n---\n*来源: [[{date_str}]]*\n" return content def _format_lessons(self, lessons: List[Dict[str, Any]], date_str: str) -> str: """格式化经验教训内容""" content: str = f"# 经验教训 - {date_str}\n\n" content += f"*生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n\n" for i, lesson in enumerate(lessons, 1): content += f"## {i}. {lesson.get('lesson', '教训')}\n\n" content += f"**背景**: {lesson.get('context', '')}\n\n" content += f"**应用**: {lesson.get('application', '')}\n\n" content += f"\n---\n*来源: [[{date_str}]]*\n" return content def _format_tasks(self, tasks: List[Dict[str, Any]], date_str: str) -> str: """格式化待办事项内容""" content: str = f"# 待办事项 - {date_str}\n\n" content += f"*生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n\n" # 按优先级分组 by_priority: Dict[str, List[Dict[str, Any]]] = { "high": [], "medium": [], "low": [], } for task in tasks: priority: str = task.get("priority", "medium") by_priority[priority].append(task) for priority in ["high", "medium", "low"]: if by_priority[priority]: priority_text: Dict[str, str] = { "high": "🔴 高", "medium": "� 中", r"low": "🟢 低", } content += f"## {priority_text[priority]} 优先级\n\n" for task in by_priority[priority]: content += f"- [ ] {task.get('task', '任务')}\n" if task.get("deadline"): content += f" - 截止: {task.get('deadline')}\n" content += "\n" content += f"\n---\n*来源: [[{date_str}]]*\n" return content def _format_problems(self, problems: List[Dict[str, Any]], date_str: str) -> str: """格式化问题内容""" content: str = f"# 问题记录 - {date_str}\n\n" content += f"*生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n\n" for i, problem in enumerate(problems, 1): content += f"## {i}. {problem.get('problem', '问题')}\n\n" content += f"**影响**: {problem.get('impact', '')}\n\n" content += f"**建议方案**: {problem.get('proposed_solution', '')}\n\n" content += f"\n---\n*来源: [[{date_str}]]*\n" return content def _format_achievements( self, achievements: List[Dict[str, Any]], date_str: str ) -> str: """格式化成就内容""" content: str = f"# 成就记录 - {date_str}\n\n" content += f"*生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n\n" for i, achievement in enumerate(achievements, 1): content += f"## {i}. {achievement.get('achievement', '成就')}\n\n" content += f"**重要性**: {achievement.get('significance', '')}\n\n" content += f"**证据**: {achievement.get('evidence', '')}\n\n" content += f"\n---\n*来源: [[{date_str}]]*\n" return content def _format_improvements( self, improvements: List[Dict[str, Any]], date_str: str ) -> str: """格式化改进建议内容""" content: str = f"# 改进建议 - {date_str}\n\n" content += f"*生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n\n" for i, improvement in enumerate(improvements, 1): content += f"## {i}. {improvement.get('area', '改进领域')}\n\n" content += f"**当前状态**: {improvement.get('current_state', '')}\n\n" content += f"**建议改进**: {improvement.get('suggested_change', '')}\n\n" content += f"**预期收益**: {improvement.get('expected_benefit', '')}\n\n" content += f"\n---\n*来源: [[{date_str}]]*\n" return content