Skip to content

Conversation

@Zafeeruddin
Copy link

@Zafeeruddin Zafeeruddin commented Dec 18, 2025

Related Issue

Closes cortexlinux/cortex-pro#1

Summary

This PR implements Role-Based Prompts for Cortex, allowing specialized AI personas for different use cases like security auditing, DevOps, data science, and system administration.

What's New

Built-in Roles (5 roles included):

Role Focus
default General-purpose package installation
security Hardening, CVE awareness, minimal attack surface, firewall rules
devops CI/CD, containers, automation, observability
datascience GPU/CUDA setup, ML frameworks, Jupyter, reproducibility
sysadmin Monitoring, backups, logging, system maintenance

CLI Integration:

Use roles during installation

cortex --role security install nginx
cortex -r devops install docker

Manage roles

cortex role list # List all roles
cortex role show security # View role details
cortex role create my-role --from security # Create custom role
cortex role delete my-role # Delete custom roleCustom Roles: Users can create their own roles stored in ~/.cortex/roles/*.yaml

Files Changed

File Change
cortex/role_manager.py NEW - Core role management module
cortex/roles/*.yaml NEW - 5 built-in role definitions
cortex/cli.py Added --role flag and role subcommand
cortex/llm/interpreter.py Integrated role-specific prompt injection
tests/test_role_manager.py NEW - 26 unit tests

Testing

  • ✅ 26/26 new role manager tests passing
  • ✅ All role CLI commands tested (list, show, create, delete)
  • ✅ Error handling verified (non-existent roles, protected built-in deletion)
  • ✅ Role prompt injection verified via verbose mode

Checklist

  • Tests pass (pytest tests/test_role_manager.py)
  • PR title format: [#270] Feat: Custom AI Roles
  • MVP label added if closing MVP issue

AI Disclosure

Note: This PR documentation was written with AI assistance. The implementation has been extensively tested both manually and via automated unit tests.

Summary by CodeRabbit

  • New Features

    • Role management via CLI: list, show, create, delete roles; CLI accepts a role argument with a sensible default
    • Built-in roles added: default, security, devops, datascience, sysadmin
    • Role-aware prompt customization in the interpreter; role guidance and priorities applied
    • Create custom roles from templates, with validation and safe persistence
  • Tests

    • Comprehensive test suite covering role lifecycle, validation, loading, precedence, caching, and safeguards
  • Documentation

    • Enhanced CLI help and role usage examples

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 18, 2025

Walkthrough

Adds a role/persona system: new RoleManager module, five built-in role YAMLs, CLI role subcommands and wiring, and CommandInterpreter now accepts and applies a role's prompt additions during initialization.

Changes

Cohort / File(s) Summary
CLI Integration
cortex/cli.py
Add role parameter to CortexCLI (stored as self.role), import RoleManager and exceptions, add role subcommands (list, show, create, delete), implement role_list, role_show, role_create, role_delete methods, and pass role=self.role into CommandInterpreter during install and main wiring.
Interpreter Integration
cortex/llm/interpreter.py
CommandInterpreter.__init__ gains role: str = "default"; added _load_role(role_name) helper with resilient error handling; load role before cache/model selection; augment _get_system_prompt to include role name, description, prompt_additions, and priorities.
Role Manager Module
cortex/role_manager.py
New module: Role dataclass, exceptions (RoleError, RoleNotFoundError, RoleValidationError), RoleManager with load/list/create/delete/exists/clear_cache, YAML persistence with atomic writes, validation, caching, builtin/custom precedence, and BUILTIN_ROLES constant.
Built-in Roles Package
cortex/roles/__init__.py, cortex/roles/*.yaml
New package and five built-in YAMLs: default.yaml, security.yaml, devops.yaml, datascience.yaml, sysadmin.yaml defining name, description, prompt_additions, and priorities.
Role Manager Tests
tests/test_role_manager.py
New comprehensive tests for Role and RoleManager: creation, to_dict/from_dict, validation, loading (builtin/custom), precedence, create-from-template, delete protections, caching and clear_cache, and error handling using tmp dirs and pytest.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI as CortexCLI
    participant CI as CommandInterpreter
    participant RM as RoleManager
    participant Builtin as Built-in Roles (YAML)
    participant Custom as Custom Roles (YAML)

    User->>CLI: run `cortex --role security install ...`
    CLI->>CLI: set self.role = "security"
    CLI->>CI: instantiate CommandInterpreter(..., role="security")
    CI->>RM: RM.get_role("security")
    RM->>Custom: check ~/.cortex/roles/security.yaml
    alt custom exists
        Custom-->>RM: return Role (custom)
    else custom missing
        RM->>Builtin: read cortex/roles/security.yaml
        Builtin-->>RM: return Role (builtin)
    end
    RM-->>CI: Role object (cached)
    CI->>CI: build system prompt including role.prompt_additions/priorities
    CI-->>CLI: interpreter ready (role-aware)
    CLI->>User: execute command with role-enhanced prompt
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Focus review on:
    • cortex/role_manager.py: validation rules, atomic write logic, builtin vs custom precedence, and cache invalidation.
    • cortex/llm/interpreter.py: ordering of role load vs. cache/model selection and prompt formatting.
    • cortex/cli.py: CLI parsing/dispatch and error messaging for role commands.
    • cortex/roles/*.yaml: schema consistency across role files and encoding.

Possibly related PRs

Suggested reviewers

  • mikejmorgan-ai

Poem

🐰 I nibble YAML, hop and write,
Roles for data, ops, and byte.
Prompts aligned, each task in sight,
Tail twitch, quick hops—everything's right! ✨

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Feat/270 custom ai roles' is related to the main changeset, which implements custom AI roles functionality, but lacks clarity and proper formatting; it should follow conventional commit format.
Description check ✅ Passed The PR description is comprehensive and well-structured, covering objectives, files changed, testing status, and checklist items. It aligns with the repository template and provides clear context for the implementation.
Linked Issues check ✅ Passed The PR successfully implements all core requirements from issue cortexlinux/cortex-pro#1: built-in roles (default, security, devops, datascience, sysadmin), CLI integration with --role flag and role subcommand (list/show/create/delete), custom role management at ~/.cortex/roles/*.yaml, and role-aware prompt injection.
Out of Scope Changes check ✅ Passed All code changes are directly aligned with issue cortexlinux/cortex-pro#1 requirements; no unrelated or out-of-scope modifications were introduced. Changes are focused on role management, CLI integration, and prompt customization.
Docstring Coverage ✅ Passed Docstring coverage is 91.07% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (5)
tests/test_role_manager.py (1)

406-413: Weak assertion in default role test.

Line 411 asserts len(role.priorities) >= 0, which is always True for any list. Consider either checking for specific expected priorities or verifying the list is non-empty if that's the requirement.

🔎 Suggested improvement
 def test_default_role_priorities(self):
     """Test that default role has expected priorities"""
     manager = RoleManager()
     try:
         role = manager.get_role("default")
-        assert "reliability" in role.priorities or len(role.priorities) >= 0
+        # Default role should have meaningful priorities defined
+        assert isinstance(role.priorities, list)
+        assert len(role.priorities) > 0
     except RoleNotFoundError:
         pass  # Skip if not in correct directory
cortex/cli.py (2)

555-581: Consider adding return type hint to the docstring.

The method implementation is clean and provides a well-formatted table output. Per coding guidelines requiring type hints, consider adding the return type annotation:

-    def role_list(self):
+    def role_list(self) -> int:

659-663: Handle editor subprocess errors for better user feedback.

subprocess.run without check=True silently ignores editor failures. If the editor fails to launch or exits with an error, the user won't know. Consider adding error handling:

🔎 Proposed fix
         try:
-            subprocess.run([editor, str(role_path)])
+            result = subprocess.run([editor, str(role_path)])
+            if result.returncode != 0:
+                cx_print(f"Editor exited with code {result.returncode}", "warning")
         except FileNotFoundError:
             cx_print(f"Could not open editor. Edit the file manually: {role_path}", "warning")
cortex/role_manager.py (1)

123-145: Consider validating priorities type.

The validation doesn't verify that priorities is a list. If a YAML file has priorities: "single-value" instead of a list, Role.from_dict() will assign the string directly, which could cause issues downstream.

🔎 Proposed fix
         if not role.prompt_additions:
             raise RoleValidationError("Role must have prompt_additions")
+
+        if not isinstance(role.priorities, list):
+            raise RoleValidationError("Role priorities must be a list")
cortex/llm/interpreter.py (1)

97-98: Consider using the logging framework.

The codebase consistently uses Python's logging module across 15+ files. Printing directly to stderr at line 98 is inconsistent with this established pattern. Replace with:

import logging
logger = logging.getLogger(__name__)
# ...
logger.warning(f"Could not load role '{role_name}': {e}")
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2041fcd and 58adc31.

📒 Files selected for processing (10)
  • cortex/cli.py (9 hunks)
  • cortex/llm/interpreter.py (6 hunks)
  • cortex/role_manager.py (1 hunks)
  • cortex/roles/__init__.py (1 hunks)
  • cortex/roles/datascience.yaml (1 hunks)
  • cortex/roles/default.yaml (1 hunks)
  • cortex/roles/devops.yaml (1 hunks)
  • cortex/roles/security.yaml (1 hunks)
  • cortex/roles/sysadmin.yaml (1 hunks)
  • tests/test_role_manager.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Follow PEP 8 style guide
Type hints required in Python code
Docstrings required for all public APIs

Files:

  • cortex/roles/__init__.py
  • cortex/llm/interpreter.py
  • cortex/cli.py
  • tests/test_role_manager.py
  • cortex/role_manager.py
{setup.py,setup.cfg,pyproject.toml,**/__init__.py}

📄 CodeRabbit inference engine (AGENTS.md)

Use Python 3.10 or higher as the minimum supported version

Files:

  • cortex/roles/__init__.py
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Maintain >80% test coverage for pull requests

Files:

  • tests/test_role_manager.py
🧬 Code graph analysis (3)
cortex/llm/interpreter.py (1)
cortex/role_manager.py (3)
  • RoleManager (67-374)
  • RoleNotFoundError (24-27)
  • get_role (147-180)
cortex/cli.py (1)
cortex/role_manager.py (9)
  • RoleManager (67-374)
  • RoleError (18-21)
  • RoleNotFoundError (24-27)
  • list_roles (182-231)
  • get_role (147-180)
  • role_exists (358-370)
  • create_role (233-285)
  • get_role_template (339-356)
  • delete_role (309-337)
tests/test_role_manager.py (1)
cortex/role_manager.py (14)
  • Role (37-64)
  • RoleError (18-21)
  • RoleManager (67-374)
  • RoleNotFoundError (24-27)
  • RoleValidationError (30-33)
  • _validate_role (123-145)
  • _load_role_from_file (93-121)
  • get_role (147-180)
  • list_roles (182-231)
  • create_role (233-285)
  • delete_role (309-337)
  • role_exists (358-370)
  • get_role_template (339-356)
  • clear_cache (372-374)
🔇 Additional comments (35)
cortex/roles/__init__.py (1)

1-3: LGTM!

The package initializer is minimal and well-documented. The comments clearly explain the role loading behavior.

cortex/roles/default.yaml (1)

1-14: LGTM!

The default role definition is well-structured with clear, general-purpose guidelines. The priorities (reliability, simplicity, compatibility) align well with a default configuration.

cortex/roles/security.yaml (1)

1-22: LGTM!

The security role provides comprehensive security-focused guidelines covering hardening, auditing, and secure configuration. The priorities appropriately emphasize security and compliance concerns.

cortex/roles/devops.yaml (1)

1-23: LGTM!

The devops role provides excellent guidelines for modern DevOps practices, covering containerization, orchestration, and observability. The priorities appropriately emphasize automation and scalability.

cortex/roles/datascience.yaml (1)

1-23: LGTM!

The datascience role provides comprehensive guidelines for ML/AI and scientific computing, with appropriate focus on GPU optimization, reproducibility, and performance. The priorities align well with data science requirements.

cortex/roles/sysadmin.yaml (1)

1-26: LGTM!

The sysadmin role provides comprehensive system administration guidelines covering monitoring, maintenance, and disaster recovery. The priorities appropriately emphasize stability and backup.

tests/test_role_manager.py (5)

1-20: LGTM!

Imports are well-organized (standard library, third-party, local) and the module docstring is present.


21-74: LGTM!

The Role dataclass tests are comprehensive, covering creation, serialization, deserialization, and default handling. All tests have clear docstrings.


105-345: LGTM!

The RoleManager tests provide excellent coverage of validation, loading, precedence rules, CRUD operations, and utility methods. The test structure is clear with well-named methods and comprehensive docstrings.


347-387: LGTM!

The caching tests properly verify that roles are cached after first load and that clear_cache() invalidates the cache correctly by checking object identity.


389-424: LGTM!

The built-in role tests appropriately handle missing roles (when running from different directories) and verify role content matches expectations.

cortex/cli.py (9)

21-23: LGTM!

Clean import of RoleManager and related exceptions. The exception hierarchy supports proper error handling in the CLI methods.


37-43: LGTM!

The role parameter is properly typed and stored. The default value "default" aligns with the built-in role names in RoleManager.BUILTIN_ROLES.


200-211: LGTM!

Good integration of the role parameter into the install workflow. The debug message at line 200 aids troubleshooting, and passing role=self.role to CommandInterpreter ensures role-specific prompts are applied.


583-602: LGTM!

Good exception handling for RoleNotFoundError with a user-friendly error message. The role details are displayed clearly using Rich formatting.


666-676: LGTM!

Clean delegation to RoleManager.delete_role() with proper error handling. The RoleError catch covers both built-in role protection and not-found scenarios.


708-714: LGTM!

Clear examples demonstrating both the --role flag for installation and the role subcommand for management. This helps users discover the feature.


747-752: LGTM!

Good choice making --role a global flag available to all commands. The help text clearly lists available built-in roles.


818-838: LGTM!

Well-structured subparser for role commands. The --from argument using dest="from_template" correctly avoids the Python keyword conflict.


875-891: LGTM!

Clean dispatch logic with appropriate fallback to role_list() when no action is specified. The use of getattr(args, "from_template", None) safely handles the optional argument.

cortex/role_manager.py (11)

1-15: LGTM!

Good module-level docstring explaining the purpose of the role management system. The imports are appropriate for YAML parsing and dataclass usage.


18-33: LGTM!

Clean exception hierarchy with specific exceptions for different error scenarios. This enables targeted error handling in the CLI layer.


36-64: LGTM!

Good use of dataclass with proper type hints. The use of field(default_factory=list) correctly avoids the mutable default argument pitfall. The to_dict() method appropriately excludes is_builtin since it's metadata not meant for persistence.


81-91: LGTM!

The directory initialization with mkdir(parents=True, exist_ok=True) is idempotent and safe. The cache type hint dict[str, Role] provides good documentation.


93-121: LGTM!

Proper use of yaml.safe_load() for security. The error handling distinguishes between YAML parsing errors and file access errors, providing appropriate exception types for each.


147-180: LGTM!

Clean implementation with cache-first lookup. The custom role precedence over built-in roles is well-documented and correctly implemented.


182-230: LGTM!

The precedence handling with seen_names correctly ensures custom roles override built-in roles. Silently skipping invalid role files is appropriate for a listing operation to avoid breaking the entire list.


233-285: LGTM!

Good implementation with template copying support. The defensive priorities.copy() at line 265 prevents shared state issues. Note that the method allows overwriting existing roles by design—the CLI layer handles the existence check before calling this method.


287-307: LGTM!

Excellent atomic write pattern using a temp file and Path.replace(). The cleanup of the temp file in the exception handler prevents orphaned files.


309-337: LGTM!

Good protection against deleting built-in roles. The logic correctly distinguishes between: (1) built-in with no custom override (error), (2) custom override exists (delete allowed), and (3) pure custom role (delete allowed).


339-374: LGTM!

The template provides clear placeholders for users creating custom roles. The role_exists() method correctly checks both custom and built-in directories.

cortex/llm/interpreter.py (4)

21-22: LGTM!

Documentation clearly describes the new role-based customization feature.


32-32: LGTM!

The role parameter is properly added with a sensible default, well-documented, and stored appropriately. The default value ensures backward compatibility.

Also applies to: 42-42, 47-47


49-51: Good initialization order.

Loading the role before cache initialization is correct since the role affects the system prompt, which is used in cache keys (line 278). The initial None assignment provides a safe fallback state.


120-158: LGTM!

The system prompt construction properly integrates role-specific additions. The defensive getattr check on line 145 is good practice even though _role is always initialized. The string formatting clearly separates the base prompt from role-specific content.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
tests/test_role_manager.py (1)

98-104: Fix tautological and incorrect assertions.

Two issues with this test:

  1. Line 101's assertion is tautological (always True), as flagged in a previous review.
  2. Line 103 checks manager.custom_roles_dir.parent.exists() instead of manager.custom_roles_dir.exists(). Since RoleManager.__init__ calls mkdir(parents=True, exist_ok=True) on custom_roles_dir (per cortex/role_manager.py lines 85-86), the test should verify that the directory itself was created.
🔎 Proposed fix
 def test_manager_initialization(self):
     """Test RoleManager initialization"""
     manager = RoleManager()
-    assert manager.builtin_roles_dir.exists() or not manager.builtin_roles_dir.exists()
+    assert isinstance(manager.builtin_roles_dir, Path)
     # Custom roles dir should be created
-    assert manager.custom_roles_dir.parent.exists()
+    assert manager.custom_roles_dir.exists()
🧹 Nitpick comments (4)
tests/test_role_manager.py (4)

7-7: Unused import: mock.

The mock module is imported but never used in this test file. Consider removing it to keep imports clean.

🔎 Proposed fix
 import tempfile
 from pathlib import Path
-from unittest import mock
 
 import pytest

144-180: Consider adding test for empty YAML file.

The file loading tests cover valid YAML and invalid syntax. However, RoleManager._load_role_from_file (lines 111-112 in role_manager.py) also handles empty YAML files with a specific error. Consider adding a test for this case:

def test_load_role_empty_file(self, temp_dirs):
    """Test loading a role from an empty YAML file"""
    custom_dir, builtin_dir = temp_dirs
    manager = RoleManager()
    manager.custom_roles_dir = custom_dir
    manager.builtin_roles_dir = builtin_dir
    
    role_file = builtin_dir / "empty.yaml"
    role_file.touch()  # Create empty file
    
    with pytest.raises(RoleValidationError, match="Empty role file"):
        manager._load_role_from_file(role_file)

215-243: Consider testing override scenario in list_roles.

The current test verifies basic listing functionality. Consider adding a test case where a custom role overrides a built-in role to verify that:

  1. The role appears only once in the list
  2. The is_custom_override flag is set correctly
  3. The is_builtin flag is False for the override

This would provide more comprehensive coverage of list_roles behavior (lines 181-230 in role_manager.py).


338-346: Consider validating template YAML syntax.

The test verifies that the template contains expected keys by string matching. For more robust testing, consider parsing the template as YAML to ensure it's syntactically valid:

def test_get_role_template(self, manager_with_temp_dirs):
    """Test getting a role template"""
    manager = manager_with_temp_dirs
    template = manager.get_role_template()
    
    # Verify it's valid YAML
    data = yaml.safe_load(template)
    assert "name" in data
    assert "description" in data
    assert "prompt_additions" in data
    assert "priorities" in data
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 58adc31 and 1d28da7.

📒 Files selected for processing (1)
  • tests/test_role_manager.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Follow PEP 8 style guide
Type hints required in Python code
Docstrings required for all public APIs

Files:

  • tests/test_role_manager.py
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Maintain >80% test coverage for pull requests

Files:

  • tests/test_role_manager.py
🧬 Code graph analysis (1)
tests/test_role_manager.py (1)
cortex/role_manager.py (14)
  • Role (37-64)
  • RoleError (18-21)
  • RoleManager (67-374)
  • RoleNotFoundError (24-27)
  • RoleValidationError (30-33)
  • _validate_role (123-145)
  • _load_role_from_file (93-121)
  • get_role (147-180)
  • list_roles (182-231)
  • create_role (233-285)
  • delete_role (309-337)
  • role_exists (358-370)
  • get_role_template (339-356)
  • clear_cache (372-374)
🔇 Additional comments (8)
tests/test_role_manager.py (8)

21-74: LGTM: Comprehensive Role dataclass tests.

The Role dataclass tests cover creation, serialization (to_dict), deserialization (from_dict), and default handling. The verification that is_builtin is excluded from serialization (line 52) correctly aligns with the implementation.


79-96: LGTM: Clean fixture setup for test isolation.

The fixtures properly use temporary directories to isolate tests. Note that manager_with_temp_dirs patches directory paths after initialization, which bypasses the mkdir(parents=True, exist_ok=True) call in RoleManager.__init__ (line 85 in role_manager.py). This is acceptable for testing since the fixture manually creates these directories.


105-143: LGTM: Thorough validation test coverage.

The validation tests comprehensively cover all validation rules from RoleManager._validate_role: missing name, description, prompt_additions, and invalid name format. Testing the private _validate_role method directly is appropriate for unit testing.


181-214: LGTM: Correct precedence and error handling tests.

The tests properly verify that custom roles override built-in roles (line 206 checks custom description is returned) and that missing roles raise RoleNotFoundError. This aligns with the get_role implementation's precedence logic.


244-282: LGTM: Create role tests cover basic and template scenarios.

Both tests correctly verify role creation. The template test (lines 256-282) properly validates that empty description and prompt_additions are inherited from the template, matching the implementation logic (lines 248-253 in role_manager.py).


283-318: LGTM: Delete tests properly verify deletion and built-in protection.

The tests correctly verify that custom roles can be deleted and built-in roles are protected from deletion. The error message check on line 316 matches the implementation (line 325 in role_manager.py).


319-337: LGTM: Straightforward existence check test.

The test correctly verifies both positive and negative cases for role_exists, matching the implementation that checks both custom and built-in paths.


347-387: LGTM: Cache tests properly use identity checks.

The caching tests correctly use identity checks (is/is not) to verify that cached roles return the same object instance (line 366) and that cache clearing forces reloading (line 386). This properly validates the caching mechanism.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d28da7 and c1e1d6f.

📒 Files selected for processing (2)
  • cortex/llm/interpreter.py (6 hunks)
  • tests/test_role_manager.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/test_role_manager.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Follow PEP 8 style guide
Type hints required in Python code
Docstrings required for all public APIs

Files:

  • cortex/llm/interpreter.py
🧬 Code graph analysis (1)
cortex/llm/interpreter.py (1)
cortex/role_manager.py (3)
  • RoleManager (67-374)
  • RoleNotFoundError (24-27)
  • get_role (147-180)
🔇 Additional comments (2)
cortex/llm/interpreter.py (2)

76-104: Docstring accurately reflects exception handling behavior.

The docstring has been properly updated to reflect that ValueError is raised only when the role is not found, while other exceptions result in a warning with graceful fallback. This addresses the previous review concern.


125-164: LGTM: Clean role-aware prompt construction.

The implementation correctly incorporates role-specific additions into the system prompt with proper defensive checks and clear formatting.

self.role_name = role

# Load role configuration
self._role = None
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Add type annotation for the _role attribute.

The _role attribute should have a type annotation per coding guidelines. Import Role under the TYPE_CHECKING block to avoid circular imports, then annotate as Optional[Role].

🔎 Suggested changes

At the top of the file, add the Role import to the TYPE_CHECKING block:

 if TYPE_CHECKING:
     from cortex.semantic_cache import SemanticCache
+    from cortex.role_manager import Role

Then annotate the attribute:

-        self._role = None
+        self._role: Optional[Role] = None

As per coding guidelines: "Type hints required in Python code"

📝 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
self._role = None
self._role: Optional[Role] = None
Suggested change
self._role = None
if TYPE_CHECKING:
from cortex.semantic_cache import SemanticCache
from cortex.role_manager import Role
🤖 Prompt for AI Agents
In cortex/llm/interpreter.py around line 50, annotate the `_role` attribute as
Optional[Role] and add the necessary TYPE_CHECKING import: import Role under an
existing TYPE_CHECKING block (from cortex.llm.types or the correct module) and
ensure Optional is imported from typing; then change the assignment to include
the type annotation (self._role: Optional[Role] = None) to satisfy the
type-hinting guideline and avoid circular imports by using TYPE_CHECKING.

@lu11y0
Copy link
Contributor

lu11y0 commented Dec 19, 2025

Hi @Zafeeruddin - please only work on issues that are assigned to you, and make sure to comment when you start working on an issue so others know. Issue cortexlinux/cortex-pro#1 is assigned to @pavanimanchala53 and is already in progress, which caused duplicate work. Take a look at https://github.com/cortexlinux/cortex/blob/main/Contributing.md

@sonarqubecloud
Copy link

@Zafeeruddin
Copy link
Author

@mikejmorgan-ai Please have a look

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c1e1d6f and d9ffff6.

📒 Files selected for processing (1)
  • cortex/cli.py (9 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Follow PEP 8 style guide
Type hints required in Python code
Docstrings required for all public APIs

Files:

  • cortex/cli.py
🔇 Additional comments (6)
cortex/cli.py (6)

20-22: LGTM! Clean import of role management components.

The import statement is well-organized with a descriptive comment.


37-37: LGTM! Role parameter integration is clean.

The role parameter is properly typed and stored with a sensible default.

Also applies to: 43-43


319-319: LGTM! Role integration into installation flow is correct.

Debug logging and parameter passing are implemented consistently with the existing pattern.

Also applies to: 330-330


820-820: LGTM! Help text additions are clear and helpful.

The role command documentation and examples follow the existing pattern and provide good user guidance.

Also applies to: 829-834


867-872: LGTM! Argument parser setup is well-structured.

The global --role flag and role subcommands are properly configured with appropriate defaults and help text. Good use of dest="from_template" to avoid keyword conflicts.

Also applies to: 949-968


976-976: LGTM! Command dispatch logic is robust.

The role parameter is correctly threaded through, and the command dispatch handles all role subcommands with safe defaults. Good defensive use of getattr for optional arguments.

Also applies to: 1008-1024

Comment on lines +674 to +700
def role_list(self):
"""List all available roles"""
from rich.table import Table

manager = RoleManager()
roles = manager.list_roles()

cx_header("Available Roles")

table = Table(show_header=True, header_style="bold cyan", box=None)
table.add_column("Role", style="green")
table.add_column("Description")
table.add_column("Type", style="dim")

for role in roles:
role_type = "built-in"
if role["is_custom_override"]:
role_type = "custom (override)"
elif not role["is_builtin"]:
role_type = "custom"

table.add_row(role["name"], role["description"], role_type)

console.print(table)
console.print()
cx_print("Use [bold]cortex --role <name> install <pkg>[/bold] to use a role", "info")
return 0
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Add return type hint and enhance docstring.

The method is missing a return type hint, and the docstring could be more descriptive.

🔎 Proposed enhancement
-    def role_list(self):
-        """List all available roles"""
+    def role_list(self) -> int:
+        """List all available roles (built-in and custom).
+        
+        Returns:
+            int: Exit code (0 for success)
+        """
         from rich.table import Table

As per coding guidelines, type hints are required in Python code.

📝 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 role_list(self):
"""List all available roles"""
from rich.table import Table
manager = RoleManager()
roles = manager.list_roles()
cx_header("Available Roles")
table = Table(show_header=True, header_style="bold cyan", box=None)
table.add_column("Role", style="green")
table.add_column("Description")
table.add_column("Type", style="dim")
for role in roles:
role_type = "built-in"
if role["is_custom_override"]:
role_type = "custom (override)"
elif not role["is_builtin"]:
role_type = "custom"
table.add_row(role["name"], role["description"], role_type)
console.print(table)
console.print()
cx_print("Use [bold]cortex --role <name> install <pkg>[/bold] to use a role", "info")
return 0
def role_list(self) -> int:
"""List all available roles (built-in and custom).
Returns:
int: Exit code (0 for success)
"""
from rich.table import Table
manager = RoleManager()
roles = manager.list_roles()
cx_header("Available Roles")
table = Table(show_header=True, header_style="bold cyan", box=None)
table.add_column("Role", style="green")
table.add_column("Description")
table.add_column("Type", style="dim")
for role in roles:
role_type = "built-in"
if role["is_custom_override"]:
role_type = "custom (override)"
elif not role["is_builtin"]:
role_type = "custom"
table.add_row(role["name"], role["description"], role_type)
console.print(table)
console.print()
cx_print("Use [bold]cortex --role <name> install <pkg>[/bold] to use a role", "info")
return 0
🤖 Prompt for AI Agents
In cortex/cli.py around lines 674 to 700, the role_list method lacks a return
type hint and has a minimal docstring; update the docstring to briefly describe
what the method does and its return value, and add an explicit return type hint
(-> int) to the method signature. Ensure the docstring follows project style
(one-line summary plus optional short detail) and mentions that it prints roles
to console and returns 0 on success.

Comment on lines +702 to +721
def role_show(self, name: str):
"""Show details of a specific role"""
try:
manager = RoleManager()
role = manager.get_role(name)

cx_header(f"Role: {role.name}")
console.print(f"[bold]Description:[/bold] {role.description}")
console.print(f"[bold]Type:[/bold] {'Built-in' if role.is_builtin else 'Custom'}")

if role.priorities:
console.print(f"[bold]Priorities:[/bold] {', '.join(role.priorities)}")

console.print("\n[bold]Prompt Additions:[/bold]")
console.print(f"[dim]{role.prompt_additions}[/dim]")

return 0
except RoleNotFoundError:
self._print_error(f"Role '{name}' not found")
return 1
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Add return type hint and enhance docstring.

The method lacks a return type hint, and the docstring should document parameters and return values.

🔎 Proposed enhancement
-    def role_show(self, name: str):
-        """Show details of a specific role"""
+    def role_show(self, name: str) -> int:
+        """Show details of a specific role.
+        
+        Args:
+            name: The role name to display
+            
+        Returns:
+            int: Exit code (0 for success, 1 if role not found)
+        """
         try:

As per coding guidelines, type hints are required in Python code.

🤖 Prompt for AI Agents
In cortex/cli.py around lines 702 to 721, the role_show method is missing a
return type hint and has an underspecified docstring; update the signature to
include a return type hint (-> int) and expand the docstring to document the
parameters and return value (describe the 'name' parameter and that the method
returns 0 on success and 1 on not found), keeping existing behavior and
exception handling unchanged.

Comment on lines +723 to +783
def role_create(self, name: str, description: str = "", from_template: str | None = None):
"""Create a new custom role"""
import subprocess
import tempfile

manager = RoleManager()

# Check if role already exists
if manager.role_exists(name):
self._print_error(f"Role '{name}' already exists")
return 1

# If from_template, copy the template
if from_template:
try:
template_role = manager.get_role(from_template)
manager.create_role(
name=name,
description=description or f"Custom role based on {from_template}",
prompt_additions=template_role.prompt_additions,
priorities=template_role.priorities.copy(),
)
self._print_success(f"Created role '{name}' from template '{from_template}'")
cx_print(f"Edit at: ~/.cortex/roles/{name}.yaml", "info")
return 0
except RoleNotFoundError:
self._print_error(f"Template role '{from_template}' not found")
return 1
except RoleError as e:
self._print_error(f"Failed to create role: {e}")
return 1

# Create from scratch - use template and open in editor
template = manager.get_role_template().replace("my-custom-role", name)

if description:
template = template.replace(
'"Brief description of what this role does"',
f'"{description}"'
)

# Save template to the roles directory
role_path = manager.custom_roles_dir / f"{name}.yaml"
role_path.parent.mkdir(parents=True, exist_ok=True)

with open(role_path, "w") as f:
f.write(template)

self._print_success(f"Created role template at: {role_path}")
cx_print("Edit the file to customize your role's behavior", "info")

# Try to open in editor
editor = os.environ.get("EDITOR", "nano")
cx_print(f"Opening in {editor}...", "info")

try:
subprocess.run([editor, str(role_path)])
except FileNotFoundError:
cx_print(f"Could not open editor. Edit the file manually: {role_path}", "warning")

return 0
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Add return type hint, enhance docstring, and remove unused import.

Several improvements needed for code quality and documentation.

🔎 Proposed enhancements
-    def role_create(self, name: str, description: str = "", from_template: str | None = None):
-        """Create a new custom role"""
+    def role_create(self, name: str, description: str = "", from_template: str | None = None) -> int:
+        """Create a new custom role, optionally from a template.
+        
+        Args:
+            name: Name for the new role
+            description: Optional description for the role
+            from_template: Optional existing role name to copy from
+            
+        Returns:
+            int: Exit code (0 for success, 1 on error)
+        """
         import subprocess
-        import tempfile

Notes:

  • tempfile is imported at line 726 but never used
  • As per coding guidelines, type hints and comprehensive docstrings are required
🤖 Prompt for AI Agents
In cortex/cli.py around lines 723 to 783, remove the unused "tempfile" import,
add an explicit return type hint "-> int" to the role_create signature, and
replace the short docstring with a full one-line summary plus parameter and
return descriptions (name: role name, description: optional text, from_template:
optional template to copy; return: 0 on success, 1 on error). Ensure imports
still include subprocess and os as needed and update any linter comments if
required.

Comment on lines +785 to +795
def role_delete(self, name: str):
"""Delete a custom role"""
manager = RoleManager()

try:
manager.delete_role(name)
self._print_success(f"Deleted role '{name}'")
return 0
except RoleError as e:
self._print_error(str(e))
return 1
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Add return type hint and enhance docstring.

The method needs a return type hint and better documentation.

🔎 Proposed enhancement
-    def role_delete(self, name: str):
-        """Delete a custom role"""
+    def role_delete(self, name: str) -> int:
+        """Delete a custom role.
+        
+        Args:
+            name: Name of the role to delete
+            
+        Returns:
+            int: Exit code (0 for success, 1 on error)
+        """
         manager = RoleManager()

As per coding guidelines, type hints are required in Python code.

🤖 Prompt for AI Agents
In cortex/cli.py around lines 785 to 795, the role_delete method is missing a
return type hint and has a minimal docstring; update the signature to include a
return type of int and expand the docstring to describe the purpose, parameters
(name: str), return value (0 on success, 1 on failure), and which exceptions are
handled (RoleError) so callers and static type checkers have clear expectations.

@Zafeeruddin
Copy link
Author

Hi @Zafeeruddin - please only work on issues that are assigned to you, and make sure to comment when you start working on an issue so others know. Issue cortexlinux/cortex-pro#1 is assigned to @pavanimanchala53 and is already in progress, which caused duplicate work. Take a look at https://github.com/cortexlinux/cortex/blob/main/Contributing.md

Thanks for letting me know
Please close this, I will look onto other issues.

@mikejmorgan-ai
Copy link
Member

@Zafeeruddin Thanks for working on Custom AI Roles! We merged #310 from @pavanimanchala53 which implemented the same feature. Please check our other open issues for more contribution opportunities - we appreciate your effort!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants