diff --git a/src/clis/agent/episodic_memory.py b/src/clis/agent/episodic_memory.py index 7db5616..60f0f60 100644 --- a/src/clis/agent/episodic_memory.py +++ b/src/clis/agent/episodic_memory.py @@ -1,11 +1,11 @@ """ -情景记忆模块 - 当前任务的持久化 Markdown 文档 +Episodic memory module - Persistent Markdown document for current task -特点: -- 持久化到 .clis_memory/ 目录 -- 人类可读可编辑 -- 结构化 Markdown (checklist, findings, next steps) -- 跨会话保留 +Features: +- Persist to .clis_memory/ directory +- Human-readable and editable +- Structured Markdown (checklist, findings, next steps) +- Cross-session retention """ from pathlib import Path @@ -16,15 +16,15 @@ class EpisodicMemory: """ - 情景记忆 - 当前任务的持久化 Markdown 文档 + Episodic memory - Persistent Markdown document for current task - 每个任务对应一个 Markdown 文件,包含: - - 任务目标 - - 任务分解 (checklist) - - 关键发现 - - 当前进度 - - 下一步行动 - - 执行日志 + Each task corresponds to a Markdown file containing: + - Task objective + - Task breakdown (checklist) + - Key findings + - Current progress + - Next actions + - Execution log """ def __init__(self, task_id: str, memory_dir: str = ".clis_memory"): @@ -33,18 +33,18 @@ def __init__(self, task_id: str, memory_dir: str = ".clis_memory"): self.tasks_dir = self.memory_dir / "tasks" / "active" self.task_file = self.tasks_dir / f"task_{task_id}.md" - # 确保目录存在 + # Ensure directory exists self.tasks_dir.mkdir(parents=True, exist_ok=True) def load_or_create(self, task_description: str) -> str: """ - 加载现有任务文档或创建新文档 + Load existing task document or create new one Args: - task_description: 用户的任务描述 + task_description: User's task description Returns: - 任务文档内容 + Task document content """ if self.task_file.exists(): return self.task_file.read_text(encoding='utf-8') @@ -52,48 +52,48 @@ def load_or_create(self, task_description: str) -> str: return self._create_initial_doc(task_description) def _create_initial_doc(self, task_description: str) -> str: - """创建初始任务文档""" + """Create initial task document""" doc = f"""# Task: {task_description} -**任务ID**: {self.task_id} -**创建时间**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} -**状态**: 🔄 进行中 +**Task ID**: {self.task_id} +**Created**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} +**Status**: 🔄 In Progress --- -## 📋 任务目标 +## 📋 Task Objective {task_description} --- -## ✅ 任务分解 +## ✅ Task Breakdown - -- [ ] 步骤将在执行中自动识别 + +- [ ] Steps will be automatically identified during execution --- -## 🔍 关键发现 +## 🔍 Key Findings -*(执行中会自动记录)* +*(Will be automatically recorded during execution)* --- -## 📊 当前进度 +## 📊 Current Progress -**阶段**: 初始化 -**进度**: 0/0 +**Phase**: Initialization +**Progress**: 0/0 --- -## 🎯 下一步行动 +## 🎯 Next Actions -开始执行任务... +Starting task execution... --- -## 📝 执行日志 +## 📝 Execution Log """ self.task_file.write_text(doc, encoding='utf-8') @@ -101,10 +101,10 @@ def _create_initial_doc(self, task_description: str) -> str: def update_step(self, step_description: str, status: str = "done"): """ - 更新任务步骤状态 + Update task step status Args: - step_description: 步骤描述 + step_description: Step description status: "done" | "in_progress" | "pending" """ if not self.task_file.exists(): @@ -112,18 +112,18 @@ def update_step(self, step_description: str, status: str = "done"): content = self.task_file.read_text(encoding='utf-8') - # 查找任务分解区域 - checklist_pattern = r'(## ✅ 任务分解.*?)(##|\Z)' + # Find task breakdown area + checklist_pattern = r'(## ✅ Task Breakdown.*?)(##|\Z)' match = re.search(checklist_pattern, content, re.DOTALL) if match: checklist_section = match.group(1) - # 检查是否已存在此步骤 + # Check if this step already exists step_exists = step_description in checklist_section if not step_exists: - # 添加新步骤 + # Add new step checkbox = { "done": "- [x]", "in_progress": "- [ ] 🔄", @@ -132,43 +132,43 @@ def update_step(self, step_description: str, status: str = "done"): new_step = f"{checkbox} {step_description}\n" - # 在下一个 ## 前插入 + # Insert before next ## next_section_pos = content.find('##', match.end(1)) if next_section_pos != -1: - # 在找到的位置前插入 + # Insert before found position insert_pos = content.rfind('\n', match.start(1), next_section_pos) if insert_pos == -1: insert_pos = match.end(1) content = content[:insert_pos] + '\n' + new_step + content[insert_pos:] else: - # 在结尾插入 + # Insert at end content = content.rstrip() + '\n' + new_step + '\n' self.task_file.write_text(content, encoding='utf-8') def add_finding(self, finding: str, category: str = "general"): """ - 添加关键发现 + Add key finding Args: - finding: 发现内容 - category: 分类标签 + finding: Finding content + category: Category label """ if not self.task_file.exists(): return content = self.task_file.read_text(encoding='utf-8') - # 查找关键发现区域 - findings_pattern = r'(## 🔍 关键发现.*?)(##|\Z)' + # Find key findings area + findings_pattern = r'(## 🔍 Key Findings.*?)(##|\Z)' match = re.search(findings_pattern, content, re.DOTALL) if match: - # 添加新发现 + # Add new finding timestamp = datetime.now().strftime('%H:%M:%S') new_finding = f"- **[{category}]** ({timestamp}): {finding}\n" - # 在下一个 ## 前插入 + # Insert before next ## next_section_pos = content.find('##', match.end(1)) if next_section_pos != -1: insert_pos = content.rfind('\n', match.start(1), next_section_pos) @@ -181,52 +181,52 @@ def add_finding(self, finding: str, category: str = "general"): self.task_file.write_text(content, encoding='utf-8') def update_progress(self, phase: str, progress: str): - """更新当前进度""" + """Update current progress""" if not self.task_file.exists(): return content = self.task_file.read_text(encoding='utf-8') - # 更新阶段 + # Update phase content = re.sub( - r'\*\*阶段\*\*:.*', - f'**阶段**: {phase}', + r'\*\*Phase\*\*:.*', + f'**Phase**: {phase}', content ) - # 更新进度 + # Update progress content = re.sub( - r'\*\*进度\*\*:.*', - f'**进度**: {progress}', + r'\*\*Progress\*\*:.*', + f'**Progress**: {progress}', content ) self.task_file.write_text(content, encoding='utf-8') def update_next_action(self, action: str): - """更新下一步行动建议""" + """Update next action suggestion""" if not self.task_file.exists(): return content = self.task_file.read_text(encoding='utf-8') - # 查找下一步行动区域 - next_action_pattern = r'(## 🎯 下一步行动.*?)(##|\Z)' + # Find next actions area + next_action_pattern = r'(## 🎯 Next Actions.*?)(##|\Z)' match = re.search(next_action_pattern, content, re.DOTALL) if match: - new_section = f"## 🎯 下一步行动\n\n{action}\n\n" + new_section = f"## 🎯 Next Actions\n\n{action}\n\n" content = content[:match.start(1)] + new_section + content[match.end(1):] self.task_file.write_text(content, encoding='utf-8') def append_log(self, log_entry: str): - """添加执行日志""" + """Add execution log""" if not self.task_file.exists(): return content = self.task_file.read_text(encoding='utf-8') - # 在执行日志区域追加 + # Append to execution log area timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') log_line = f"\n[{timestamp}] {log_entry}\n" @@ -235,13 +235,13 @@ def append_log(self, log_entry: str): def inject_to_prompt(self, include_log: bool = False) -> str: """ - 将任务文档注入到 prompt + Inject task document to prompt Args: - include_log: 是否包含执行日志 (较长) + include_log: Whether to include execution log (lengthy) Returns: - 格式化的 prompt 文本 + Formatted prompt text """ if not self.task_file.exists(): return "" @@ -249,27 +249,27 @@ def inject_to_prompt(self, include_log: bool = False) -> str: content = self.task_file.read_text(encoding='utf-8') if not include_log: - # 移除执行日志部分 (节省 tokens) - content = re.sub(r'## 📝 执行日志.*', '', content, flags=re.DOTALL) + # Remove execution log section (save tokens) + content = re.sub(r'## 📝 Execution Log.*', '', content, flags=re.DOTALL) return f""" ╭──────────────────────────────────────────────────────────────╮ -│ 📖 任务记忆 (TASK MEMORY / MEMORY BANK) │ +│ 📖 TASK MEMORY / MEMORY BANK │ ╰──────────────────────────────────────────────────────────────╯ {content} -⚠️ 重要提醒: - • 检查上方的 ✅ 任务分解,看哪些步骤已完成 - • 查看 🔍 关键发现,已收集的信息就在这里 - • 参考 🎯 下一步行动的建议 - • 如果任务完成,调用 {{"type": "done", "summary": "..."}} +⚠️ Important Reminders: + • Check ✅ Task Breakdown above to see which steps are completed + • Review 🔍 Key Findings - collected information is here + • Refer to 🎯 Next Actions suggestions + • If task is complete, call {{"type": "done", "summary": "..."}} """ def get_file_path(self) -> Path: - """获取任务文件路径""" + """Get task file path""" return self.task_file def exists(self) -> bool: - """检查任务文件是否存在""" + """Check if task file exists""" return self.task_file.exists() diff --git a/src/clis/agent/interactive_agent.py b/src/clis/agent/interactive_agent.py index ede9dd1..299133d 100644 --- a/src/clis/agent/interactive_agent.py +++ b/src/clis/agent/interactive_agent.py @@ -85,20 +85,20 @@ def __init__( logger.warning(f"Failed to load safety config: {e}") self.safety_config = None - # ============ 新增: 混合记忆系统 ============ - # 工作记忆 (内存) + # ============ New: Hybrid memory system ============ + # Working memory (in-memory) self.working_memory = WorkingMemory() - # 情景记忆 (任务文档) - 在任务开始时创建 + # Episodic memory (task document) - created at task start self.episodic_memory: Optional[EpisodicMemory] = None - # 状态机 + # State machine self.state_machine = TaskStateMachine(max_iterations=self.max_iterations) - # 记忆管理器 + # Memory manager self.memory_manager = MemoryManager() - # 当前任务ID + # Current task ID self.current_task_id: Optional[str] = None def execute(self, query: str, stream_thinking: bool = False) -> Generator[Dict[str, Any], None, None]: @@ -119,14 +119,14 @@ def execute(self, query: str, stream_thinking: bool = False) -> Generator[Dict[s "needs_confirmation": bool (for commands) } """ - # ============ 初始化记忆系统 ============ - # 创建任务记忆 + # ============ Initialize memory system ============ + # Create task memory self.current_task_id = datetime.now().strftime('%Y%m%d_%H%M%S') task_id, task_file = self.memory_manager.create_task_memory(query, self.current_task_id) self.episodic_memory = EpisodicMemory(task_id) self.episodic_memory.load_or_create(query) - # 清空工作记忆 + # Clear working memory self.working_memory.clear() logger.info(f"Task memory created: {task_file}") @@ -134,7 +134,7 @@ def execute(self, query: str, stream_thinking: bool = False) -> Generator[Dict[s platform = get_platform() shell = get_shell() - # 用于跟踪任务是否成功 + # Used to track if task is successful task_success = True # Build base system prompt template (will be updated each iteration) @@ -289,18 +289,18 @@ def build_system_prompt(iteration: int) -> str: # Yield iteration start (always, for counting) yield {"type": "iteration_start", "iteration": iteration + 1} - # ============ 状态机检测 ============ + # ============ State machine detection ============ state_advice = self.state_machine.detect_state(iteration, self.working_memory) - # 如果是紧急状态(循环或超时),强制提示 + # If urgent state (loop or timeout), force warning if state_advice.is_urgent: logger.warning(f"Urgent state detected: {state_advice.message}") yield { "type": "warning", - "content": f"{state_advice.message}\n建议: {'; '.join(state_advice.suggested_actions)}" + "content": f"{state_advice.message}\nSuggestions: {'; '.join(state_advice.suggested_actions)}" } - # 更新进度 + # Update progress self.working_memory.update_phase( state_advice.state.value, f"{iteration + 1}/{self.max_iterations}" @@ -349,19 +349,19 @@ def build_system_prompt(iteration: int) -> str: # Handle completion if action_type == "done": - # ============ 完成任务记忆 ============ + # ============ Complete task memory ============ summary = action.get("summary", "Task completed") - self.episodic_memory.update_step("任务完成", "done") - self.episodic_memory.update_next_action(f"✅ 已完成: {summary}") + self.episodic_memory.update_step("Task completed", "done") + self.episodic_memory.update_next_action(f"✅ Completed: {summary}") - # 完成任务 + # Complete task self.memory_manager.complete_task( self.current_task_id, success=task_success, extract_knowledge=True ) - # 显示统计 + # Show statistics stats = self.working_memory.get_stats() logger.info(f"Task completed. Stats: {stats}") @@ -379,23 +379,23 @@ def build_system_prompt(iteration: int) -> str: tool_name = action.get("tool") params = action.get("params", {}) - # ============ 更新工作记忆 ============ + # ============ Update working memory ============ self.working_memory.increment_tool(tool_name) - # 特殊处理: 文件读取 + # Special handling: file reading if tool_name == 'read_file': file_path = params.get('path', '') is_new = self.working_memory.add_file_read(file_path) if not is_new: - # 重复读取!强制警告 - warning_msg = f"⚠️ 警告: 文件 '{file_path}' 已经读过!可能陷入循环。" + # Repeated read! Force warning + warning_msg = f"⚠️ Warning: File '{file_path}' already read! May be stuck in loop." yield { "type": "warning", "content": warning_msg } self.episodic_memory.add_finding( - f"重复读取文件: {file_path}", + f"Repeated file read: {file_path}", category="warning" ) @@ -493,18 +493,18 @@ def build_system_prompt(iteration: int) -> str: "success": result.success }) - # ============ 更新记忆系统 ============ - # 记录文件写入 + # ============ Update memory system ============ + # Record file writes if tool_name in ('write_file', 'edit_file'): file_path = params.get('path', '') self.working_memory.add_file_written(file_path) - self.episodic_memory.update_step(f"写入文件: {file_path}", "done") + self.episodic_memory.update_step(f"Write file: {file_path}", "done") - # 记录关键发现 + # Record key findings if result.success and tool_name in ('read_file', 'search_files', 'file_tree'): preview = result.output[:100] if result.output else "" self.episodic_memory.add_finding( - f"从 {tool_name}({params}) 获取: {preview}...", + f"From {tool_name}({params}) obtained: {preview}...", category="data" ) @@ -513,7 +513,7 @@ def build_system_prompt(iteration: int) -> str: content = result.output[:500] if result.output else "Success" else: content = result.error if result.error else (result.output[:500] if result.output else "Unknown error") - task_success = False # 标记任务失败 + task_success = False # Mark task as failed # Add to context manager obs_type = ObservationType.ERROR if not result.success else ObservationType.TOOL_RESULT @@ -572,7 +572,7 @@ def build_system_prompt(iteration: int) -> str: # Execute command (low risk, auto-approved) result = self.tool_executor.execute("execute_command", {"command": command}) - # ============ 更新工作记忆 ============ + # ============ Update working memory ============ self.working_memory.add_command(command, result.success) if not result.success: task_success = False @@ -604,7 +604,7 @@ def build_system_prompt(iteration: int) -> str: context_summary = self.context_manager.get_context() stats = self.context_manager.get_summary() - # ============ 注入记忆系统到 prompt ============ + # ============ Inject memory system to prompt ============ state_advice_text = self.state_machine.format_advice(state_advice) working_memory_text = self.working_memory.to_prompt() episodic_memory_text = self.episodic_memory.inject_to_prompt(include_log=False) @@ -618,24 +618,24 @@ def build_system_prompt(iteration: int) -> str: {episodic_memory_text} ╭──────────────────────────────────────────────────────────────╮ -│ 📜 最近观察 (OBSERVATIONS) │ +│ 📜 RECENT OBSERVATIONS │ ╰──────────────────────────────────────────────────────────────╯ Previous observations ({stats['total']} total, {stats['critical']} critical): {context_summary} ⚠️ IMPORTANT: -- 检查工作记忆中的"已读文件"列表,不要重复读取! -- 查看状态机建议,遵循当前阶段的指导 -- 参考任务记忆中的已完成步骤和关键发现 +- Check "Files Read" list in working memory, don't repeat reading! +- Review state machine suggestions, follow current phase guidance +- Refer to completed steps and key findings in task memory - If task is COMPLETE, respond with {{"type": "done", "summary": "..."}} - Otherwise, take a DIFFERENT action to make progress What's your next action?""" # Max iterations reached - # ============ 标记任务失败 ============ - self.episodic_memory.update_step("任务未完成(达到迭代上限)", "done") + # ============ Mark task as failed ============ + self.episodic_memory.update_step("Task incomplete (reached iteration limit)", "done") self.memory_manager.complete_task( self.current_task_id, success=False, @@ -881,10 +881,10 @@ def execute_tool(self, tool_name: str, params: Dict[str, Any], approved: bool = obs_type=ObservationType.REJECTION, is_critical=True ) - # 记录到任务文档 + # Record to task document if self.episodic_memory: self.episodic_memory.add_finding( - f"用户拒绝工具: {tool_name}({params})", + f"User rejected tool: {tool_name}({params})", category="rejection" ) return { @@ -895,7 +895,7 @@ def execute_tool(self, tool_name: str, params: Dict[str, Any], approved: bool = result = self.tool_executor.execute(tool_name, params) - # ============ 更新工作记忆 ============ + # ============ Update working memory ============ if self.working_memory: self.working_memory.increment_tool(tool_name) @@ -906,11 +906,11 @@ def execute_tool(self, tool_name: str, params: Dict[str, Any], approved: bool = file_path = params.get('path', '') self.working_memory.add_file_written(file_path) - # 更新任务文档 + # Update task document if self.episodic_memory and result.success: if tool_name in ('write_file', 'edit_file'): file_path = params.get('path', '') - self.episodic_memory.update_step(f"写入文件: {file_path}", "done") + self.episodic_memory.update_step(f"Write file: {file_path}", "done") # Prepare content for return (use error message if failed) if result.success: @@ -954,10 +954,10 @@ def execute_command(self, command: str, approved: bool = True) -> Dict[str, Any] if not approved: # Record rejection self.context_manager.add_rejection(command, "User rejected command") - # 记录到任务文档 + # Record to task document if self.episodic_memory: self.episodic_memory.add_finding( - f"用户拒绝命令: {command}", + f"User rejected command: {command}", category="rejection" ) return { @@ -968,7 +968,7 @@ def execute_command(self, command: str, approved: bool = True) -> Dict[str, Any] result = self.tool_executor.execute("execute_command", {"command": command}) - # ============ 更新工作记忆 ============ + # ============ Update working memory ============ if self.working_memory: self.working_memory.add_command(command, result.success) diff --git a/src/clis/agent/memory_manager.py b/src/clis/agent/memory_manager.py index cef9677..55354cd 100644 --- a/src/clis/agent/memory_manager.py +++ b/src/clis/agent/memory_manager.py @@ -1,11 +1,11 @@ """ -记忆生命周期管理器 +Memory lifecycle manager -职责: -- 创建和组织记忆文件 -- 自动清理过期记忆 -- 归档长期记忆 -- 提供查询接口 +Responsibilities: +- Create and organize memory files +- Auto cleanup expired memories +- Archive long-term memories +- Provide query interface """ from pathlib import Path @@ -17,19 +17,19 @@ class TaskStatus(Enum): - """任务状态""" - ACTIVE = "active" # 进行中 - COMPLETED = "completed" # 已完成 - ARCHIVED = "archived" # 已归档 - FAILED = "failed" # 失败 + """Task status""" + ACTIVE = "active" # In progress + COMPLETED = "completed" # Completed + ARCHIVED = "archived" # Archived + FAILED = "failed" # Failed class MemoryManager: """ - 记忆生命周期管理器 + Memory lifecycle manager - 管理任务记忆的完整生命周期: - 创建 → active/ → completed/ → archived/ + Manages complete lifecycle of task memories: + create → active/ → completed/ → archived/ """ def __init__(self, memory_dir: str = ".clis_memory"): @@ -41,42 +41,42 @@ def __init__(self, memory_dir: str = ".clis_memory"): self.knowledge_dir = self.memory_dir / "knowledge" self.metadata_file = self.memory_dir / ".metadata.json" - # 创建目录结构 + # Create directory structure self._ensure_dirs() - # 加载元数据 + # Load metadata self.metadata = self._load_metadata() def _ensure_dirs(self): - """确保目录结构存在""" + """Ensure directory structure exists""" for dir_path in [self.active_dir, self.completed_dir, self.archived_dir, self.knowledge_dir]: dir_path.mkdir(parents=True, exist_ok=True) def _load_metadata(self) -> Dict: - """加载记忆元数据""" + """Load memory metadata""" if self.metadata_file.exists(): try: return json.loads(self.metadata_file.read_text(encoding='utf-8')) except (json.JSONDecodeError, Exception): - # 元数据损坏,返回默认值 + # Metadata corrupted, return default return {"tasks": {}, "config": self._default_config()} return {"tasks": {}, "config": self._default_config()} def _save_metadata(self): - """保存元数据""" + """Save metadata""" self.metadata_file.write_text( json.dumps(self.metadata, indent=2, ensure_ascii=False), encoding='utf-8' ) def _default_config(self) -> Dict: - """默认配置""" + """Default configuration""" return { - "retention_days": 7, # 已完成任务保留天数 - "auto_archive": True, # 自动归档 - "auto_cleanup": True, # 自动清理 - "max_active_tasks": 10, # 最大并发任务数 + "retention_days": 7, # Days to keep completed tasks + "auto_archive": True, # Auto archive + "auto_cleanup": True, # Auto cleanup + "max_active_tasks": 10, # Max concurrent tasks } def create_task_memory( @@ -85,11 +85,11 @@ def create_task_memory( task_id: Optional[str] = None ) -> tuple[str, Path]: """ - 创建新的任务记忆 + Create new task memory Args: - task_description: 任务描述 - task_id: 任务ID (可选,不提供则自动生成) + task_description: Task description + task_id: Task ID (optional, auto-generated if not provided) Returns: (task_id, file_path) @@ -99,10 +99,10 @@ def create_task_memory( task_file = self.active_dir / f"task_{task_id}.md" - # 记录元数据 + # Record metadata self.metadata["tasks"][task_id] = { "status": TaskStatus.ACTIVE.value, - "description": task_description[:100], # 只保存前100字符 + "description": task_description[:100], # Save only first 100 chars "created_at": datetime.now().isoformat(), "file_path": str(task_file.relative_to(self.memory_dir)), } @@ -117,29 +117,29 @@ def complete_task( extract_knowledge: bool = True ): """ - 标记任务完成 + Mark task as complete Args: - task_id: 任务ID - success: 是否成功完成 - extract_knowledge: 是否提取知识到知识库 + task_id: Task ID + success: Whether completed successfully + extract_knowledge: Whether to extract knowledge to knowledge base """ if task_id not in self.metadata["tasks"]: - # 任务不存在,可能是直接创建的文件,添加元数据 + # Task doesn't exist, may be directly created file, add metadata self.metadata["tasks"][task_id] = { "status": TaskStatus.ACTIVE.value, "description": "Unknown task", "created_at": datetime.now().isoformat(), } - # 移动文件: active → completed + # Move file: active → completed active_file = self.active_dir / f"task_{task_id}.md" completed_file = self.completed_dir / f"task_{task_id}.md" if active_file.exists(): shutil.move(str(active_file), str(completed_file)) - # 在文件头部添加完成标记 + # Add completion marker at file header content = completed_file.read_text(encoding='utf-8') header = f""" @@ -147,7 +147,7 @@ def complete_task( """ completed_file.write_text(header + content, encoding='utf-8') - # 更新元数据 + # Update metadata self.metadata["tasks"][task_id].update({ "status": TaskStatus.COMPLETED.value if success else TaskStatus.FAILED.value, "completed_at": datetime.now().isoformat(), @@ -155,29 +155,29 @@ def complete_task( }) self._save_metadata() - # 提取知识 + # Extract knowledge if extract_knowledge and success: self._extract_knowledge(task_id, completed_file) def _extract_knowledge(self, task_id: str, task_file: Path): """ - 从任务中提取知识到知识库 + Extract knowledge from task to knowledge base - 策略: - - 提取 "关键发现" 部分 - - 识别通用模式 - - 更新项目知识库 + Strategy: + - Extract "Key Findings" section + - Identify general patterns + - Update project knowledge base """ - # TODO: 实现智能知识提取 - # 可以调用 LLM 总结任务中的可复用知识 + # TODO: Implement intelligent knowledge extraction + # Can call LLM to summarize reusable knowledge from task pass def archive_old_tasks(self, days: Optional[int] = None): """ - 归档旧任务 + Archive old tasks Args: - days: 保留天数 (None = 使用配置) + days: Days to keep (None = use config) """ if days is None: days = self.metadata["config"]["retention_days"] @@ -200,7 +200,7 @@ def archive_old_tasks(self, days: Optional[int] = None): continue def _archive_task(self, task_id: str): - """归档单个任务""" + """Archive single task""" if task_id not in self.metadata["tasks"]: return @@ -210,7 +210,7 @@ def _archive_task(self, task_id: str): if not completed_file.exists(): return - # 按月归档 + # Archive by month completed_at_str = task_info.get("completed_at") if not completed_at_str: completed_at = datetime.now() @@ -226,7 +226,7 @@ def _archive_task(self, task_id: str): archive_file = archive_month_dir / f"task_{task_id}.md" shutil.move(str(completed_file), str(archive_file)) - # 更新元数据 + # Update metadata task_info.update({ "status": TaskStatus.ARCHIVED.value, "archived_at": datetime.now().isoformat(), @@ -235,7 +235,7 @@ def _archive_task(self, task_id: str): self._save_metadata() def cleanup(self): - """执行清理任务""" + """Execute cleanup tasks""" if self.metadata["config"]["auto_archive"]: self.archive_old_tasks() @@ -243,7 +243,7 @@ def cleanup(self): self._cleanup_failed_tasks() def _cleanup_failed_tasks(self): - """清理失败的任务 (保留 1 天)""" + """Clean up failed tasks (keep 1 day)""" cutoff = datetime.now() - timedelta(days=1) for task_id, task_info in list(self.metadata["tasks"].items()): @@ -255,12 +255,12 @@ def _cleanup_failed_tasks(self): try: completed_at = datetime.fromisoformat(completed_at_str) if completed_at < cutoff: - # 删除文件 + # Delete file task_file = self.memory_dir / task_info["file_path"] if task_file.exists(): task_file.unlink() - # 删除元数据 + # Delete metadata del self.metadata["tasks"][task_id] except (ValueError, Exception): continue @@ -273,14 +273,14 @@ def list_tasks( limit: int = 20 ) -> List[Dict]: """ - 列出任务 + List tasks Args: - status: 过滤状态 (None = 全部) - limit: 最大返回数量 + status: Filter by status (None = all) + limit: Maximum number to return Returns: - 任务列表 (按创建时间倒序) + Task list (sorted by creation time descending) """ tasks = [] for task_id, task_info in self.metadata["tasks"].items(): @@ -290,12 +290,12 @@ def list_tasks( **task_info }) - # 按创建时间倒序 + # Sort by creation time descending tasks.sort(key=lambda t: t.get("created_at", ""), reverse=True) return tasks[:limit] def get_task_file(self, task_id: str) -> Optional[Path]: - """获取任务文件路径""" + """Get task file path""" if task_id not in self.metadata["tasks"]: return None @@ -304,13 +304,13 @@ def get_task_file(self, task_id: str) -> Optional[Path]: def search_tasks(self, query: str) -> List[Dict]: """ - 搜索任务 (简单文本匹配) + Search tasks (simple text matching) Args: - query: 搜索关键词 + query: Search keyword Returns: - 匹配的任务列表 + List of matching tasks """ results = [] for task_id, task_info in self.metadata["tasks"].items(): @@ -324,10 +324,10 @@ def search_tasks(self, query: str) -> List[Dict]: def get_stats(self) -> Dict: """ - 获取记忆统计信息 + Get memory statistics Returns: - 统计信息字典 + Statistics dictionary """ stats = { "total": len(self.metadata["tasks"]), @@ -352,20 +352,20 @@ def get_stats(self) -> Dict: def delete_task(self, task_id: str): """ - 删除任务 + Delete task Args: - task_id: 任务ID + task_id: Task ID """ if task_id not in self.metadata["tasks"]: return - # 删除文件 + # Delete file task_info = self.metadata["tasks"][task_id] task_file = self.memory_dir / task_info["file_path"] if task_file.exists(): task_file.unlink() - # 删除元数据 + # Delete metadata del self.metadata["tasks"][task_id] self._save_metadata() diff --git a/src/clis/agent/state_machine.py b/src/clis/agent/state_machine.py index 8f02749..236c168 100644 --- a/src/clis/agent/state_machine.py +++ b/src/clis/agent/state_machine.py @@ -1,10 +1,10 @@ """ -任务状态机模块 - 显式引导弱模型 +Task state machine module - Explicitly guide weak models -设计目的: -- 减少模型的决策负担 -- 明确告知当前应该做什么 -- 自动检测异常状态并干预 +Design purpose: +- Reduce the model's decision-making burden +- Clearly tell it what should be done now +- Automatically detect abnormal states and intervene """ from enum import Enum @@ -13,18 +13,18 @@ class TaskState(Enum): - """任务状态""" - INIT = "initialization" # 初始化 - GATHER = "information_gathering" # 信息收集 - ANALYZE = "data_analysis" # 数据分析 - EXECUTE = "execution" # 执行操作 - FINALIZE = "finalization" # 完成总结 - STUCK = "stuck_in_loop" # 陷入循环 + """Task states""" + INIT = "initialization" # Initialization + GATHER = "information_gathering" # Information gathering + ANALYZE = "data_analysis" # Data analysis + EXECUTE = "execution" # Execute operations + FINALIZE = "finalization" # Finalize summary + STUCK = "stuck_in_loop" # Stuck in loop @dataclass class StateAdvice: - """状态建议""" + """State advice""" state: TaskState message: str suggested_actions: list[str] @@ -33,9 +33,9 @@ class StateAdvice: class TaskStateMachine: """ - 任务状态机 - 显式引导弱模型 + Task state machine - Explicitly guide weak models - 通过检测工作记忆的状态,自动判断当前阶段并给出明确建议 + By detecting the state of working memory, automatically determine the current stage and give clear advice """ def __init__(self, max_iterations: int = 100): @@ -48,70 +48,70 @@ def detect_state( working_memory ) -> StateAdvice: """ - 检测当前状态并给出建议 + Detect current state and give advice Args: - iteration: 当前迭代次数 - working_memory: 工作记忆对象 + iteration: Current iteration number + working_memory: Working memory object Returns: - 状态和建议 + State and advice """ - # 检测循环 + # Detect loop is_loop, loop_reason = working_memory.detect_loop() if is_loop: self.current_state = TaskState.STUCK return StateAdvice( state=TaskState.STUCK, - message=f"🚨 检测到循环: {loop_reason}", + message=f"🚨 Loop detected: {loop_reason}", suggested_actions=[ - "立即停止当前操作", - "基于已有信息总结答案", - "调用 {\"type\": \"done\", \"summary\": \"...\"}", - "不要尝试更多读取或命令!" + "Immediately stop current operation", + "Summarize answer based on existing information", + "Call {\"type\": \"done\", \"summary\": \"...\"}", + "Do not attempt more reads or commands!" ], is_urgent=True ) - # 检测信息过载 (读太多文件) + # Detect information overload (reading too many files) if len(working_memory.files_read) > 15 and len(working_memory.files_written) == 0: self.current_state = TaskState.ANALYZE return StateAdvice( state=TaskState.ANALYZE, - message="📚 信息收集已充分 → 切换到分析阶段", + message="📚 Information gathering complete → Switch to analysis phase", suggested_actions=[ - "不要再读取新文件", - "分析已收集的信息", - "提取关键发现", - "准备给出结论" + "Do not read more new files", + "Analyze collected information", + "Extract key findings", + "Prepare to give conclusions" ], is_urgent=False ) - # 检测接近迭代上限 + # Detect approaching iteration limit if iteration >= self.max_iterations * 0.8: self.current_state = TaskState.FINALIZE return StateAdvice( state=TaskState.FINALIZE, - message=f"⏰ 接近迭代上限 ({iteration}/{self.max_iterations}) → 必须收尾", + message=f"⏰ Approaching iteration limit ({iteration}/{self.max_iterations}) → Must finalize", suggested_actions=[ - "立即基于现有信息给出答案", - "不要开启新的子任务", - "调用 {\"type\": \"done\", \"summary\": \"...\"} 结束" + "Immediately give answer based on existing information", + "Do not start new subtasks", + "Call {\"type\": \"done\", \"summary\": \"...\"} to finish" ], is_urgent=True ) - # 正常状态判断 + # Normal state judgment if len(working_memory.files_read) < 5 and len(working_memory.commands_run) == 0: self.current_state = TaskState.GATHER return StateAdvice( state=TaskState.GATHER, - message="🔍 信息收集阶段", + message="🔍 Information gathering phase", suggested_actions=[ - "继续收集必要信息", - "读取相关文件", - "探索项目结构" + "Continue gathering necessary information", + "Read relevant files", + "Explore project structure" ], is_urgent=False ) @@ -120,11 +120,11 @@ def detect_state( self.current_state = TaskState.ANALYZE return StateAdvice( state=TaskState.ANALYZE, - message="🧠 分析阶段", + message="🧠 Analysis phase", suggested_actions=[ - "分析已收集的数据", - "提取关键信息", - "准备执行或给出结论" + "Analyze collected data", + "Extract key information", + "Prepare to execute or give conclusions" ], is_urgent=False ) @@ -132,25 +132,25 @@ def detect_state( self.current_state = TaskState.EXECUTE return StateAdvice( state=TaskState.EXECUTE, - message="⚙️ 执行阶段", - suggested_actions=["继续执行任务"], + message="⚙️ Execution phase", + suggested_actions=["Continue executing task"], is_urgent=False ) def format_advice(self, advice: StateAdvice) -> str: - """格式化建议为 prompt 文本""" - urgency = "🚨 紧急!" if advice.is_urgent else "" + """Format advice as prompt text""" + urgency = "🚨 Urgent!" if advice.is_urgent else "" actions_text = "\n".join(f" {i+1}. {action}" for i, action in enumerate(advice.suggested_actions)) return f""" ╭──────────────────────────────────────────────────────────────╮ -│ 🎯 状态机引导 (STATE MACHINE) │ +│ 🎯 STATE MACHINE GUIDANCE │ ╰──────────────────────────────────────────────────────────────╯ {urgency} {advice.message} -📋 建议行动: +📋 Suggested Actions: {actions_text} """ diff --git a/src/clis/agent/working_memory.py b/src/clis/agent/working_memory.py index 5c06ed9..e77d0d6 100644 --- a/src/clis/agent/working_memory.py +++ b/src/clis/agent/working_memory.py @@ -1,10 +1,10 @@ """ -工作记忆模块 - 最近 5-10 步的结构化操作记录 +Working memory module - Structured operation records for recent 5-10 steps -特点: -- 轻量级,纯内存 -- 结构化,易于查询 -- 显式状态,减少推理 +Features: +- Lightweight, pure in-memory +- Structured, easy to query +- Explicit state, less reasoning """ from typing import List, Dict, Set @@ -16,33 +16,33 @@ @dataclass class WorkingMemory: """ - 工作记忆 - 最近 5-10 步的结构化操作记录 + Working memory - Structured operation records for recent 5-10 steps - 用于显式跟踪 Agent 的操作历史,帮助弱模型避免循环 + Used to explicitly track Agent's operation history, helping weak models avoid loops """ - # 操作记录 (保留顺序) + # Operation records (preserve order) files_read: List[str] = field(default_factory=list) files_written: List[str] = field(default_factory=list) commands_run: List[Dict] = field(default_factory=list) # {cmd, time, success} - # 工具使用统计 + # Tool usage statistics tools_used: Dict[str, int] = field(default_factory=dict) - # 显式状态 + # Explicit state current_phase: str = "initialization" phase_progress: str = "0/0" - # 去重集合 (快速查询) + # Deduplication sets (fast lookup) _files_read_set: Set[str] = field(default_factory=set, init=False, repr=False) _files_written_set: Set[str] = field(default_factory=set, init=False, repr=False) def add_file_read(self, path: str) -> bool: """ - 记录文件读取 + Record file read Args: - path: 文件路径 + path: File path Returns: True if new, False if duplicate @@ -52,18 +52,18 @@ def add_file_read(self, path: str) -> bool: self.files_read.append(path) self._files_read_set.add(path) else: - # 即使是重复,也记录(用于检测循环) + # Even if duplicate, record (for loop detection) self.files_read.append(path) return is_new def add_file_written(self, path: str): - """记录文件写入""" + """Record file write""" if path not in self._files_written_set: self.files_written.append(path) self._files_written_set.add(path) def add_command(self, cmd: str, success: bool): - """记录命令执行""" + """Record command execution""" self.commands_run.append({ 'cmd': cmd, 'time': datetime.now().isoformat(), @@ -71,109 +71,109 @@ def add_command(self, cmd: str, success: bool): }) def increment_tool(self, tool_name: str): - """增加工具使用计数""" + """Increment tool usage count""" self.tools_used[tool_name] = self.tools_used.get(tool_name, 0) + 1 def update_phase(self, phase: str, progress: str): - """更新当前阶段""" + """Update current phase""" self.current_phase = phase self.phase_progress = progress def to_prompt(self, max_items: int = 10) -> str: """ - 转换为弱模型友好的 prompt 文本 + Convert to weak-model-friendly prompt text - 设计原则: - - 使用 emoji 和树状结构 (视觉清晰) - - 显示数量统计 (让模型有"进度感") - - 高亮最近项目 (时间就近性) - - 明确警告重复操作 + Design principles: + - Use emoji and tree structure (visual clarity) + - Show quantity statistics (give model "progress sense") + - Highlight recent items (temporal proximity) + - Clearly warn about repeated operations Args: - max_items: 最多显示的项目数 + max_items: Maximum items to display Returns: - 格式化的 prompt 文本 + Formatted prompt text """ - recent_files = self.files_read[-max_items:] if self.files_read else ["无"] + recent_files = self.files_read[-max_items:] if self.files_read else ["None"] files_summary = ", ".join(recent_files) if len(recent_files) <= 5 else \ - ", ".join(recent_files[:5]) + f" ... (共 {len(self.files_read)} 个)" + ", ".join(recent_files[:5]) + f" ... ({len(self.files_read)} total)" - recent_written = self.files_written[-5:] if self.files_written else ["无"] + recent_written = self.files_written[-5:] if self.files_written else ["None"] written_summary = ", ".join(recent_written) - recent_cmds = [c['cmd'][:50] for c in self.commands_run[-3:]] if self.commands_run else ["无"] + recent_cmds = [c['cmd'][:50] for c in self.commands_run[-3:]] if self.commands_run else ["None"] cmd_summary = "\n ".join(recent_cmds) return f""" ╭──────────────────────────────────────────────────────────────╮ -│ 📋 工作记忆 (WORKING MEMORY) │ +│ 📋 WORKING MEMORY │ ╰──────────────────────────────────────────────────────────────╯ -🎯 当前阶段: {self.current_phase} ({self.phase_progress}) +🎯 Current Phase: {self.current_phase} ({self.phase_progress}) -📂 已读文件 (共 {len(self.files_read)} 个): +📂 Files Read ({len(self.files_read)} total): {files_summary} -✏️ 已写文件 (共 {len(self.files_written)} 个): +✏️ Files Written ({len(self.files_written)} total): {written_summary} -⚙️ 已执行命令 (共 {len(self.commands_run)} 个): +⚙️ Commands Executed ({len(self.commands_run)} total): {cmd_summary} -📊 工具使用统计: +📊 Tool Usage Statistics: {self._format_tool_stats()} -⚠️ 重要提醒: - • 如果你想读的文件已在"已读"列表 → 不要重复读取! - • 如果已读文件超过 10 个 → 应该开始分析而非继续收集 - • 如果同一工具使用超过 5 次 → 可能陷入循环,改变策略! +⚠️ Important Reminders: + • If file you want to read is in "Files Read" list → Don't repeat reading! + • If files read exceeds 10 → Should start analysis instead of continuing collection + • If same tool used more than 5 times → May be stuck in loop, change strategy! """ def _format_tool_stats(self) -> str: - """格式化工具统计""" + """Format tool statistics""" if not self.tools_used: - return " (暂无)" + return " (None yet)" sorted_tools = sorted(self.tools_used.items(), key=lambda x: x[1], reverse=True) stats = [] for tool, count in sorted_tools[:5]: - warning = " ⚠️ 过度使用!" if count > 5 else "" - stats.append(f"{tool}: {count}次{warning}") + warning = " ⚠️ Overused!" if count > 5 else "" + stats.append(f"{tool}: {count} times{warning}") return "\n ".join(stats) def detect_loop(self) -> tuple[bool, str]: """ - 检测是否陷入循环 + Detect if stuck in loop Returns: (is_loop, reason) """ - # 规则 1: 单个文件读取超过 2 次 + # Rule 1: Single file read more than 2 times file_counts = Counter(self.files_read) for file, count in file_counts.items(): if count > 2: - return True, f"文件 '{file}' 已读取 {count} 次!" + return True, f"File '{file}' read {count} times!" - # 规则 2: 单个工具使用超过 10 次 + # Rule 2: Single tool used more than 10 times for tool, count in self.tools_used.items(): if count > 10: - return True, f"工具 '{tool}' 已使用 {count} 次!" + return True, f"Tool '{tool}' used {count} times!" - # 规则 3: 最近 5 次操作都是 read_file + # Rule 3: Last 5 operations all read_file if len(self.files_read) >= 5: recent = self.files_read[-5:] - if len(set(recent)) <= 2: # 只在读 2 个文件来回切换 - return True, f"最近 5 次操作都在读取文件: {set(recent)}" + if len(set(recent)) <= 2: # Only switching between reading 2 files + return True, f"Last 5 operations all reading files: {set(recent)}" return False, "" def get_stats(self) -> Dict: """ - 获取统计信息 + Get statistics Returns: - 统计信息字典 + Statistics dictionary """ return { 'files_read_count': len(self.files_read), @@ -187,7 +187,7 @@ def get_stats(self) -> Dict: } def clear(self): - """清空工作记忆""" + """Clear working memory""" self.files_read.clear() self.files_written.clear() self.commands_run.clear() diff --git a/src/clis/cli_commands/config_cli.py b/src/clis/cli_commands/config_cli.py index 147749a..9d6c9be 100644 --- a/src/clis/cli_commands/config_cli.py +++ b/src/clis/cli_commands/config_cli.py @@ -13,13 +13,13 @@ @click.group(name="config") def config_cli(): - """配置管理 (Configuration management)""" + """Configuration management""" pass @config_cli.command(name="show") def show_config() -> None: - """显示所有配置值""" + """Show all configuration values""" console = Console() config_manager = ConfigManager() @@ -62,7 +62,7 @@ def show_config() -> None: @config_cli.command(name="get") @click.argument("key") def get_config(key: str) -> None: - """获取配置值 + """Get configuration value Examples: clis config get llm.provider @@ -103,7 +103,7 @@ def get_config(key: str) -> None: @click.argument("key") @click.argument("value") def set_config(key: str, value: str) -> None: - """设置配置值 + """Set configuration value Examples: clis config set llm.temperature 0.7 @@ -117,7 +117,7 @@ def set_config(key: str, value: str) -> None: @config_cli.command(name="reset") @click.option("--confirm", is_flag=True, help="Skip confirmation") def reset_config(confirm: bool) -> None: - """重置配置为默认值""" + """Reset configuration to default values""" if not confirm: if not click.confirm("Are you sure you want to reset all configuration?"): click.echo("Cancelled.") @@ -135,7 +135,7 @@ def reset_config(confirm: bool) -> None: @config_cli.command(name="path") def show_config_path() -> None: - """显示配置目录路径""" + """Show configuration directory path""" from clis.utils.platform import get_clis_dir config_dir = get_clis_dir() / "config" diff --git a/src/clis/cli_commands/memory_cli.py b/src/clis/cli_commands/memory_cli.py index f5f9dfa..0c6b330 100644 --- a/src/clis/cli_commands/memory_cli.py +++ b/src/clis/cli_commands/memory_cli.py @@ -25,22 +25,22 @@ @click.group(name="memory") def memory_cli(): - """管理任务记忆 (Manage task memories)""" + """Manage task memories""" pass @memory_cli.command() @click.option('--status', type=click.Choice(['active', 'completed', 'archived', 'failed']), - help='过滤任务状态') -@click.option('--limit', type=int, default=20, help='最大显示数量') -@click.option('--verbose', '-v', is_flag=True, help='显示详细信息') + help='Filter task status') +@click.option('--limit', type=int, default=20, help='Maximum number to display') +@click.option('--verbose', '-v', is_flag=True, help='Show detailed information') def list(status: Optional[str], limit: int, verbose: bool): - """列出任务记忆""" + """List task memories""" from clis.agent.memory_manager import MemoryManager manager = MemoryManager() - # 获取任务列表 + # Get task list if status: from clis.agent.memory_manager import TaskStatus status_enum = TaskStatus(status) @@ -49,11 +49,11 @@ def list(status: Optional[str], limit: int, verbose: bool): tasks = manager.list_tasks(limit=limit) if not tasks: - console.print("[yellow]没有找到任务记忆[/yellow]") + console.print("[yellow]No task memories found[/yellow]") return - # 创建表格 - table = Table(title=f"📋 任务记忆 (共 {len(tasks)} 个)") + # Create table + table = Table(title=f"📋 Task Memories ({len(tasks)} total)") table.add_column("Task ID", style="cyan") table.add_column("Status", style="magenta") table.add_column("Created", style="green") @@ -79,15 +79,15 @@ def list(status: Optional[str], limit: int, verbose: bool): row = [task_id, status_display, created, description] if verbose: - # 计算持续时间 + # Calculate duration if 'completed_at' in task: start = datetime.fromisoformat(task['created_at']) end = datetime.fromisoformat(task['completed_at']) - duration = str(end - start).split('.')[0] # 去掉微秒 + duration = str(end - start).split('.')[0] # Remove microseconds else: - duration = "进行中" + duration = "In progress" - # TODO: 从文件读取统计信息 + # TODO: Read statistics from file files_count = "N/A" row.extend([duration, files_count]) @@ -96,39 +96,39 @@ def list(status: Optional[str], limit: int, verbose: bool): console.print(table) - # 提示 - console.print(f"\n💡 使用 [cyan]clis memory show [/cyan] 查看详情") + # Hint + console.print(f"\n💡 Use [cyan]clis memory show [/cyan] to view details") @memory_cli.command() @click.argument('task_id') -@click.option('--full', is_flag=True, help='显示完整内容') -@click.option('--edit', is_flag=True, help='在编辑器中打开') +@click.option('--full', is_flag=True, help='Show full content') +@click.option('--edit', is_flag=True, help='Open in editor') def show(task_id: str, full: bool, edit: bool): - """查看任务详情""" + """View task details""" from clis.agent.memory_manager import MemoryManager manager = MemoryManager() task_file = manager.get_task_file(task_id) if not task_file or not task_file.exists(): - console.print(f"[red]❌ 任务 {task_id} 不存在[/red]") + console.print(f"[red]❌ Task {task_id} does not exist[/red]") return - # 在编辑器中打开 + # Open in editor if edit: import subprocess editor = os.environ.get('EDITOR', 'vim') subprocess.run([editor, str(task_file)]) return - # 读取任务信息 + # Read task information task_info = manager.metadata['tasks'].get(task_id) if not task_info: - console.print(f"[red]❌ 任务元数据不存在[/red]") + console.print(f"[red]❌ Task metadata does not exist[/red]") return - # 显示任务信息 + # Display task information status_icon = { 'active': '🔄', 'completed': '✅', @@ -155,106 +155,106 @@ def show(task_id: str, full: bool, edit: bool): panel = Panel(info_text, title=f"📋 Task: {task_id}", border_style="cyan") console.print(panel) - # 显示文件内容 + # Show file content if full: content = task_file.read_text(encoding='utf-8') console.print("\n" + "="*60) console.print(content) else: - # 显示摘要 - console.print(f"\n[dim]文件位置: {task_file}[/dim]") - console.print("[dim]使用 --full 显示完整内容[/dim]") - console.print("[dim]使用 --edit 在编辑器中打开[/dim]") + # Show summary + console.print(f"\n[dim]File location: {task_file}[/dim]") + console.print("[dim]Use --full to show full content[/dim]") + console.print("[dim]Use --edit to open in editor[/dim]") @memory_cli.command() @click.argument('query') -@click.option('--content', is_flag=True, help='搜索文件内容') -@click.option('--regex', is_flag=True, help='使用正则表达式') +@click.option('--content', is_flag=True, help='Search file content') +@click.option('--regex', is_flag=True, help='Use regular expression') def search(query: str, content: bool, regex: bool): - """搜索任务记忆""" + """Search task memories""" from clis.agent.memory_manager import MemoryManager manager = MemoryManager() if content: - console.print("[yellow]内容搜索功能开发中...[/yellow]") + console.print("[yellow]Content search feature is under development...[/yellow]") return - # 简单搜索描述 + # Simple description search results = manager.search_tasks(query) if not results: - console.print(f"[yellow]没有找到匹配 '{query}' 的任务[/yellow]") + console.print(f"[yellow]No tasks matching '{query}' found[/yellow]") return - console.print(f"[green]找到 {len(results)} 个匹配的任务:[/green]\n") + console.print(f"[green]Found {len(results)} matching tasks:[/green]\n") for task in results: console.print(f" • [cyan]{task['id']}[/cyan]: {task['description']}") - console.print(f"\n💡 使用 [cyan]clis memory show [/cyan] 查看详情") + console.print(f"\n💡 Use [cyan]clis memory show [/cyan] to view details") @memory_cli.command() @click.argument('task_id', required=False) -@click.option('--status', type=click.Choice(['failed']), help='删除指定状态的所有任务') -@click.option('--older-than', help='删除早于指定时间的任务 (如: 90days)') -@click.option('--force', '-f', is_flag=True, help='跳过确认') +@click.option('--status', type=click.Choice(['failed']), help='Delete all tasks with specified status') +@click.option('--older-than', help='Delete tasks older than specified time (e.g.: 90days)') +@click.option('--force', '-f', is_flag=True, help='Skip confirmation') def delete(task_id: Optional[str], status: Optional[str], older_than: Optional[str], force: bool): - """删除任务记忆""" + """Delete task memories""" from clis.agent.memory_manager import MemoryManager manager = MemoryManager() - # 删除单个任务 + # Delete single task if task_id: task_info = manager.metadata['tasks'].get(task_id) if not task_info: - console.print(f"[red]❌ 任务 {task_id} 不存在[/red]") + console.print(f"[red]❌ Task {task_id} does not exist[/red]") return - # 确认 + # Confirm if not force: - confirm = click.confirm(f"确定要删除任务 {task_id}?") + confirm = click.confirm(f"Are you sure you want to delete task {task_id}?") if not confirm: - console.print("[yellow]已取消[/yellow]") + console.print("[yellow]Cancelled[/yellow]") return - # 删除文件 + # Delete file task_file = manager.get_task_file(task_id) if task_file and task_file.exists(): task_file.unlink() - # 删除元数据 + # Delete metadata del manager.metadata['tasks'][task_id] manager._save_metadata() - console.print(f"[green]✅ 已删除任务 {task_id}[/green]") + console.print(f"[green]✅ Task {task_id} deleted[/green]") return - # 批量删除 + # Batch delete if status or older_than: - console.print("[yellow]批量删除功能开发中...[/yellow]") + console.print("[yellow]Batch delete feature is under development...[/yellow]") return - console.print("[red]请指定 task_id 或使用 --status/--older-than 选项[/red]") + console.print("[red]Please specify task_id or use --status/--older-than options[/red]") @memory_cli.command() @click.argument('task_id', required=False) -@click.option('--all-completed', is_flag=True, help='归档所有已完成任务') +@click.option('--all-completed', is_flag=True, help='Archive all completed tasks') def archive(task_id: Optional[str], all_completed: bool): - """归档任务记忆""" + """Archive task memories""" from clis.agent.memory_manager import MemoryManager manager = MemoryManager() if task_id: manager._archive_task(task_id) - console.print(f"[green]✅ 已归档任务 {task_id}[/green]") + console.print(f"[green]✅ Task {task_id} archived[/green]") elif all_completed: - # 归档所有已完成任务 + # Archive all completed tasks completed_tasks = [ tid for tid, info in manager.metadata['tasks'].items() if info['status'] == 'completed' @@ -263,49 +263,49 @@ def archive(task_id: Optional[str], all_completed: bool): for tid in completed_tasks: manager._archive_task(tid) - console.print(f"[green]✅ 已归档 {len(completed_tasks)} 个任务[/green]") + console.print(f"[green]✅ {len(completed_tasks)} tasks archived[/green]") else: - console.print("[red]请指定 task_id 或使用 --all-completed[/red]") + console.print("[red]Please specify task_id or use --all-completed[/red]") @memory_cli.command() -@click.option('--keep-days', type=int, help='保留天数') -@click.option('--archive', is_flag=True, help='清理归档任务') -@click.option('--keep-months', type=int, default=3, help='归档保留月数') -@click.option('--dry-run', is_flag=True, help='预览清理 (不实际删除)') +@click.option('--keep-days', type=int, help='Days to keep') +@click.option('--archive', is_flag=True, help='Clean archived tasks') +@click.option('--keep-months', type=int, default=3, help='Months to keep archived tasks') +@click.option('--dry-run', is_flag=True, help='Preview cleanup (no actual deletion)') def cleanup(keep_days: Optional[int], archive: bool, keep_months: int, dry_run: bool): - """清理过期记忆""" + """Clean up expired memories""" from clis.agent.memory_manager import MemoryManager manager = MemoryManager() - console.print("🧹 清理记忆...\n") + console.print("🧹 Cleaning up memories...\n") - # 执行清理 + # Execute cleanup if dry_run: - console.print("[yellow]预览模式 (不会实际删除)[/yellow]\n") + console.print("[yellow]Preview mode (no actual deletion)[/yellow]\n") if keep_days: - # 归档旧任务 + # Archive old tasks manager.archive_old_tasks(days=keep_days) - console.print(f"[green]✅ 已归档超过 {keep_days} 天的任务[/green]") + console.print(f"[green]✅ Tasks older than {keep_days} days archived[/green]") else: - # 使用配置 + # Use configuration manager.cleanup() - console.print("[green]✅ 已执行自动清理[/green]") + console.print("[green]✅ Auto cleanup executed[/green]") if archive: - console.print("[yellow]归档清理功能开发中...[/yellow]") + console.print("[yellow]Archive cleanup feature is under development...[/yellow]") @memory_cli.command() @click.argument('task_id', required=False) -@click.option('--output', '-o', help='输出文件路径') +@click.option('--output', '-o', help='Output file path') @click.option('--format', type=click.Choice(['markdown', 'json', 'html']), - default='markdown', help='导出格式') -@click.option('--all', is_flag=True, help='导出所有任务') + default='markdown', help='Export format') +@click.option('--all', is_flag=True, help='Export all tasks') def export(task_id: Optional[str], output: Optional[str], format: str, all: bool): - """导出任务为文档""" + """Export tasks as documents""" from clis.agent.memory_manager import MemoryManager manager = MemoryManager() @@ -313,36 +313,36 @@ def export(task_id: Optional[str], output: Optional[str], format: str, all: bool if task_id: task_file = manager.get_task_file(task_id) if not task_file or not task_file.exists(): - console.print(f"[red]❌ 任务 {task_id} 不存在[/red]") + console.print(f"[red]❌ Task {task_id} does not exist[/red]") return - # 读取内容 + # Read content content = task_file.read_text(encoding='utf-8') - # 导出 + # Export if output: output_path = Path(output) output_path.write_text(content, encoding='utf-8') - console.print(f"[green]✅ 已导出到 {output_path}[/green]") + console.print(f"[green]✅ Exported to {output_path}[/green]") else: console.print(content) elif all: - console.print("[yellow]批量导出功能开发中...[/yellow]") + console.print("[yellow]Batch export feature is under development...[/yellow]") else: - console.print("[red]请指定 task_id 或使用 --all[/red]") + console.print("[red]Please specify task_id or use --all[/red]") @memory_cli.command() -@click.option('--verbose', '-v', is_flag=True, help='显示详细统计') +@click.option('--verbose', '-v', is_flag=True, help='Show detailed statistics') def stats(verbose: bool): - """显示记忆统计信息""" + """Display memory statistics""" from clis.agent.memory_manager import MemoryManager manager = MemoryManager() - # 统计任务数量 + # Count tasks status_counts = {} total_size = 0 @@ -350,15 +350,15 @@ def stats(verbose: bool): status = task_info['status'] status_counts[status] = status_counts.get(status, 0) + 1 - # 计算文件大小 + # Calculate file size task_file = manager.get_task_file(task_id) if task_file and task_file.exists(): total_size += task_file.stat().st_size - # 格式化大小 + # Format size size_mb = total_size / (1024 * 1024) - # 显示统计 + # Display statistics stats_text = f""" [bold cyan]Tasks:[/bold cyan] • Active: {status_counts.get('active', 0)} @@ -381,7 +381,7 @@ def stats(verbose: bool): console.print(panel) if verbose: - console.print("\n[dim]详细统计功能开发中...[/dim]") + console.print("\n[dim]Detailed statistics feature is under development...[/dim]") @memory_cli.command() @@ -389,13 +389,13 @@ def stats(verbose: bool): @click.argument('key', required=False) @click.argument('value', required=False) def config(action: str, key: Optional[str], value: Optional[str]): - """配置记忆管理""" + """Configure memory management""" from clis.agent.memory_manager import MemoryManager manager = MemoryManager() if action == 'show': - # 显示配置 + # Show configuration config = manager.metadata['config'] console.print("[bold cyan]Memory Configuration:[/bold cyan]\n") for k, v in config.items(): @@ -403,12 +403,12 @@ def config(action: str, key: Optional[str], value: Optional[str]): elif action == 'set': if not key or not value: - console.print("[red]请指定 key 和 value[/red]") + console.print("[red]Please specify key and value[/red]") return - # 设置配置 + # Set configuration if key in manager.metadata['config']: - # 类型转换 + # Type conversion old_value = manager.metadata['config'][key] if isinstance(old_value, bool): value = value.lower() in ('true', '1', 'yes') @@ -417,32 +417,32 @@ def config(action: str, key: Optional[str], value: Optional[str]): manager.metadata['config'][key] = value manager._save_metadata() - console.print(f"[green]✅ 已设置 {key} = {value}[/green]") + console.print(f"[green]✅ Set {key} = {value}[/green]") else: - console.print(f"[red]未知配置项: {key}[/red]") + console.print(f"[red]Unknown configuration item: {key}[/red]") elif action == 'reset': - # 重置配置 + # Reset configuration manager.metadata['config'] = manager._default_config() manager._save_metadata() - console.print("[green]✅ 已重置为默认配置[/green]") + console.print("[green]✅ Reset to default configuration[/green]") -# 快捷命令 +# Shortcut commands @memory_cli.command() -@click.option('--limit', type=int, default=5, help='显示数量') +@click.option('--limit', type=int, default=5, help='Number to display') def recent(limit: int): - """查看最近的任务""" + """View recent tasks""" from clis.agent.memory_manager import MemoryManager manager = MemoryManager() tasks = manager.list_tasks(limit=limit) if not tasks: - console.print("[yellow]没有任务记忆[/yellow]") + console.print("[yellow]No task memories[/yellow]") return - console.print(f"[bold cyan]📋 最近 {len(tasks)} 个任务:[/bold cyan]\n") + console.print(f"[bold cyan]📋 Recent {len(tasks)} tasks:[/bold cyan]\n") for task in tasks: status_icon = { @@ -458,17 +458,17 @@ def recent(limit: int): @memory_cli.command() def current(): - """查看当前活跃任务""" + """View current active tasks""" from clis.agent.memory_manager import MemoryManager, TaskStatus manager = MemoryManager() tasks = manager.list_tasks(status=TaskStatus.ACTIVE) if not tasks: - console.print("[yellow]没有活跃任务[/yellow]") + console.print("[yellow]No active tasks[/yellow]") return - console.print(f"[bold cyan]🔄 当前活跃任务 ({len(tasks)} 个):[/bold cyan]\n") + console.print(f"[bold cyan]🔄 Current active tasks ({len(tasks)}):[/bold cyan]\n") for task in tasks: console.print(f" • [cyan]{task['id']}[/cyan]: {task['description']}") @@ -476,14 +476,14 @@ def current(): @memory_cli.command() def open(): - """打开记忆目录""" + """Open memory directory""" from clis.agent.memory_manager import MemoryManager import subprocess import sys manager = MemoryManager() - # 根据操作系统打开文件管理器 + # Open file manager based on OS if sys.platform == 'darwin': # macOS subprocess.run(['open', str(manager.memory_dir)]) elif sys.platform == 'win32': # Windows @@ -491,18 +491,18 @@ def open(): else: # Linux subprocess.run(['xdg-open', str(manager.memory_dir)]) - console.print(f"[green]✅ 已打开目录: {manager.memory_dir}[/green]") + console.print(f"[green]✅ Directory opened: {manager.memory_dir}[/green]") @memory_cli.command() def tidy(): - """快速清理 (归档 + 清理失败任务)""" + """Quick cleanup (archive + clean failed tasks)""" from clis.agent.memory_manager import MemoryManager manager = MemoryManager() - console.print("🧹 执行快速清理...\n") + console.print("🧹 Running quick cleanup...\n") manager.cleanup() - console.print("[green]✅ 清理完成![/green]") + console.print("[green]✅ Cleanup complete![/green]") diff --git a/src/clis/cli_commands/skill_cli.py b/src/clis/cli_commands/skill_cli.py index ff8aa87..190ca8b 100644 --- a/src/clis/cli_commands/skill_cli.py +++ b/src/clis/cli_commands/skill_cli.py @@ -9,13 +9,13 @@ @click.group(name="skill") def skill_cli(): - """技能管理 (Skill management)""" + """Skill management""" pass @skill_cli.command(name="list") def list_skills() -> None: - """列出所有可用技能""" + """List all available skills""" from clis.output.formatter import OutputFormatter from clis.router import SkillRouter @@ -38,7 +38,7 @@ def list_skills() -> None: @click.option("--auto", is_flag=True, help="Use LLM to auto-generate skill based on prompt") @click.option("--tools", multiple=True, help="Tools to include in the skill") def create_skill(name: str, auto: bool, tools: Tuple[str, ...]) -> None: - """创建新技能 + """Create new skill Examples: clis skill create my-skill @@ -64,7 +64,7 @@ def create_skill(name: str, auto: bool, tools: Tuple[str, ...]) -> None: @click.argument("name") @click.option("--editor", help="Editor to use (code, vim, nano, etc.)") def edit_skill(name: str, editor: Optional[str] = None) -> None: - """编辑技能文件""" + """Edit skill file""" import subprocess import os from clis.router import SkillRouter @@ -94,7 +94,7 @@ def edit_skill(name: str, editor: Optional[str] = None) -> None: @skill_cli.command(name="validate") @click.argument("name") def validate_skill(name: str) -> None: - """验证技能格式""" + """Validate skill format""" from clis.router import SkillRouter from clis.output.formatter import OutputFormatter @@ -116,7 +116,7 @@ def validate_skill(name: str) -> None: @click.argument("query", required=False) @click.option("--dry-run", is_flag=True, help="Show commands without executing") def test_skill(name: str, query: Optional[str], dry_run: bool) -> None: - """测试技能 + """Test skill Examples: clis skill test docker-manager @@ -152,7 +152,7 @@ def test_skill(name: str, query: Optional[str], dry_run: bool) -> None: @click.argument("source") @click.option("--with-deps", is_flag=True, help="Download skill dependencies") def install_skill(source: str, with_deps: bool) -> None: - """从 GitHub 或 URL 安装技能 + """Install skill from GitHub or URL Examples: clis skill install username/repo diff --git a/src/clis/cli_commands/system_cli.py b/src/clis/cli_commands/system_cli.py index fef324a..c22e526 100644 --- a/src/clis/cli_commands/system_cli.py +++ b/src/clis/cli_commands/system_cli.py @@ -7,7 +7,7 @@ @click.command(name="version") def version() -> None: - """显示版本信息""" + """Show version information""" from clis import __version__ from clis.utils.platform import get_platform, get_clis_dir @@ -18,7 +18,7 @@ def version() -> None: @click.command(name="doctor") def doctor() -> None: - """检查 CLIS 环境和配置""" + """Check CLIS environment and configuration""" import platform import sys from pathlib import Path @@ -144,10 +144,10 @@ def doctor() -> None: help="LLM provider to use", ) def init(provider: str = None) -> None: - """初始化 CLIS 配置 (交互式向导) + """Initialize CLIS configuration (interactive wizard) This will create configuration files in ~/.clis/config/ with - detailed comments in both English and Chinese. + detailed comments. """ from clis.config import ConfigManager