Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 77 additions & 2 deletions cortex/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
logging.getLogger("cortex.installation_history").setLevel(logging.ERROR)

sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))

from LLM.interpreter import CommandInterpreter
from cortex.coordinator import InstallationCoordinator, StepStatus
from cortex.installation_history import (
Expand Down Expand Up @@ -53,6 +52,8 @@ def _debug(self, message: str):
"""Print debug info only in verbose mode"""
if self.verbose:
console.print(f"[dim][DEBUG] {message}[/dim]")



def _get_api_key(self) -> Optional[str]:
# Check if using Ollama (no API key needed)
Expand Down Expand Up @@ -202,7 +203,6 @@ def install(self, software: str, execute: bool = False, dry_run: bool = False):
interpreter = CommandInterpreter(api_key=api_key, provider=provider)

self._print_status("📦", "Planning installation...")

for _ in range(10):
self._animate_spinner("Analyzing system requirements...")
self._clear_line()
Expand Down Expand Up @@ -310,7 +310,56 @@ def progress_callback(current, total, step):
history.update_installation(install_id, InstallationStatus.FAILED, str(e))
self._print_error(f"Unexpected error: {str(e)}")
return 1

def diagnose(self,error: Optional[str] = None,image: Optional[str] = None,clipboard: bool = False,):
"""Diagnose an error from text input (MVP: text-only)."""

if not error:
self._print_error("Please provide an error message to diagnose")
return 1

cx_print("Diagnosing error...", "info")

provider = self._get_provider()

# 🔹 OFFLINE FALLBACK (NO API KEYS REQUIRED)
if provider == "ollama":
print("\nDiagnosis:\n")
print(
f"Detected error:\n{error}\n\n"
"Possible causes:\n"
"- Interrupted package installation\n"
"- Broken dpkg state\n"
"- Locked apt database\n\n"
"Suggested fixes:\n"
"sudo dpkg --configure -a\n"
"sudo apt --fix-broken install\n"
"sudo apt update\n"
)
return 0

# 🔹 LLM PATH (Claude / Kimi later)
api_key = self._get_api_key()
if not api_key:
return 1

try:
from cortex.llm_router import complete_task, TaskType

response = complete_task(
prompt=f"Diagnose this Linux error and suggest fixes:\n{error}",
task_type=TaskType.ERROR_DEBUGGING,
)

print("\nDiagnosis:\n")
print(response)
return 0

except Exception as e:
self._print_error(str(e))
return 1


def history(self, limit: int = 20, status: Optional[str] = None, show_id: Optional[str] = None):
"""Show installation history"""
history = InstallationHistory()
Expand Down Expand Up @@ -577,6 +626,26 @@ def main():
install_parser.add_argument('software', type=str, help='Software to install')
install_parser.add_argument('--execute', action='store_true', help='Execute commands')
install_parser.add_argument('--dry-run', action='store_true', help='Show commands only')

# Diagnose command
diagnose_parser = subparsers.add_parser(
'diagnose',
help='Diagnose an error from text or image'
)
diagnose_parser.add_argument(
'error',
nargs='?',
help='Error message text'
)
diagnose_parser.add_argument(
'--image',
help='Path to error screenshot (PNG, JPG, WebP)'
)
diagnose_parser.add_argument(
'--clipboard',
action='store_true',
help='Use image from clipboard'
)

# History command
history_parser = subparsers.add_parser('history', help='View history')
Expand Down Expand Up @@ -634,6 +703,12 @@ def main():
return cli.status()
elif args.command == 'install':
return cli.install(args.software, execute=args.execute, dry_run=args.dry_run)
elif args.command == 'diagnose':
return cli.diagnose(
error=args.error,
image=args.image,
clipboard=args.clipboard
)
elif args.command == 'history':
return cli.history(limit=args.limit, status=args.status, show_id=args.show_id)
elif args.command == 'rollback':
Expand Down
29 changes: 20 additions & 9 deletions cortex/llm_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
from typing import Dict, List, Optional, Any, Literal
from enum import Enum
from dataclasses import dataclass, asdict
from anthropic import Anthropic
from openai import OpenAI
import logging

# Configure logging
Expand Down Expand Up @@ -131,17 +129,27 @@ def __init__(
self.kimi_client = None

if self.claude_api_key:
self.claude_client = Anthropic(api_key=self.claude_api_key)
logger.info("✅ Claude API client initialized")
try:
from anthropic import Anthropic
self.claude_client = Anthropic(api_key=self.claude_api_key)
logger.info("✅ Claude API client initialized")
except ImportError:
self.claude_client = None
logger.warning("⚠️ anthropic not installed, Claude disabled")
else:
logger.warning("⚠️ No Claude API key provided")

if self.kimi_api_key:
self.kimi_client = OpenAI(
api_key=self.kimi_api_key,
base_url="https://api.moonshot.ai/v1"
)
logger.info("✅ Kimi K2 API client initialized")
try:
from openai import OpenAI
self.kimi_client = OpenAI(
api_key=self.kimi_api_key,
base_url="https://api.moonshot.ai/v1"
)
logger.info("✅ Kimi K2 API client initialized")
except ImportError:
self.kimi_client = None
logger.warning("⚠️ openai not installed, Kimi disabled")
else:
logger.warning("⚠️ No Kimi K2 API key provided")

Expand Down Expand Up @@ -495,3 +503,6 @@ def complete_task(
print("=== Usage Statistics ===")
stats = router.get_stats()
print(json.dumps(stats, indent=2))