Skip to content

Conversation

@dhvll
Copy link
Collaborator

@dhvll dhvll commented Nov 18, 2025

A lightweight Python helper that converts natural language requests into apt/yum/dnf commands.

from cortex.packages import PackageManager

pm = PackageManager()  # auto-detects apt/yum/dnf
commands = pm.parse("install python with data science libraries")
print(commands)

Basic Installation

pm.parse("install python")        # ['apt install -y python3 python3-pip python3-venv']
pm.parse("install docker")        # ['apt install -y docker.io docker-compose']

Development Tools

pm.parse("install python development tools")  # ['apt install -y python3-dev build-essential ...']
pm.parse("install web development tools")     # ['apt install -y nodejs npm git curl wget']

Data Science & Machine Learning

pm.parse("install python with data science libraries")
pm.parse("install python machine learning libraries")

Databases

pm.parse("install mysql")        # ['apt install -y mysql-server mysql-client']
pm.parse("install postgresql")   # ['apt install -y postgresql postgresql-contrib']
pm.parse("install redis")        # ['apt install -y redis-server']

System Tools

pm.parse("install system monitoring tools")  # ['apt install -y htop iotop nethogs sysstat']
pm.parse("install network tools")            # ['apt install -y net-tools iputils-ping tcpdump wireshark']
pm.parse("install security tools")           # ['apt install -y ufw fail2ban openssh-server']

Different Actions

pm.parse("remove python")
pm.parse("update python")
pm.parse("search python")

Case Insensitive & Variations

pm.parse("INSTALL PYTHON")
pm.parse("Setup Docker")
pm.parse("get git")
pm.parse("install python and docker and git")

Multi-Distribution Examples

from cortex.packages import PackageManager, PackageManagerType

pm_apt = PackageManager(pm_type=PackageManagerType.APT)
pm_apt.parse("install apache")   # ['apt install -y apache2']

pm_yum = PackageManager(pm_type=PackageManagerType.YUM)
pm_yum.parse("install apache")   # ['yum install -y httpd']

pm_dnf = PackageManager(pm_type=PackageManagerType.DNF)
pm_dnf.parse("install docker")   # ['dnf install -y docker docker-compose']

Error Handling

try:
    pm.parse("")
except ValueError as e:
    print(e)  # Empty request provided

try:
    pm.parse("install xyzabc123unknown")
except ValueError as e:
    print(e)  # No matching packages found

Package Manager Detection

pm = PackageManager()  # auto-detects apt/yum/dnf

pm_apt = PackageManager(pm_type=PackageManagerType.APT)
pm_yum = PackageManager(pm_type=PackageManagerType.YUM)
pm_dnf = PackageManager(pm_type=PackageManagerType.DNF)

Package Information Lookup

info = pm.get_package_info("python3")
if info:
    for key, value in list(info.items())[:5]:
        print(key, value)

Key Features

  • Natural-language to package-manager translation
  • Supports apt (Ubuntu/Debian), yum (RHEL/CentOS), and dnf (Fedora)
  • 20+ curated software bundles with distro-specific package names
  • Actions: install, remove, update, search
  • Basic validation and error handling
Screencast.from.18-11-25.12.06.16.PM.IST.webm

/closes #7

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 18, 2025

Walkthrough

This PR introduces an intelligent package manager wrapper that auto-detects the system's package manager (apt/yum/dnf), maps natural language requests to packages across multiple domains (Python, data science, web, databases, dev tools, etc.), and generates concrete package manager commands. It includes comprehensive test coverage with 20+ common requests.

Changes

Cohort / File(s) Summary
Module Exports
cortex/__init__.py
Adds public API exports: imports PackageManager and PackageManagerType from .packages module and extends __all__ to expose both new classes.
Package Manager Implementation
cortex/packages.py
New module implementing PackageManagerType enum (APT, YUM, DNF) and PackageManager class. Features auto-detection of system package manager, builds action regex patterns and domain-specific package mappings (Python, data science, web, databases, dev tools, editors, version control, system utilities), normalizes input text, extracts actions (install/remove/update/search), matches packages, and generates appropriate commands (e.g., apt install -y ... or dnf search ...). Includes get_package_info() for fetching package metadata and raises ValueError for empty or unmatched requests.
Test Suite
test/test_packages.py
Comprehensive unittest module with 20+ test cases covering installation, removal, updates, searches, and category-specific requests (Python, data science, web, databases, dev tools, editors, version control) across APT, YUM, and DNF. Tests include normalization logic, action extraction, package info retrieval, edge cases (empty requests, unknown packages), multi-category queries, and case-insensitivity validation. Mocks subprocess.run to validate command generation.

Sequence Diagram

sequenceDiagram
    participant User
    participant PM as PackageManager
    participant PkgMap as Package Mappings
    participant SubProcess as subprocess

    User->>PM: parse("install python with data science")
    activate PM
    
    PM->>PM: _normalize_text()
    Note over PM: "install python with data science"
    
    PM->>PM: _extract_action()
    Note over PM: Action = "install"
    
    PM->>PM: _find_matching_packages()
    PM->>PkgMap: lookup "python" + "data science"
    PkgMap-->>PM: ["python3", "python3-pip", "python3-numpy", "python3-pandas"]
    
    PM->>PM: Build command template<br/>(apt/yum/dnf)
    
    PM->>SubProcess: apt install -y python3 python3-pip...
    SubProcess-->>PM: Success
    
    deactivate PM
    PM-->>User: ["apt install -y python3 python3-pip python3-numpy python3-pandas"]
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Dense logic in cortex/packages.py with multiple interrelated helper methods (_normalize_text, _extract_action, _find_matching_packages, _build_package_mappings, etc.)
  • Intricate natural language parsing and package mapping across multiple domains and package managers
  • Cross-platform compatibility logic (apt, yum, dnf detection and command generation)
  • Comprehensive package mappings must be validated for accuracy and completeness
  • Package name variations and edge cases (e.g., apache vs. httpd for yum) need careful review
  • Test suite is extensive but patterns are consistent; validate representative scenarios cover real-world usage

Poem

🐰 A parser hops through natural speech,
apt and yum now in its reach,
From "install Python" to commands so grand,
The package manager takes a stand!

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Introduce intelligent package manager wrapper' accurately summarizes the main change—adding a new intelligent package manager wrapper module.
Linked Issues check ✅ Passed The pull request fully implements all coding requirements from issue #7: natural language parsing, apt/yum/dnf support, 20+ software bundles, error handling, and comprehensive unit tests.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the package manager wrapper implementation: new PackageManager class, PackageManagerType enum, public API exports, and corresponding test suite.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% 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

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

@sonarqubecloud
Copy link

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: 0

🧹 Nitpick comments (6)
cortex/__init__.py (1)

2-6: Public API export looks good; optionally sort __all__ for Ruff.

Re‑exporting PackageManager and PackageManagerType from the package root is a good call and matches the tests’ usage. To satisfy Ruff (RUF022) and avoid lint noise, you can sort __all__ alphabetically:

-__all__ = ["main", "PackageManager", "PackageManagerType"]
+__all__ = ["PackageManager", "PackageManagerType", "main"]
test/test_packages.py (2)

1-4: Drop the shebang from this test module.

The shebang isn’t needed for a test file that’s run via python -m unittest/pytest and also triggers Ruff’s EXE001 (file not executable). Simplify the header like this:

-#!/usr/bin/env python3
-"""
-Unit tests for the intelligent package manager wrapper.
-"""
+"""
+Unit tests for the intelligent package manager wrapper.
+"""

20-25: Simplify setUp by removing the unused subprocess.run patch.

Because you pass pm_type=PackageManagerType.APT, _detect_package_manager is never called here, so patching cortex.packages.subprocess.run in setUp has no effect. You can simplify:

    def setUp(self):
        """Set up test fixtures."""
-        # Mock package manager detection to use apt for consistent testing
-        with patch('cortex.packages.subprocess.run') as mock_run:
-            mock_run.return_value = MagicMock(returncode=0)
-            self.pm = PackageManager(pm_type=PackageManagerType.APT)
+        # Use a fixed package manager type to keep tests deterministic
+        self.pm = PackageManager(pm_type=PackageManagerType.APT)

This keeps tests deterministic and removes an unnecessary mock.

cortex/packages.py (3)

1-7: Remove shebang and update docstring to mention DNF.

This is a library module, so the shebang isn’t needed and triggers EXE001 if the file isn’t executable. Also, the docstring omits DNF even though it’s supported. You can tidy both:

-#!/usr/bin/env python3
-"""
-Intelligent Package Manager Wrapper for Cortex Linux
-
-Translates natural language requests into apt/yum package manager commands.
-Supports common software installations, development tools, and libraries.
-"""
+"""
+Intelligent Package Manager Wrapper for Cortex Linux.
+
+Translates natural language requests into apt/yum/dnf package manager commands.
+Supports common software installations, development tools, and libraries.
+"""

9-20: Consider using shutil.which instead of spawning which subprocesses.

_detect_package_manager only needs to know whether apt, dnf, or yum exist; using shutil.which avoids extra subprocesses, timeouts, and S607 warnings. Suggested refactor:

-import re
-import subprocess
-import platform
-from typing import List, Dict, Optional, Tuple, Set
-from enum import Enum
+import re
+import shutil
+import subprocess
+import platform
+from typing import List, Dict, Optional, Tuple, Set
+from enum import Enum
     def _detect_package_manager(self) -> PackageManagerType:
-        """Detect the package manager based on the system."""
-        try:
-            # Check for apt
-            result = subprocess.run(
-                ["which", "apt"],
-                capture_output=True,
-                text=True,
-                timeout=2
-            )
-            if result.returncode == 0:
-                return PackageManagerType.APT
-            
-            # Check for dnf (preferred over yum on newer systems)
-            result = subprocess.run(
-                ["which", "dnf"],
-                capture_output=True,
-                text=True,
-                timeout=2
-            )
-            if result.returncode == 0:
-                return PackageManagerType.DNF
-            
-            # Check for yum
-            result = subprocess.run(
-                ["which", "yum"],
-                capture_output=True,
-                text=True,
-                timeout=2
-            )
-            if result.returncode == 0:
-                return PackageManagerType.YUM
-        except (subprocess.TimeoutExpired, FileNotFoundError):
-            pass
-        
-        # Default to apt (most common)
-        return PackageManagerType.APT
+        """Detect the package manager based on the system."""
+        # Prefer APT, then DNF, then YUM
+        if shutil.which("apt"):
+            return PackageManagerType.APT
+        if shutil.which("dnf"):
+            return PackageManagerType.DNF
+        if shutil.which("yum"):
+            return PackageManagerType.YUM
+        # Default to apt (most common)
+        return PackageManagerType.APT

362-393: Fix the unnecessary f-string for "apt update".

The update branch for APT uses an f‑string without placeholders (f"apt update"), which is what Ruff flags (F541). You can drop the f without changing behavior:

-            elif action == "update":
-                return [f"apt update", f"apt upgrade -y {' '.join(packages)}"]
+            elif action == "update":
+                return ["apt update", f"apt upgrade -y {' '.join(packages)}"]

The rest of parse (validation, error handling, and per‑PM command construction) looks solid and is well covered by the tests.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 32ff7ac and f1d6aba.

📒 Files selected for processing (3)
  • cortex/__init__.py (1 hunks)
  • cortex/packages.py (1 hunks)
  • test/test_packages.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
test/test_packages.py (1)
cortex/packages.py (6)
  • PackageManager (23-452)
  • PackageManagerType (16-20)
  • parse (362-406)
  • _normalize_text (261-270)
  • _extract_action (272-282)
  • get_package_info (408-452)
cortex/__init__.py (1)
cortex/packages.py (2)
  • PackageManager (23-452)
  • PackageManagerType (16-20)
🪛 Ruff (0.14.5)
test/test_packages.py

1-1: Shebang is present but file is not executable

(EXE001)

cortex/packages.py

1-1: Shebang is present but file is not executable

(EXE001)


49-49: Starting a process with a partial executable path

(S607)


59-59: Starting a process with a partial executable path

(S607)


69-69: Starting a process with a partial executable path

(S607)


376-376: Avoid specifying long messages outside the exception class

(TRY003)


382-382: Avoid specifying long messages outside the exception class

(TRY003)


391-391: f-string without any placeholders

Remove extraneous f prefix

(F541)


420-420: subprocess call: check for execution of untrusted input

(S603)


421-421: Starting a process with a partial executable path

(S607)


436-436: subprocess call: check for execution of untrusted input

(S603)

cortex/__init__.py

6-6: __all__ is not sorted

Apply an isort-style sorting to __all__

(RUF022)

🔇 Additional comments (4)
test/test_packages.py (1)

27-361: Test coverage for PackageManager is strong and well-aligned with behavior.

The suite thoroughly exercises Python variants, web/dev/database/system bundles, multiple actions (install/remove/update/search), case‑insensitivity, multi‑category requests, and get_package_info for both APT and YUM/DNF with mocks. This gives good safety net around the NL‑to‑command translation behavior.

cortex/packages.py (3)

111-259: Package mappings look consistent and comprehensive.

The curated mappings for Python variants, web/dev tools, databases, system/network utilities, media, security, editors, and VCS align with the tests and acceptance criteria (20+ common requests). The separation into "apt" vs "yum" keys and reuse of "yum" for DNF keep the implementation clear and maintainable.


261-360: NL matching logic matches requirements and test expectations.

The normalization + action extraction + _find_matching_packages pipeline correctly prioritizes Python special cases, multi‑word categories (e.g., “web development”, “system monitoring”), and single‑word software (docker/nginx/git/etc.), and aggregates them via a set before sorting. This matches the behavior asserted in the tests (multi‑category requests, name variations, YUM vs APT differences).


408-452: get_package_info subprocess usage is reasonable and guarded.

get_package_info uses fixed executables (apt-cache, yum/dnf) with shell=False, timeouts, and graceful handling of TimeoutExpired/FileNotFoundError, returning None on failure. Given that, the S603/S607 hints here are effectively false positives and I wouldn’t change this logic unless you want to further constrain package_name or silence the lints with # noqa.

Copy link
Member

@mikejmorgan-ai mikejmorgan-ai left a comment

Choose a reason for hiding this comment

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

✅ APPROVED - Excellent package manager implementation! This replaces PR #17 and unblocks the entire MVP. Outstanding work @dhvll!

@mikejmorgan-ai mikejmorgan-ai merged commit 8514d0f into cortexlinux:main Nov 18, 2025
2 checks passed
Copy link
Member

@mikejmorgan-ai mikejmorgan-ai left a comment

Choose a reason for hiding this comment

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

✅ APPROVED - Excellent package manager implementation! This replaces PR #17 and unblocks the entire MVP. Outstanding work @dhvll!

@dhvll
Copy link
Collaborator Author

dhvll commented Nov 18, 2025

thanks @mikejmorgan-ai

dhvll added a commit to dhvll/cortex that referenced this pull request Nov 28, 2025
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.

Build intelligent apt/yum package manager wrapper

2 participants