diff --git a/chat.py b/chat.py index 29f056d..1865b32 100644 --- a/chat.py +++ b/chat.py @@ -348,10 +348,147 @@ class MachaChatSession: """ response = self.process_message(question, verbose=verbose) return response + + def explain_action(self, action_index: int) -> str: + """Explain a pending action from the approval queue""" + # Get action from approval queue + approval_queue_file = self.agent.state_dir / "approval_queue.json" + + if not approval_queue_file.exists(): + return "Error: No approval queue found." + + try: + with open(approval_queue_file, 'r') as f: + queue = json.load(f) + + if not (0 <= action_index < len(queue)): + return f"Error: Action #{action_index} not found in approval queue (queue has {len(queue)} items)." + + action_item = queue[action_index] + except Exception as e: + return f"Error reading approval queue: {e}" + + action = action_item.get("action", {}) + context = action_item.get("context", {}) + timestamp = action_item.get("timestamp", "unknown") + + # Build explanation prompt + prompt = f"""You are Macha, explaining a proposed system action to the user. + +ACTION DETAILS: +- Proposed Action: {action.get('proposed_action', 'N/A')} +- Action Type: {action.get('action_type', 'N/A')} +- Risk Level: {action.get('risk_level', 'N/A')} +- Diagnosis: {action.get('diagnosis', 'N/A')} +- Commands to execute: {', '.join(action.get('commands', []))} +- Timestamp: {timestamp} + +SYSTEM CONTEXT: +{json.dumps(context, indent=2)} + +Please provide a clear, concise explanation of: +1. What problem was detected +2. What this action will do to fix it +3. Why this approach was chosen +4. Any potential risks or side effects +5. Expected outcome + +Be conversational and helpful. Use plain language, not technical jargon unless necessary.""" + + try: + response = self.agent._query_ollama(prompt, temperature=0.7) + return response + except Exception as e: + return f"Error generating explanation: {e}" + + def answer_action_followup(self, action_index: int, user_question: str) -> str: + """Answer a follow-up question about a pending action""" + # Get action from approval queue + approval_queue_file = self.agent.state_dir / "approval_queue.json" + + if not approval_queue_file.exists(): + return "Error: No approval queue found." + + try: + with open(approval_queue_file, 'r') as f: + queue = json.load(f) + + if not (0 <= action_index < len(queue)): + return f"Error: Action #{action_index} not found." + + action_item = queue[action_index] + except Exception as e: + return f"Error reading approval queue: {e}" + + action = action_item.get("action", {}) + context = action_item.get("context", {}) + + # Build follow-up prompt + prompt = f"""You are Macha, answering a follow-up question about a proposed action. + +ACTION SUMMARY: +- Proposed: {action.get('proposed_action', 'N/A')} +- Type: {action.get('action_type', 'N/A')} +- Risk: {action.get('risk_level', 'N/A')} +- Diagnosis: {action.get('diagnosis', 'N/A')} +- Commands: {', '.join(action.get('commands', []))} + +SYSTEM CONTEXT: +{json.dumps(context, indent=2)[:2000]} + +USER'S QUESTION: +{user_question} + +Please answer the user's question clearly and honestly. If you're uncertain about something, say so. Focus on helping them make an informed decision about whether to approve this action.""" + + try: + response = self.agent._query_ollama(prompt, temperature=0.7) + return response + except Exception as e: + return f"Error: {e}" def main(): - """Main entry point for macha-chat""" + """Main entry point for macha-chat and conversation.py""" + + # Check for --discuss flag (used by macha-approve discuss) + if "--discuss" in sys.argv: + try: + discuss_index = sys.argv.index("--discuss") + if discuss_index + 1 >= len(sys.argv): + print("Error: --discuss requires an action number", file=sys.stderr) + sys.exit(1) + + action_number = int(sys.argv[discuss_index + 1]) + + session = MachaChatSession() + + # Check if this is a follow-up question or initial explanation + if "--follow-up" in sys.argv: + followup_index = sys.argv.index("--follow-up") + if followup_index + 1 >= len(sys.argv): + print("Error: --follow-up requires a question", file=sys.stderr) + sys.exit(1) + + # Get the rest of the arguments as the question + question = " ".join(sys.argv[followup_index + 1:]) + response = session.answer_action_followup(action_number, question) + print(response) + else: + # Initial explanation + explanation = session.explain_action(action_number) + print(explanation) + + return + + except (ValueError, IndexError) as e: + print(f"Error: Invalid action number: {e}", file=sys.stderr) + sys.exit(1) + except Exception as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) + + # Normal interactive chat mode session = MachaChatSession() session.run_interactive() diff --git a/conversation.py b/conversation.py index 4f54cb0..2fab547 100644 --- a/conversation.py +++ b/conversation.py @@ -2,11 +2,12 @@ """ Macha conversation interface - legacy compatibility wrapper. This module now uses the unified chat.py implementation. +All discussion functionality has been moved to chat.py. """ # Import the unified implementation -from chat import ask_main +from chat import main as chat_main # Entry point if __name__ == "__main__": - ask_main() + chat_main() diff --git a/orchestrator.py b/orchestrator.py index 973ffdc..32f635c 100644 --- a/orchestrator.py +++ b/orchestrator.py @@ -20,6 +20,7 @@ from remote_monitor import RemoteMonitor from config_parser import ConfigParser from system_discovery import SystemDiscovery from issue_tracker import IssueTracker +from git_context import GitContext from typing import List @@ -63,6 +64,20 @@ class MachaOrchestrator: except Exception as e: self._log(f"Warning: Could not initialize config parser: {e}") + # Initialize git context + self.git_context = None + if self.config_parser: + try: + # Use the same local repo path as config_parser + local_repo_path = Path("/var/lib/macha/config-repo") + if local_repo_path.exists(): + self.git_context = GitContext(repo_path=str(local_repo_path)) + self._log(f"Git context initialized for {local_repo_path}") + else: + self._log(f"Warning: Config repo not found at {local_repo_path}") + except Exception as e: + self._log(f"Warning: Could not initialize git context: {e}") + # Initialize components self.monitor = SystemMonitor(state_dir) self.agent = MachaAgent( @@ -667,6 +682,22 @@ class MachaOrchestrator: if self._cycle_count % 10 == 1: # First cycle and every 10th self._update_service_registry() + # Refresh configuration repository every 3 cycles (~15 min) to keep git context current + # This ensures git_context has up-to-date information about recent config changes + if self._cycle_count % 3 == 1 and self.config_parser: + try: + self._log("Refreshing configuration repository...") + if self.config_parser.ensure_repo(): + self._log("✓ Configuration repository updated") + # Reinitialize git_context if it exists to pick up fresh data + if self.git_context: + local_repo_path = Path("/var/lib/macha/config-repo") + self.git_context = GitContext(repo_path=str(local_repo_path)) + else: + self._log("⚠ Could not refresh configuration repository") + except Exception as e: + self._log(f"⚠ Error refreshing config repo: {e}") + # Step 1: Monitor system self._log("Collecting system health data...") monitoring_data = self.monitor.collect_all()