Skip to content
Closed
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
21 changes: 21 additions & 0 deletions cortex/ui/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""Cortex UI - Modern terminal interface components."""

from .console import console, CortexConsole
from .theme import COLORS, SYMBOLS, CORTEX_THEME, PANEL_STYLES
from .prompts import CommandPrompt, MenuAction, PromptResult, confirm, select
from .progress import spinner, progress_bar, steps, indeterminate_progress, download_progress
from .errors import show_error, show_conflict, show_warning, auto_suggest_fix, SuggestedFix
from .explain import explain_command, explain_pipeline, CommandPart
from .trust import TrustManager, TrustScope, trust_manager
from .panels import ai_thinking, ai_response, status_panel, code_panel, diff_panel, summary_panel, welcome_banner, help_footer

Check failure on line 10 in cortex/ui/__init__.py

View workflow job for this annotation

GitHub Actions / lint

Ruff (I001)

cortex/ui/__init__.py:3:1: I001 Import block is un-sorted or un-formatted

Check failure on line 10 in cortex/ui/__init__.py

View workflow job for this annotation

GitHub Actions / Lint

Ruff (I001)

cortex/ui/__init__.py:3:1: I001 Import block is un-sorted or un-formatted
Comment on lines +3 to +10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix unsorted imports to pass CI.

The pipeline is failing due to Ruff I001 (unsorted imports). Reorder imports alphabetically by module name.

🔧 Sorted imports
 """Cortex UI - Modern terminal interface components."""

-from .console import console, CortexConsole
-from .theme import COLORS, SYMBOLS, CORTEX_THEME, PANEL_STYLES
-from .prompts import CommandPrompt, MenuAction, PromptResult, confirm, select
-from .progress import spinner, progress_bar, steps, indeterminate_progress, download_progress
 from .errors import show_error, show_conflict, show_warning, auto_suggest_fix, SuggestedFix
 from .explain import explain_command, explain_pipeline, CommandPart
-from .trust import TrustManager, TrustScope, trust_manager
 from .panels import ai_thinking, ai_response, status_panel, code_panel, diff_panel, summary_panel, welcome_banner, help_footer
+from .progress import spinner, progress_bar, steps, indeterminate_progress, download_progress
+from .prompts import CommandPrompt, MenuAction, PromptResult, confirm, select
+from .console import console, CortexConsole
+from .theme import COLORS, SYMBOLS, CORTEX_THEME, PANEL_STYLES
+from .trust import TrustManager, TrustScope, trust_manager
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from .console import console, CortexConsole
from .theme import COLORS, SYMBOLS, CORTEX_THEME, PANEL_STYLES
from .prompts import CommandPrompt, MenuAction, PromptResult, confirm, select
from .progress import spinner, progress_bar, steps, indeterminate_progress, download_progress
from .errors import show_error, show_conflict, show_warning, auto_suggest_fix, SuggestedFix
from .explain import explain_command, explain_pipeline, CommandPart
from .trust import TrustManager, TrustScope, trust_manager
from .panels import ai_thinking, ai_response, status_panel, code_panel, diff_panel, summary_panel, welcome_banner, help_footer
from .console import console, CortexConsole
from .errors import show_error, show_conflict, show_warning, auto_suggest_fix, SuggestedFix
from .explain import explain_command, explain_pipeline, CommandPart
from .panels import ai_thinking, ai_response, status_panel, code_panel, diff_panel, summary_panel, welcome_banner, help_footer
from .progress import spinner, progress_bar, steps, indeterminate_progress, download_progress
from .prompts import CommandPrompt, MenuAction, PromptResult, confirm, select
from .theme import COLORS, SYMBOLS, CORTEX_THEME, PANEL_STYLES
from .trust import TrustManager, TrustScope, trust_manager
🧰 Tools
🪛 GitHub Actions: CI

[error] 3-3: Import block is un-sorted or un-formatted

🪛 GitHub Check: lint

[failure] 3-10: Ruff (I001)
cortex/ui/init.py:3:1: I001 Import block is un-sorted or un-formatted

🪛 GitHub Check: Lint

[failure] 3-10: Ruff (I001)
cortex/ui/init.py:3:1: I001 Import block is un-sorted or un-formatted

🤖 Prompt for AI Agents
In @cortex/ui/__init__.py around lines 3 - 10, The imports in
cortex/ui/__init__.py are not alphabetized causing Ruff I001; reorder the
top-level import statements alphabetically by module name (for example:
.console, .errors, .explain, .panels, .progress, .prompts, .theme, .trust) while
preserving the existing imported symbols (CortexConsole/console,
show_error/etc., explain_command/etc., ai_thinking/etc., spinner/etc.,
CommandPrompt/etc., COLORS/etc., TrustManager/etc.) so only the import lines are
re-sorted and no other code changes are made.


__all__ = [
"console", "CortexConsole", "COLORS", "SYMBOLS", "CORTEX_THEME", "PANEL_STYLES",
"CommandPrompt", "MenuAction", "PromptResult", "confirm", "select",
"spinner", "progress_bar", "steps", "indeterminate_progress", "download_progress",
"show_error", "show_conflict", "show_warning", "auto_suggest_fix", "SuggestedFix",
"explain_command", "explain_pipeline", "CommandPart",
"TrustManager", "TrustScope", "trust_manager",
"ai_thinking", "ai_response", "status_panel", "code_panel", "diff_panel", "summary_panel", "welcome_banner", "help_footer",
]
__version__ = "0.1.0"
78 changes: 78 additions & 0 deletions cortex/ui/console.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""Cortex Console - Themed console singleton with semantic message methods."""

from typing import Optional
from rich.console import Console as RichConsole
from rich.panel import Panel
from rich.syntax import Syntax
from rich.markdown import Markdown
from .theme import CORTEX_THEME, SYMBOLS, PANEL_STYLES

Check failure on line 8 in cortex/ui/console.py

View workflow job for this annotation

GitHub Actions / lint

Ruff (I001)

cortex/ui/console.py:3:1: I001 Import block is un-sorted or un-formatted

Check failure on line 8 in cortex/ui/console.py

View workflow job for this annotation

GitHub Actions / Lint

Ruff (I001)

cortex/ui/console.py:3:1: I001 Import block is un-sorted or un-formatted
Comment on lines +3 to +8
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix import sorting and use modern type annotation syntax.

Static analysis flagged unsorted imports (I001) and outdated Optional usage (UP045). Per PEP 604 (Python 3.10+), prefer X | None over Optional[X].

Proposed fix
-from typing import Optional
-from rich.console import Console as RichConsole
-from rich.panel import Panel
-from rich.syntax import Syntax
-from rich.markdown import Markdown
-from .theme import CORTEX_THEME, SYMBOLS, PANEL_STYLES
+from rich.console import Console as RichConsole
+from rich.markdown import Markdown
+from rich.panel import Panel
+from rich.syntax import Syntax
+
+from .theme import CORTEX_THEME, PANEL_STYLES, SYMBOLS
🧰 Tools
🪛 GitHub Check: lint

[failure] 3-8: Ruff (I001)
cortex/ui/console.py:3:1: I001 Import block is un-sorted or un-formatted

🪛 GitHub Check: Lint

[failure] 3-8: Ruff (I001)
cortex/ui/console.py:3:1: I001 Import block is un-sorted or un-formatted

🤖 Prompt for AI Agents
In @cortex/ui/console.py around lines 3 - 8, Imports at the top of the module
are unsorted and you’re still importing typing.Optional; reorder imports (stdlib
first, third-party rich packages next, then local package imports) and update
any type annotations that use Optional[...] to use the PEP 604 union syntax
(e.g., Foo | None). Remove the now-unused Optional import from typing and ensure
all function signatures, variable annotations, and return types that used
Optional are updated to the X | None form (search for "Optional[" and replace
accordingly), keeping the existing symbols like CORTEX_THEME, SYMBOLS, and
PANEL_STYLES untouched.



class CortexConsole:
"""Themed console with semantic message methods."""

Check failure on line 13 in cortex/ui/console.py

View workflow job for this annotation

GitHub Actions / lint

Ruff (W293)

cortex/ui/console.py:13:1: W293 Blank line contains whitespace

Check failure on line 13 in cortex/ui/console.py

View workflow job for this annotation

GitHub Actions / Lint

Ruff (W293)

cortex/ui/console.py:13:1: W293 Blank line contains whitespace
_instance: Optional['CortexConsole'] = None

Check failure on line 15 in cortex/ui/console.py

View workflow job for this annotation

GitHub Actions / lint

Ruff (W293)

cortex/ui/console.py:15:1: W293 Blank line contains whitespace

Check failure on line 15 in cortex/ui/console.py

View workflow job for this annotation

GitHub Actions / Lint

Ruff (W293)

cortex/ui/console.py:15:1: W293 Blank line contains whitespace
def __new__(cls) -> 'CortexConsole':
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._console = RichConsole(theme=CORTEX_THEME)
return cls._instance

Check failure on line 21 in cortex/ui/console.py

View workflow job for this annotation

GitHub Actions / lint

Ruff (W293)

cortex/ui/console.py:21:1: W293 Blank line contains whitespace

Check failure on line 21 in cortex/ui/console.py

View workflow job for this annotation

GitHub Actions / Lint

Ruff (W293)

cortex/ui/console.py:21:1: W293 Blank line contains whitespace
@property
def rich(self) -> RichConsole:
return self._console

Check failure on line 25 in cortex/ui/console.py

View workflow job for this annotation

GitHub Actions / lint

Ruff (W293)

cortex/ui/console.py:25:1: W293 Blank line contains whitespace

Check failure on line 25 in cortex/ui/console.py

View workflow job for this annotation

GitHub Actions / Lint

Ruff (W293)

cortex/ui/console.py:25:1: W293 Blank line contains whitespace
def print(self, *args, **kwargs) -> None:
self._console.print(*args, **kwargs)
Comment on lines +11 to +27
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove trailing whitespace and add docstrings to public methods.

Multiple blank lines contain whitespace (W293 on lines 13, 15, 21, 25, 28, 31, 36). Additionally, public methods lack docstrings per coding guidelines.

The singleton pattern implemented via __new__ is not thread-safe. If CortexConsole() is called from multiple threads simultaneously during first initialization, multiple instances could be created.

Fix whitespace and update type annotation
 class CortexConsole:
     """Themed console with semantic message methods."""
-    
+
     _instance: Optional['CortexConsole'] = None
-    
+
     def __new__(cls) -> 'CortexConsole':
         if cls._instance is None:
             cls._instance = super().__new__(cls)
             cls._instance._console = RichConsole(theme=CORTEX_THEME)
         return cls._instance
-    
+
     @property
     def rich(self) -> RichConsole:
+        """Return the underlying Rich Console instance."""
         return self._console
🧰 Tools
🪛 GitHub Check: lint

[failure] 25-25: Ruff (W293)
cortex/ui/console.py:25:1: W293 Blank line contains whitespace


[failure] 21-21: Ruff (W293)
cortex/ui/console.py:21:1: W293 Blank line contains whitespace


[failure] 15-15: Ruff (W293)
cortex/ui/console.py:15:1: W293 Blank line contains whitespace


[failure] 13-13: Ruff (W293)
cortex/ui/console.py:13:1: W293 Blank line contains whitespace

🪛 GitHub Check: Lint

[failure] 25-25: Ruff (W293)
cortex/ui/console.py:25:1: W293 Blank line contains whitespace


[failure] 21-21: Ruff (W293)
cortex/ui/console.py:21:1: W293 Blank line contains whitespace


[failure] 15-15: Ruff (W293)
cortex/ui/console.py:15:1: W293 Blank line contains whitespace


[failure] 13-13: Ruff (W293)
cortex/ui/console.py:13:1: W293 Blank line contains whitespace

🤖 Prompt for AI Agents
In @cortex/ui/console.py around lines 11 - 27, Remove trailing whitespace and
extra blank-line whitespace throughout the CortexConsole class, add concise
docstrings to the public members __new__, rich (property) and print to meet
docstring guidelines, and make the singleton initialization in __new__
thread-safe (e.g., guard creation of _instance/_console with a threading.Lock or
use double-checked locking) so simultaneous CortexConsole() calls cannot create
multiple instances; reference the class name CortexConsole, the __new__ method,
the _instance and _console attributes, the rich property, and the print method
when applying these changes.


Check failure on line 28 in cortex/ui/console.py

View workflow job for this annotation

GitHub Actions / lint

Ruff (W293)

cortex/ui/console.py:28:1: W293 Blank line contains whitespace

Check failure on line 28 in cortex/ui/console.py

View workflow job for this annotation

GitHub Actions / Lint

Ruff (W293)

cortex/ui/console.py:28:1: W293 Blank line contains whitespace
def success(self, message: str) -> None:
self._console.print(f"[success_symbol]{SYMBOLS['success']}[/] [success]{message}[/]")

Check failure on line 31 in cortex/ui/console.py

View workflow job for this annotation

GitHub Actions / lint

Ruff (W293)

cortex/ui/console.py:31:1: W293 Blank line contains whitespace

Check failure on line 31 in cortex/ui/console.py

View workflow job for this annotation

GitHub Actions / Lint

Ruff (W293)

cortex/ui/console.py:31:1: W293 Blank line contains whitespace
def error(self, message: str, details: Optional[str] = None) -> None:

Check failure on line 32 in cortex/ui/console.py

View workflow job for this annotation

GitHub Actions / lint

Ruff (UP045)

cortex/ui/console.py:32:44: UP045 Use `X | None` for type annotations

Check failure on line 32 in cortex/ui/console.py

View workflow job for this annotation

GitHub Actions / Lint

Ruff (UP045)

cortex/ui/console.py:32:44: UP045 Use `X | None` for type annotations
self._console.print(f"[error_symbol]{SYMBOLS['error']}[/] [error]{message}[/]")
if details:
self._console.print(f" [secondary]{details}[/]")
Comment on lines +34 to +35
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The details message in the error method is indented with two spaces, but the main error message is not. This creates an inconsistent visual alignment. Consider aligning the details message with the main error message for better readability.

Suggested change
if details:
self._console.print(f" [secondary]{details}[/]")
self._console.print(f"[error_symbol]{SYMBOLS['error']}[/] [error]{message}[/]")
if details:
self._console.print(f"[secondary] {details}[/]")

Comment on lines +32 to +35
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Update type annotation syntax.

Use str | None instead of Optional[str] per UP045.

Proposed fix
-    def error(self, message: str, details: Optional[str] = None) -> None:
+    def error(self, message: str, details: str | None = None) -> None:
         self._console.print(f"[error_symbol]{SYMBOLS['error']}[/] [error]{message}[/]")
         if details:
             self._console.print(f"  [secondary]{details}[/]")
🧰 Tools
🪛 GitHub Check: lint

[failure] 32-32: Ruff (UP045)
cortex/ui/console.py:32:44: UP045 Use X | None for type annotations

🪛 GitHub Check: Lint

[failure] 32-32: Ruff (UP045)
cortex/ui/console.py:32:44: UP045 Use X | None for type annotations

🤖 Prompt for AI Agents
In @cortex/ui/console.py around lines 32 - 35, The method signature for error
uses legacy typing Optional[str]; update it to use the new union syntax (str |
None) in the error method definition (error(self, message: str, details: str |
None) -> None) and remove the now-unused Optional import from the module if
present; ensure no other references in cortex/ui/console.py still use Optional
so linting/type-checking passes.


Check failure on line 36 in cortex/ui/console.py

View workflow job for this annotation

GitHub Actions / lint

Ruff (W293)

cortex/ui/console.py:36:1: W293 Blank line contains whitespace

Check failure on line 36 in cortex/ui/console.py

View workflow job for this annotation

GitHub Actions / Lint

Ruff (W293)

cortex/ui/console.py:36:1: W293 Blank line contains whitespace
def warning(self, message: str) -> None:
self._console.print(f"[warning_symbol]{SYMBOLS['warning']}[/] [warning]{message}[/]")

def info(self, message: str) -> None:
self._console.print(f"[info_symbol]{SYMBOLS['info']}[/] [info]{message}[/]")

def command(self, cmd: str) -> None:
self._console.print(f"[command]{SYMBOLS['command']} {cmd}[/]")

def step(self, message: str, current: int, total: int) -> None:
self._console.print(f"[info_symbol]{SYMBOLS['step']}[/] [info]{message}[/] [secondary]({current}/{total})[/]")

def thinking(self, message: str) -> None:
self._console.print(f"[cortex]{SYMBOLS['thinking']} {message}[/]")

def secondary(self, message: str) -> None:
self._console.print(f" [secondary]{message}[/]")

def blank(self) -> None:
self._console.print()

def rule(self, title: str = "") -> None:
self._console.rule(title, style="panel_border")

def code(self, code: str, language: str = "bash") -> None:
syntax = Syntax(code, language, theme="monokai", line_numbers=False)
self._console.print(syntax)

def markdown(self, text: str) -> None:
self._console.print(Markdown(text))

def cortex_panel(self, content: str, title: str = "CORTEX") -> None:
panel = Panel(content, title=f"[cortex]─ {title} [/]", **PANEL_STYLES["cortex"])
self._console.print(panel)

def panel(self, content: str, title: str = "", style: str = "default") -> None:
panel_style = PANEL_STYLES.get(style, PANEL_STYLES["default"])
panel = Panel(content, title=f"─ {title} " if title else None, **panel_style)
self._console.print(panel)


console = CortexConsole()
97 changes: 97 additions & 0 deletions cortex/ui/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"""Cortex Errors - Structured error display with context and fixes."""

from typing import Optional, Dict, List
from dataclasses import dataclass
import re
from rich.panel import Panel
from .theme import SYMBOLS
from .console import console


@dataclass
class SuggestedFix:
command: str
description: Optional[str] = None


COMMON_ERRORS = {
"container_conflict": {"pattern": "container name .* already in use", "title": "Container Name Conflict", "fix_template": "docker rm -f {container_name}", "fix_description": "Remove the existing container"},
"port_in_use": {"pattern": "port .* already in use", "title": "Port Already In Use", "fix_template": "lsof -ti:{port} | xargs kill -9", "fix_description": "Kill the process using the port"},
"permission_denied": {"pattern": "permission denied", "title": "Permission Denied", "fix_template": "sudo {original_command}", "fix_description": "Run with elevated privileges"},
"not_found": {"pattern": "command not found|not found", "title": "Command Not Found", "fix_template": "apt install {package}", "fix_description": "Install the missing package"},
}
Comment on lines +17 to +22
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

not_found error type has unresolved placeholder.

The "not_found" entry uses {package} in the fix template, but auto_suggest_fix (lines 81-97) has no extraction logic for this error type. This will leave {package} unsubstituted in the suggested fix.

🔧 Option 1: Add extraction logic
             elif error_type == "permission_denied":
                 fix_cmd = fix_cmd.format(original_command=command)
+            elif error_type == "not_found":
+                # Try to extract package name from error
+                match = re.search(r"(\w+): command not found", error_lower)
+                if match:
+                    fix_cmd = fix_cmd.format(package=match.group(1))
+                else:
+                    fix_cmd = fix_cmd.format(package="<package>")
             return SuggestedFix(command=fix_cmd, description=info["fix_description"])
🔧 Option 2: Use generic placeholder in template
-    "not_found": {"pattern": "command not found|not found", "title": "Command Not Found", "fix_template": "apt install {package}", "fix_description": "Install the missing package"},
+    "not_found": {"pattern": "command not found|not found", "title": "Command Not Found", "fix_template": "apt install <package-name>", "fix_description": "Install the missing package"},
🤖 Prompt for AI Agents
In @cortex/ui/errors.py around lines 17 - 22, The "not_found" entry in
COMMON_ERRORS uses a {package} placeholder but auto_suggest_fix does not
populate "package", leaving the suggested fix unsubstituted; either change
COMMON_ERRORS["not_found"]["fix_template"] to a generic/safe placeholder such as
{original_command} or implement extraction logic inside auto_suggest_fix to
detect "command not found" errors (parse the missing command from the error
message) and set a "package" value before formatting the template—update the
branch in auto_suggest_fix that handles "not_found"/"not found" so it supplies
the "package" key (or switch the template) and ensure the formatted fix uses
only keys that are guaranteed to be present.



def show_error(title: str, message: str, context: Optional[Dict[str, str]] = None, suggested_fix: Optional[str] = None, fix_description: Optional[str] = None, actions: Optional[List[str]] = None) -> Optional[str]:
lines = [f"[error]{SYMBOLS['error']} FAILED:[/] [primary]{title}[/]", "", f" [error]Error:[/] {message}"]
if context:
lines.append("")
for key, value in context.items():
display_value = value if len(value) < 50 else value[:47] + "..."
lines.append(f" [secondary]{key}:[/] {display_value}")
Comment on lines +30 to +31
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The truncation logic for display_value in the show_error context (value[:47] + "...") might cut off critical information for the user. While brevity is good, important details could be lost. Consider providing an option to view the full context or truncating more intelligently, perhaps by word boundaries.


if suggested_fix:
fix_content = []
if fix_description:
fix_content.append(f"[muted]{fix_description}[/]")
fix_content.append("")
fix_content.append(f"[command]{suggested_fix}[/]")
console.print(Panel("\n".join(lines), border_style="error", padding=(1, 2)))
console.print(Panel("\n".join(fix_content), title="[success]─ SUGGESTED FIX [/]", border_style="success", padding=(0, 2)))
else:
console.print(Panel("\n".join(lines), border_style="error", padding=(1, 2)))

if actions:
action_text = [f"[highlight][{a[0].upper()}][/][muted]{a[1:]}[/]" for a in actions]
console.print(" " + " ".join(action_text))
console.print()
while True:
response = input(" > ").strip().lower()
for action in actions:
if response == action[0].lower() or response == action.lower():
return action
console.warning(f"Please enter one of: {', '.join(a[0] for a in actions)}")
Comment on lines +48 to +53
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The input() calls in show_error and show_conflict are blocking. While acceptable for simple CLI interactions, this can limit the responsiveness of the UI and make it harder to integrate with more complex event-driven systems or asynchronous operations in the future. For a rich UI, consider exploring non-blocking input mechanisms if the application's complexity grows.

return None


def show_conflict(title: str, description: str, options: List[Dict[str, str]]) -> str:
lines = [f"[warning]{SYMBOLS['warning']} CONFLICT DETECTED[/]", "", f"[primary]{description}[/]", "", "[muted]Options:[/]"]
for opt in options:
lines.append(f" [highlight][{opt['key']}][/] {opt['label']}")
if opt.get('description'):
lines.append(f" [secondary]{opt['description']}[/]")
console.print(Panel("\n".join(lines), title=f"[warning]─ {title} [/]", border_style="warning", padding=(1, 2)))
valid_keys = [opt['key'].lower() for opt in options]
while True:
response = input(f"\n {SYMBOLS['prompt']} What would you like to do? ").strip().lower()
if response in valid_keys:
return response
console.warning(f"Please enter one of: {', '.join(valid_keys)}")


def show_warning(title: str, message: str, details: Optional[List[str]] = None) -> None:
lines = [f"[warning]{SYMBOLS['warning']} {title}[/]", "", f"[primary]{message}[/]"]
if details:
lines.append("")
for detail in details:
lines.append(f" [secondary]• {detail}[/]")
console.print(Panel("\n".join(lines), border_style="warning", padding=(1, 2)))


def auto_suggest_fix(error_message: str, command: str = "") -> Optional[SuggestedFix]:
error_lower = error_message.lower()
for error_type, info in COMMON_ERRORS.items():
if re.search(info["pattern"], error_lower):
fix_cmd = info["fix_template"]
if error_type == "container_conflict":
match = re.search(r'name ["\']?/?(\w+)', error_lower)
if match:
fix_cmd = fix_cmd.format(container_name=match.group(1))
Comment on lines +86 to +89
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Container name regex may miss names with hyphens.

The regex r'name ["\']?/?(\w+)' uses \w+ which doesn't match hyphens. Docker container names commonly include hyphens (e.g., my-nginx-container).

🔧 Suggested fix
             if error_type == "container_conflict":
-                match = re.search(r'name ["\']?/?(\w+)', error_lower)
+                match = re.search(r'name ["\']?/?([\w-]+)', error_lower)
                 if match:
                     fix_cmd = fix_cmd.format(container_name=match.group(1))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if error_type == "container_conflict":
match = re.search(r'name ["\']?/?(\w+)', error_lower)
if match:
fix_cmd = fix_cmd.format(container_name=match.group(1))
if error_type == "container_conflict":
match = re.search(r'name ["\']?/?([\w-]+)', error_lower)
if match:
fix_cmd = fix_cmd.format(container_name=match.group(1))
🤖 Prompt for AI Agents
In @cortex/ui/errors.py around lines 86 - 89, The container name regex in the
container_conflict branch uses r'name ["\']?/?(\w+)' which fails to match
hyphenated names; update the pattern used in the error_type ==
"container_conflict" block (where match is assigned and fix_cmd is formatted) to
capture hyphens as well (e.g., use a character class like [\w-]+ or [-\w]+
instead of \w+) so match.group(1) will include names such as
"my-nginx-container" before calling fix_cmd.format(container_name=...).

elif error_type == "port_in_use":
match = re.search(r'port (\d+)', error_lower)
if match:
fix_cmd = fix_cmd.format(port=match.group(1))
elif error_type == "permission_denied":
fix_cmd = fix_cmd.format(original_command=command)
Comment on lines +94 to +95
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The auto_suggest_fix function currently handles container_name, port, and original_command placeholders. However, the not_found error's fix_template uses {package} which is not formatted, leading to an unformatted suggestion. Ensure all placeholders in COMMON_ERRORS fix_template strings are correctly handled and formatted.

Suggested change
elif error_type == "permission_denied":
fix_cmd = fix_cmd.format(original_command=command)
elif error_type == "permission_denied":
fix_cmd = fix_cmd.format(original_command=command)
elif error_type == "not_found":
# This requires identifying the missing package, which might be complex.
# For now, ensure the template is handled or provide a generic message.
pass # Placeholder for future logic to identify and format {package}
return SuggestedFix(command=fix_cmd, description=info["fix_description"])

return SuggestedFix(command=fix_cmd, description=info["fix_description"])
return None
103 changes: 103 additions & 0 deletions cortex/ui/explain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
"""Cortex Explain - Command explanation and breakdown."""

from typing import List, Tuple
from dataclasses import dataclass
import shlex
from rich.panel import Panel
from .console import console


@dataclass
class CommandPart:
text: str
explanation: str
is_flag: bool = False


COMMAND_EXPLANATIONS = {
"docker": {
"run": "Creates and starts a new container", "-d": "Runs in background (detached)", "--detach": "Runs in background",
"-v": "Mounts a volume (host:container)", "--volume": "Mounts a volume", "-p": "Maps a port (host:container)",
"--publish": "Maps a port", "--name": "Names the container", "--gpus": "Grants GPU access",
"--rm": "Auto-removes when exits", "-it": "Interactive with TTY", "-e": "Sets environment variable",
"pull": "Downloads an image", "stop": "Stops container", "rm": "Removes container", "-f": "Force operation",
},
"apt": {
"install": "Installs packages", "update": "Refreshes package index", "upgrade": "Upgrades all packages",
"remove": "Removes package", "purge": "Removes package + config", "-y": "Auto-yes to prompts",
},
"pip": {
"install": "Installs Python packages", "-e": "Editable/dev mode", "-r": "From requirements file",
"--upgrade": "Upgrades to latest", "-U": "Upgrades to latest", "--break-system-packages": "Allows system Python changes",
},
"git": {
"clone": "Downloads repository", "pull": "Fetches and merges", "push": "Uploads commits",
"add": "Stages files", "commit": "Records changes", "-m": "Commit message", "checkout": "Switches branches", "-b": "Creates new branch",
},
}


def parse_command(cmd: str) -> Tuple[str, List[str]]:
try:
parts = shlex.split(cmd)
except ValueError:
parts = cmd.split()
return (parts[0], parts[1:]) if parts else ("", [])


def get_explanation(base_cmd: str, part: str) -> str:
cmd_exp = COMMAND_EXPLANATIONS.get(base_cmd, {})
if part in cmd_exp:
return cmd_exp[part]
if part.lstrip('-') in cmd_exp:
return cmd_exp[part.lstrip('-')]
if part.startswith('-'):
return "Option flag"
if ':' in part:
return "Mapping (source:destination)"
if '/' in part and not part.startswith('/'):
return "Image or path reference"
return "Argument or value"


def explain_command(cmd: str, show_panel: bool = True) -> List[CommandPart]:
base_cmd, args = parse_command(cmd)
parts = [CommandPart(text=base_cmd, explanation=f"The {base_cmd} command")]

i = 0
while i < len(args):
arg = args[i]
exp = get_explanation(base_cmd, arg)
if arg.startswith('-') and i + 1 < len(args) and not args[i + 1].startswith('-'):
parts.append(CommandPart(text=f"{arg} {args[i+1]}", explanation=f"{exp}: {args[i+1]}", is_flag=True))
i += 2
else:
Comment on lines +71 to +74
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for parsing command arguments and flags in explain_command is a bit complex, especially with the i + 1 < len(args) and not args[i + 1].startswith('-') condition. This might not cover all variations of flag-value pairs (e.g., --flag=value or flags that don't take an immediate next argument). Consider a more robust parsing approach that explicitly handles different flag formats and their associated values.

parts.append(CommandPart(text=arg, explanation=exp, is_flag=arg.startswith('-')))
i += 1

if show_panel:
lines = [f"[command]{cmd}[/]", ""]
for j, part in enumerate(parts):
lines.append(f"[{'cortex' if j == 0 else 'info'}]{part.text}[/]")
lines.append(f"└─ [secondary]{part.explanation}[/]")
if j < len(parts) - 1:
lines.append("")
console.print(Panel("\n".join(lines), title="[cortex]─ EXPLANATION [/]", border_style="cortex", padding=(1, 2)))
console.print("\n [secondary]Press Enter to return to options[/]")
input()
Comment on lines +86 to +87
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to the errors.py module, the explain_command function uses a blocking input() call to pause the display. This can interrupt the user flow and might not be ideal for a highly interactive terminal UI. Consider alternatives for user acknowledgment that are less intrusive or can be integrated into a non-blocking event loop.

return parts


def explain_pipeline(cmd: str) -> None:
import re
parts = re.split(r'\s*(\||\&\&|\|\|)\s*', cmd)
lines = ["[muted]Pipeline breakdown:[/]", ""]
step = 1
for part in parts:
if part in ('|', '&&', '||'):
ops = {'|': 'pipes output to', '&&': 'then (if successful)', '||': 'or (if failed)'}
lines.append(f" [warning]{ops.get(part, part)}[/]")
elif part.strip():
lines.append(f"[info]{step}.[/] [command]{part.strip()}[/]")
step += 1
console.print(Panel("\n".join(lines), title="[cortex]─ PIPELINE [/]", border_style="cortex", padding=(1, 2)))
67 changes: 67 additions & 0 deletions cortex/ui/panels.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""Cortex Panels - AI response and status panels."""

from typing import Optional, List
from rich.panel import Panel
from rich.syntax import Syntax
from rich.text import Text
from rich.console import Group
from .console import console
from .theme import SYMBOLS


def ai_thinking(message: str, animated: bool = False) -> None:
content = f"[cortex]{SYMBOLS['thinking']} {message}[/]"
console.print(Panel(content, title="[cortex]─ CORTEX [/]", border_style="cortex", padding=(1, 2)))
Comment on lines +12 to +14
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Unused parameter: animated.

The animated parameter is accepted but never used. Either implement the animation feature or remove the parameter to avoid confusion.

🔧 Option 1: Remove unused parameter
-def ai_thinking(message: str, animated: bool = False) -> None:
+def ai_thinking(message: str) -> None:
🔧 Option 2: Add TODO for future implementation
 def ai_thinking(message: str, animated: bool = False) -> None:
+    """Display an AI thinking indicator.
+    
+    Args:
+        message: The thinking message to display.
+        animated: If True, show animated spinner. (Not yet implemented)
+    """
+    # TODO: Implement animated spinner using Rich Live
     content = f"[cortex]{SYMBOLS['thinking']} {message}[/]"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def ai_thinking(message: str, animated: bool = False) -> None:
content = f"[cortex]{SYMBOLS['thinking']} {message}[/]"
console.print(Panel(content, title="[cortex]─ CORTEX [/]", border_style="cortex", padding=(1, 2)))
def ai_thinking(message: str) -> None:
content = f"[cortex]{SYMBOLS['thinking']} {message}[/]"
console.print(Panel(content, title="[cortex]─ CORTEX [/]", border_style="cortex", padding=(1, 2)))
Suggested change
def ai_thinking(message: str, animated: bool = False) -> None:
content = f"[cortex]{SYMBOLS['thinking']} {message}[/]"
console.print(Panel(content, title="[cortex]─ CORTEX [/]", border_style="cortex", padding=(1, 2)))
def ai_thinking(message: str, animated: bool = False) -> None:
"""Display an AI thinking indicator.
Args:
message: The thinking message to display.
animated: If True, show animated spinner. (Not yet implemented)
"""
# TODO: Implement animated spinner using Rich Live
content = f"[cortex]{SYMBOLS['thinking']} {message}[/]"
console.print(Panel(content, title="[cortex]─ CORTEX [/]", border_style="cortex", padding=(1, 2)))
🤖 Prompt for AI Agents
In @cortex/ui/panels.py around lines 12 - 14, The ai_thinking function declares
an unused parameter animated; remove the animated parameter from ai_thinking's
signature and any callers, or if you prefer to keep the API, document it and add
a TODO and a brief comment inside ai_thinking noting planned animation support;
reference the ai_thinking function and SYMBOLS['thinking']/console.print call so
you update the signature and any places that call ai_thinking accordingly.



def ai_response(message: str, title: str = "CORTEX", show_commands: Optional[List[str]] = None, show_code: Optional[str] = None, code_language: str = "bash") -> None:
content_parts = [Text(message)]
if show_commands:
content_parts.append(Text())
content_parts.append(Text("Commands to execute:", style="muted"))
for cmd in show_commands:
content_parts.append(Text(f" {cmd}", style="command"))
if show_code:
content_parts.append(Text())
content_parts.append(Syntax(show_code, code_language, theme="monokai", line_numbers=False))
content = Group(*content_parts) if len(content_parts) > 1 else content_parts[0]
console.print(Panel(content, title=f"[cortex]─ {title} [/]", border_style="cortex", padding=(1, 2)))


def status_panel(title: str, items: List[str], style: str = "default") -> None:
content = "\n".join(f" {item}" for item in items)
border = {"default": "panel_border", "success": "success", "warning": "warning", "error": "error"}.get(style, "panel_border")
console.print(Panel(content, title=f"─ {title} ", border_style=border, padding=(1, 2)))


def code_panel(code: str, language: str = "bash", title: str = "Code") -> None:
console.print(Panel(Syntax(code, language, theme="monokai", line_numbers=True), title=f"─ {title} ", border_style="panel_border", padding=(0, 1)))


def diff_panel(additions: List[str], deletions: List[str], title: str = "Changes") -> None:
lines = [f"[error]- {line}[/]" for line in deletions] + [f"[success]+ {line}[/]" for line in additions]
console.print(Panel("\n".join(lines), title=f"─ {title} ", border_style="panel_border", padding=(0, 2)))


def summary_panel(title: str, summary: str, details: Optional[dict] = None) -> None:
lines = [summary]
if details:
lines.append("")
for key, value in details.items():
lines.append(f"[muted]{key}:[/] {value}")
console.print(Panel("\n".join(lines), title=f"[cortex]─ {title} [/]", border_style="cortex", padding=(1, 2)))


def welcome_banner() -> None:
banner = """[cortex] ____ _
/ ___|___ _ __| |_ _____ __
| | / _ \\| '__| __/ _ \\ \\/ /
| |__| (_) | | | || __/> <
\\____\\___/|_| \\__\\___/_/\\_\\[/]

[muted]The AI Layer for Linux[/]"""
console.print(banner)


def help_footer() -> None:
console.print("\n [secondary]esc[/] [muted]Cancel[/] · [secondary]tab[/] [muted]Add instructions[/] · [secondary]?[/] [muted]Help[/]")
Loading
Loading