Skip to content

Conversation

@bimakw
Copy link

@bimakw bimakw commented Jan 17, 2026

Summary

Implements #428 - AI-powered dependency conflict prediction

Bounty: $150

Changes

New Module: cortex/conflict_predictor.py

  • Parse /var/lib/dpkg/status for current system state
  • Build dependency graph from apt-cache
  • Predict conflicts BEFORE installation starts
  • Suggest resolution strategies ranked by safety

CLI Integration

  • New command: cortex deps predict <package>
  • Auto-runs before cortex install <package>
  • Blocks installation if conflicts detected (use --execute to override)

Test Coverage

  • 29 unit tests in tests/test_conflict_predictor.py
  • All tests passing

Features

Conflict Detection

  • Known package conflicts (mysql vs mariadb, nginx vs apache2, etc.)
  • Declared conflicts from package metadata (Conflicts/Breaks fields)
  • Version constraint mismatches
  • Transitive dependency conflicts
  • Pip package conflicts (tensorflow/numpy, etc.)

Resolution Strategies (ranked by safety)

  1. SAFE: Install compatible version
  2. SAFE: Use Python virtual environment
  3. LOW_RISK: Upgrade/downgrade conflicting package
  4. LOW_RISK: Use alternative package
  5. MEDIUM_RISK: Remove conflicting package

Example Usage

$ cortex deps predict mysql-server

╭─────────────────── Conflict Prediction ───────────────────╮
│ Conflict predicted!                                        │
│ 1 issue(s) found for mysql-server                         │
╰────────────────────────────────────────────────────────────╯

┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Type             ┃ Package        ┃ Conflicts With     ┃ Confidence┃
┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ package_conflict │ mysql-server   │ mariadb-server     │ 100%      │
└──────────────────┴────────────────┴────────────────────┴───────────┘

Suggested Resolutions:

1. Remove mariadb-server (Recommended)
   MEDIUM_RISK
   Commands:
      $ sudo apt-get remove mariadb-server
      $ sudo apt-get install mysql-server

2. Use alternative: postgresql
   LOW_RISK
   Commands:
      $ sudo apt-get install postgresql

Acceptance Criteria from Issue

  • Dependency graph analysis before install
  • Conflict prediction with confidence scores
  • Resolution suggestions ranked by safety
  • Integration with apt/dpkg dependency data
  • Works with pip packages too
  • CLI output shows prediction and suggestions

Test Plan

  • Unit tests pass: pytest tests/test_conflict_predictor.py -v (29 tests)
  • Manual testing on Ubuntu/Debian system

/claim #428

Summary by CodeRabbit

  • New Features

    • AI-powered conflict prediction runs before installation and via a new "predict" deps action
    • JSON output for machine-readable conflict reports and suggested resolutions
  • Behavioral Changes

    • Installations are blocked by default if conflicts are detected; use --execute to proceed
    • Prediction failures are non-fatal and logged; user-facing messaging clarifies detected conflicts and execution choices
  • Tests

    • Comprehensive test suite added for the conflict prediction functionality

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

Implements cortexlinux#428 - AI-powered dependency conflict prediction

Features:
- Parse /var/lib/dpkg/status for current system state
- Build dependency graph from apt-cache
- Predict conflicts BEFORE installation starts
- Check version constraints and known package conflicts
- Detect transitive dependency conflicts
- Suggest resolution strategies ranked by safety:
  1. Install compatible version (SAFE)
  2. Upgrade/downgrade conflicting package (LOW_RISK)
  3. Use virtual environment for Python (SAFE)
  4. Remove conflicting package (MEDIUM_RISK)
  5. Use alternative package (LOW_RISK)
- Support pip package conflict detection

Usage:
  cortex deps predict <package>
  cortex deps predict mysql-server

Integration:
- Automatically runs before `cortex install <package>`
- Blocks installation if conflicts detected (use --execute to override)

Example output:
  $ cortex deps predict mysql-server
  ⚠️  Conflict predicted: mysql-server vs mariadb-server

  Suggested Resolutions:
  1. Remove mariadb-server (MEDIUM_RISK) [Recommended]
  2. Use alternative: postgresql (LOW_RISK)
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 17, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

A new ConflictPredictor module is added and integrated into the CLI: installs now run a pre-install conflict prediction step and a new "deps predict" action is available. Comprehensive tests for prediction logic and parsing are included.

Changes

Cohort / File(s) Summary
Conflict Prediction Module
cortex/conflict_predictor.py
New ~841-line module implementing conflict detection and resolution suggestion types, parsing dpkg/apt/pip metadata, detecting declared/version/transitive conflicts, producing ConflictPrediction and ResolutionStrategy outputs, JSON mode and Rich CLI display.
CLI Integration
cortex/cli.py
Install flow augmented to call conflict prediction before installing; added predict action to deps (via run_conflict_predictor()); prediction failures are non-fatal; without --execute installation is blocked when conflicts found, with messaging updated.
Tests
tests/test_conflict_predictor.py
New ~521-line test suite covering parsing, VersionConstraint, package models, conflict detection, resolution generation/prioritization, dpkg status parsing, apt-cache/pip interactions (mocked), edge cases, and JSON output formatting.

Sequence Diagram

sequenceDiagram
    participant User as User/CLI
    participant CLI as cortex CLI
    participant Predictor as ConflictPredictor
    participant DpkgApt as dpkg/apt System
    participant PkgMgr as Package Manager

    User->>CLI: cortex install <package> [--execute]
    CLI->>Predictor: predict_conflicts(package_name)
    Predictor->>DpkgApt: read /var/lib/dpkg/status & apt-cache & pip list
    DpkgApt-->>Predictor: metadata, versions, deps
    Predictor->>Predictor: analyze declared/version/transitive conflicts
    Predictor-->>CLI: ConflictPrediction (conflicts, resolutions, can_install)

    alt Conflicts Detected
        CLI->>User: show conflicts & resolutions
        alt --execute provided
            User-->>CLI: confirm proceed
            CLI->>PkgMgr: install package
            PkgMgr-->>User: installation result
        else
            CLI->>User: block install, suggest resolutions
        end
    else No Conflicts
        CLI->>PkgMgr: install package
        PkgMgr-->>User: installation result
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Suggested reviewers

  • mikejmorgan-ai
  • Suyashd999

Poem

🐰 A rabbit sipped tea by the dpkg tree,

I sniffed out conflicts, then hopped with glee.
Before you install, I give you a poke—
"Here are the fixes," I whisper and joke.
Peace for your packages, one hop, no smoke 🌿

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main feature being added: AI-powered dependency conflict prediction, which is the core change across all modified and new files.
Description check ✅ Passed The PR description is comprehensive and addresses most required template sections: provides context with issue reference (#428), clear summary of changes, detailed feature list, usage examples, and test coverage status. However, the AI Disclosure checkbox section is completely missing.
Docstring Coverage ✅ Passed Docstring coverage is 95.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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.

@github-actions
Copy link

github-actions bot commented Jan 17, 2026

CLA Verification Failed

The following contributors have not signed the Contributor License Agreement:

  • @bimakw (bimakw, 51526537+bimakw@users.noreply.github.com)

How to Sign

  1. Read the CLA document
  2. Open a CLA signature request
  3. A maintainer will add you to the signers list
  4. Comment recheck on this PR to re-run verification

Verified Signers


This check runs automatically. Maintainers can update .github/cla-signers.json to add signers.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @bimakw, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the cortex CLI by implementing a robust, AI-powered dependency conflict prediction system. The primary goal is to prevent installation failures and system instability by identifying potential package conflicts before they occur. It provides users with clear insights into detected issues and offers actionable, safety-ranked resolution strategies, thereby streamlining the package management process and improving overall system reliability.

Highlights

  • AI-Powered Conflict Prediction: Introduces a new cortex/conflict_predictor.py module that leverages AI principles to predict dependency conflicts before package installation.
  • CLI Integration: Integrates conflict prediction directly into the cortex install <package> command, automatically checking for conflicts and blocking installation if issues are found (with an --execute override). A new cortex deps predict <package> command is also available for on-demand analysis.
  • Comprehensive Conflict Detection: The system detects various types of conflicts, including known package clashes (e.g., mysql vs. mariadb), declared conflicts from package metadata, version constraint mismatches, transitive dependency conflicts, and Python (pip) package conflicts.
  • Ranked Resolution Strategies: Provides suggested resolution strategies for detected conflicts, ranked by their safety level (SAFE, LOW_RISK, MEDIUM_RISK, HIGH_RISK), along with commands to execute them.
  • Extensive Test Coverage: Includes 29 new unit tests in tests/test_conflict_predictor.py to ensure the reliability and accuracy of the conflict prediction logic.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces an impressive new feature for AI-powered dependency conflict prediction. The implementation is comprehensive, covering both apt and pip packages, analyzing transitive dependencies, and suggesting resolution strategies. The code is well-structured with dataclasses and enums, and the inclusion of unit tests is commendable. However, I've identified a few critical issues in the logic for parsing package information and checking conflicts that could lead to incorrect predictions. There are also several opportunities for refactoring to improve the code's robustness and maintainability. Addressing these points will significantly enhance the reliability and quality of this new feature.

Comment on lines +838 to +845
package_name = software.split()[0].strip()
for prefix in ["apt-get", "apt", "install", "pip", "pip3", "-y"]:
if package_name == prefix:
parts = software.split()
for p in parts:
if p not in ["apt-get", "apt", "install", "pip", "pip3", "-y", "sudo"]:
package_name = p
break
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 logic to extract the package name from the software string is not robust. It fails for commands that start with sudo, as "sudo" is not in the list of prefixes to be skipped. For example, if the input is "sudo apt-get install nginx", the extracted package_name will incorrectly be "sudo". This should be refactored for more reliable package name extraction.

Suggested change
package_name = software.split()[0].strip()
for prefix in ["apt-get", "apt", "install", "pip", "pip3", "-y"]:
if package_name == prefix:
parts = software.split()
for p in parts:
if p not in ["apt-get", "apt", "install", "pip", "pip3", "-y", "sudo"]:
package_name = p
break
# Extract package name by filtering out known command parts
parts = software.split()
known_command_parts = {"sudo", "apt-get", "apt", "install", "pip", "pip3", "-y"}
package_name = next((p for p in parts if p not in known_command_parts), None)
if not package_name:
# Let the outer except block handle this by raising an error.
raise ValueError(f"Could not determine package_name from '{software}'")

Comment on lines +292 to +294
def _parse_dependency_list(self, dep_str: str) -> list[str]:
"""Parse dependency string including version constraints."""
return self._parse_package_list(dep_str)
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 _parse_dependency_list method, which is used to parse the Depends field for installed packages, calls _parse_package_list. This strips out version constraint information. As a result, the depends attribute of InstalledPackage only contains package names, not their required versions. This is a significant limitation as it prevents the conflict predictor from checking if a new package would break the dependency requirements of an already installed package.

The dependency parsing for installed packages should be enhanced to capture version constraints. You should update the InstalledPackage dataclass to store VersionConstraint objects and implement _parse_dependency_list to correctly parse them, similar to how it's done for PackageCandidate.

Comment on lines +536 to +537
def _check_pip_conflicts(self, package_name: str, prediction: ConflictPrediction) -> None:
"""Check for pip package conflicts."""
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 _check_pip_conflicts method is incomplete. It only checks for version constraints that start with <, such as numpy<2.0. It completely ignores other operators. For example, the PIP_CONFLICTS dictionary defines pandas: {"numpy": ">=1.20"}, but this >= constraint will never be evaluated by the current logic. The implementation needs to be extended to handle other common pip version specifiers like >=, ==, etc.

Additionally, simple string comparison for versions is unreliable (e.g., '1.10' > '1.2'). For robust Python package version and specifier handling, it's highly recommended to use the packaging library.

Comment on lines +861 to +863
except Exception as e:
# Don't block installation if prediction fails
self._debug(f"Conflict prediction skipped: {e}")
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

Catching a broad Exception and logging it only at the debug level can hide important errors from the user. If an unexpected error occurs during conflict prediction, it would be helpful for the user to be aware of it, even if the installation is not blocked. Consider logging these errors at a warning level so the user knows why the prediction was skipped.

Suggested change
except Exception as e:
# Don't block installation if prediction fails
self._debug(f"Conflict prediction skipped: {e}")
except Exception as e:
# Don't block installation if prediction fails, but warn the user.
console.print(f"[yellow]Warning: Conflict prediction skipped due to an error: {e}[/yellow]")
self._debug(f"Full error during conflict prediction: {e}")

Comment on lines +174 to +176
"openjdk-8-jdk": [],
"openjdk-11-jdk": [],
"openjdk-17-jdk": [],
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 KNOWN_CONFLICTS dictionary contains entries for Java JDKs with empty conflict lists (e.g., "openjdk-8-jdk": []). These entries serve no purpose and can be removed to reduce clutter. If they are intended as placeholders for future logic, a TODO comment would be more appropriate.

Suggested change
"openjdk-8-jdk": [],
"openjdk-11-jdk": [],
"openjdk-17-jdk": [],
# Java conflicts can be added here if any are identified.

Comment on lines +231 to +255
current_pkg: dict = {}
for line in content.split("\n"):
if line.startswith("Package:"):
if current_pkg.get("Package") and current_pkg.get("Status", "").startswith(
"install ok"
):
self._add_installed_package(current_pkg)
current_pkg = {"Package": line.split(":", 1)[1].strip()}
elif line.startswith("Version:"):
current_pkg["Version"] = line.split(":", 1)[1].strip()
elif line.startswith("Status:"):
current_pkg["Status"] = line.split(":", 1)[1].strip()
elif line.startswith("Depends:"):
current_pkg["Depends"] = line.split(":", 1)[1].strip()
elif line.startswith("Conflicts:"):
current_pkg["Conflicts"] = line.split(":", 1)[1].strip()
elif line.startswith("Breaks:"):
current_pkg["Breaks"] = line.split(":", 1)[1].strip()
elif line.startswith("Provides:"):
current_pkg["Provides"] = line.split(":", 1)[1].strip()

# Don't forget the last package
if current_pkg.get("Package") and current_pkg.get("Status", "").startswith("install ok"):
self._add_installed_package(current_pkg)

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 the dpkg/status file uses a long if/elif chain to process each line. This approach is a bit fragile and can be hard to maintain. A more robust and readable approach would be to parse the file in blocks (separated by blank lines) and then process each block as a dictionary of key-value pairs.

Suggested change
current_pkg: dict = {}
for line in content.split("\n"):
if line.startswith("Package:"):
if current_pkg.get("Package") and current_pkg.get("Status", "").startswith(
"install ok"
):
self._add_installed_package(current_pkg)
current_pkg = {"Package": line.split(":", 1)[1].strip()}
elif line.startswith("Version:"):
current_pkg["Version"] = line.split(":", 1)[1].strip()
elif line.startswith("Status:"):
current_pkg["Status"] = line.split(":", 1)[1].strip()
elif line.startswith("Depends:"):
current_pkg["Depends"] = line.split(":", 1)[1].strip()
elif line.startswith("Conflicts:"):
current_pkg["Conflicts"] = line.split(":", 1)[1].strip()
elif line.startswith("Breaks:"):
current_pkg["Breaks"] = line.split(":", 1)[1].strip()
elif line.startswith("Provides:"):
current_pkg["Provides"] = line.split(":", 1)[1].strip()
# Don't forget the last package
if current_pkg.get("Package") and current_pkg.get("Status", "").startswith("install ok"):
self._add_installed_package(current_pkg)
# Split content into package blocks
package_blocks = content.strip().split("\n\n")
for block in package_blocks:
pkg_dict = {}
for line in block.split("\n"):
if ":" in line:
key, value = line.split(":", 1)
pkg_dict[key.strip()] = value.strip()
if pkg_dict.get("Package") and pkg_dict.get("Status", "").startswith("install ok"):
self._add_installed_package(pkg_dict)

depth: int = 0,
) -> None:
"""Recursively check transitive dependency conflicts."""
if depth > 5: # Limit recursion depth
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 recursion depth limit is hardcoded as a magic number 5. This should be defined as a class-level constant to improve readability and make it easier to configure if needed.

For example, add MAX_RECURSION_DEPTH = 5 to the ConflictPredictor class and use self.MAX_RECURSION_DEPTH here.

Suggested change
if depth > 5: # Limit recursion depth
if depth > 5: # TODO: Use a class constant for max recursion depth

Comment on lines +622 to +626
commands=[
f"sudo apt-get install {conflict.conflicting_with}={conflict.required_version}"
if conflict.required_version
else f"sudo apt-get install --only-upgrade {conflict.conflicting_with}"
],
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 suggested command sudo apt-get install {conflict.conflicting_with}={conflict.required_version} might be incorrect or incomplete. The required_version field only contains the version number, not the comparison operator (e.g., >=, <). For apt-get, version constraints are not specified this way. This could mislead the user into running a command that doesn't work or doesn't resolve the conflict correctly. The command suggestion should be more robust or provide a more generic hint to guide the user.

Suggested change
commands=[
f"sudo apt-get install {conflict.conflicting_with}={conflict.required_version}"
if conflict.required_version
else f"sudo apt-get install --only-upgrade {conflict.conflicting_with}"
],
commands=[
f"apt-cache policy {conflict.conflicting_with}",
f"# Find a version that satisfies '{conflict.required_version or 'compatible'}' and install it:",
f"sudo apt-get install {conflict.conflicting_with}=<version>",
],

@bimakw bimakw mentioned this pull request Jan 17, 2026
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: 3

🤖 Fix all issues with AI agents
In `@cortex/cli.py`:
- Around line 832-845: The package-name extraction incorrectly treats a leading
"sudo" as the package (so ConflictPredictor gets "sudo"); update the extraction
logic in the block that builds package_name (the code around ConflictPredictor
and the variable software) to ignore common command prefixes including
"sudo"—either add "sudo" to the prefix list or explicitly strip a leading "sudo"
before splitting—and then continue scanning parts for the first token not in
["apt-get","apt","install","pip","pip3","-y","sudo"] so package_name becomes the
actual package (e.g., "nginx") before passing to ConflictPredictor.

In `@cortex/conflict_predictor.py`:
- Around line 370-387: predict_conflicts currently returns early when
_get_apt_package_info(package_name) yields no candidate, which skips pip-only
conflict checks; change the flow so that when candidate is falsy you still
normalize the input package_name (strip version specifiers like "==", ">=", "~="
or use packaging to parse names) and call _check_pip_conflicts(normalized_name,
prediction) to populate prediction.conflicts and prediction.resolutions, then
append the existing warning (prediction.warnings.append(...)) before returning;
keep existing behavior when candidate exists (i.e., run apt checks and also call
_check_pip_conflicts as appropriate) so both apt-absent pip-only installs and
version-pinned names are checked.
- Around line 552-574: The current lexicographic comparison in the pip conflict
check (involving PIP_CONFLICTS, _pip_cache and creating
PredictedConflict/ConflictType entries) must be replaced with semantic version
comparison using packaging.version.Version: import Version and InvalidVersion
(or use the project's conditional import/fallback pattern like in
config_manager.py), parse installed_ver and max_ver with Version and compare
(Version(installed_ver) >= Version(max_ver)); catch InvalidVersion and log/debug
a message instead of raising, and ensure packaging (e.g., "packaging>=21.0") is
added to dependencies in pyproject.toml or handled via the conditional import
fallback.

Comment on lines +832 to +845
# Predict conflicts before installation
try:
from cortex.conflict_predictor import ConflictPredictor

predictor = ConflictPredictor()
# Extract package name (first word, remove any pip/apt prefix)
package_name = software.split()[0].strip()
for prefix in ["apt-get", "apt", "install", "pip", "pip3", "-y"]:
if package_name == prefix:
parts = software.split()
for p in parts:
if p not in ["apt-get", "apt", "install", "pip", "pip3", "-y", "sudo"]:
package_name = p
break
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 | 🟠 Major

Handle sudo prefix when extracting package name.

If the input starts with sudo (e.g., sudo apt-get install nginx), package_name remains "sudo" because it isn’t in the prefix list, so prediction targets the wrong package.

🛠️ Suggested fix
-            package_name = software.split()[0].strip()
-            for prefix in ["apt-get", "apt", "install", "pip", "pip3", "-y"]:
-                if package_name == prefix:
-                    parts = software.split()
-                    for p in parts:
-                        if p not in ["apt-get", "apt", "install", "pip", "pip3", "-y", "sudo"]:
-                            package_name = p
-                            break
+            parts = software.split()
+            skip = {"sudo", "apt-get", "apt", "install", "pip", "pip3", "-y"}
+            package_name = next((p for p in parts if p not in skip), "")
📝 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
# Predict conflicts before installation
try:
from cortex.conflict_predictor import ConflictPredictor
predictor = ConflictPredictor()
# Extract package name (first word, remove any pip/apt prefix)
package_name = software.split()[0].strip()
for prefix in ["apt-get", "apt", "install", "pip", "pip3", "-y"]:
if package_name == prefix:
parts = software.split()
for p in parts:
if p not in ["apt-get", "apt", "install", "pip", "pip3", "-y", "sudo"]:
package_name = p
break
# Predict conflicts before installation
try:
from cortex.conflict_predictor import ConflictPredictor
predictor = ConflictPredictor()
# Extract package name (first word, remove any pip/apt prefix)
parts = software.split()
skip = {"sudo", "apt-get", "apt", "install", "pip", "pip3", "-y"}
package_name = next((p for p in parts if p not in skip), "")
🤖 Prompt for AI Agents
In `@cortex/cli.py` around lines 832 - 845, The package-name extraction
incorrectly treats a leading "sudo" as the package (so ConflictPredictor gets
"sudo"); update the extraction logic in the block that builds package_name (the
code around ConflictPredictor and the variable software) to ignore common
command prefixes including "sudo"—either add "sudo" to the prefix list or
explicitly strip a leading "sudo" before splitting—and then continue scanning
parts for the first token not in
["apt-get","apt","install","pip","pip3","-y","sudo"] so package_name becomes the
actual package (e.g., "nginx") before passing to ConflictPredictor.

Comment on lines +370 to +387
def predict_conflicts(self, package_name: str) -> ConflictPrediction:
"""Predict conflicts before installing a package.
Args:
package_name: Name of package to install
Returns:
ConflictPrediction with all detected conflicts and resolutions
"""
logger.info(f"Predicting conflicts for {package_name}...")
prediction = ConflictPrediction(package=package_name)

# Get candidate package info
candidate = self._get_apt_package_info(package_name)
if not candidate:
prediction.warnings.append(f"Package {package_name} not found in apt cache")
return prediction

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 | 🟠 Major

Pip conflicts are skipped when apt-cache lookup fails.

predict_conflicts returns early if the apt candidate is missing, so pip-only installs (or version‑pinned names not in apt) never hit _check_pip_conflicts, despite the advertised feature. Also, version‑pinned names like tensorflow==2.10 won’t match pip mappings. Consider normalizing the name and still running pip checks on apt-cache miss.

🛠️ Suggested fix
-        prediction = ConflictPrediction(package=package_name)
+        normalized_name = re.split(r"[<>=!~]", package_name, 1)[0].split("[", 1)[0].strip()
+        prediction = ConflictPrediction(package=normalized_name)
+        package_name = normalized_name
@@
-        candidate = self._get_apt_package_info(package_name)
+        candidate = self._get_apt_package_info(package_name)
         if not candidate:
             prediction.warnings.append(f"Package {package_name} not found in apt cache")
-            return prediction
+            if self._is_python_related(package_name):
+                self._check_pip_conflicts(package_name, prediction)
+                if prediction.conflicts:
+                    prediction.can_install = False
+                    self._generate_resolutions(prediction)
+            return prediction
🤖 Prompt for AI Agents
In `@cortex/conflict_predictor.py` around lines 370 - 387, predict_conflicts
currently returns early when _get_apt_package_info(package_name) yields no
candidate, which skips pip-only conflict checks; change the flow so that when
candidate is falsy you still normalize the input package_name (strip version
specifiers like "==", ">=", "~=" or use packaging to parse names) and call
_check_pip_conflicts(normalized_name, prediction) to populate
prediction.conflicts and prediction.resolutions, then append the existing
warning (prediction.warnings.append(...)) before returning; keep existing
behavior when candidate exists (i.e., run apt checks and also call
_check_pip_conflicts as appropriate) so both apt-absent pip-only installs and
version-pinned names are checked.

Comment on lines +552 to +574
if pip_name in self.PIP_CONFLICTS:
required_constraints = self.PIP_CONFLICTS[pip_name]

for dep_name, constraint in required_constraints.items():
if dep_name in self._pip_cache:
installed_ver, _ = self._pip_cache[dep_name]
# Simple version check
if constraint.startswith("<"):
max_ver = constraint[1:]
if installed_ver >= max_ver:
prediction.conflicts.append(
PredictedConflict(
conflict_type=ConflictType.VERSION_MISMATCH,
package=pip_name,
conflicting_with=dep_name,
description=(
f"{pip_name} requires {dep_name}{constraint}, "
f"but {installed_ver} is installed via pip"
),
installed_version=installed_ver,
required_version=constraint,
confidence=0.85,
)
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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

git ls-files | grep -E "conflict_predictor\.py"

Repository: cortexlinux/cortex

Length of output: 123


🏁 Script executed:

wc -l cortex/conflict_predictor.py

Repository: cortexlinux/cortex

Length of output: 94


🏁 Script executed:

sed -n '552,574p' cortex/conflict_predictor.py

Repository: cortexlinux/cortex

Length of output: 1336


🏁 Script executed:

head -50 cortex/conflict_predictor.py

Repository: cortexlinux/cortex

Length of output: 1499


🏁 Script executed:

grep -n "packaging\|from packaging" cortex/conflict_predictor.py

Repository: cortexlinux/cortex

Length of output: 44


🏁 Script executed:

grep -n "import" cortex/conflict_predictor.py | head -20

Repository: cortexlinux/cortex

Length of output: 380


🏁 Script executed:

cat requirements.txt 2>/dev/null || cat setup.py 2>/dev/null | head -50 || ls *.txt pyproject.toml

Repository: cortexlinux/cortex

Length of output: 44


🏁 Script executed:

find . -maxdepth 2 -name "requirements*.txt" -o -name "setup.py" -o -name "pyproject.toml" 2>/dev/null

Repository: cortexlinux/cortex

Length of output: 78


🏁 Script executed:

cat pyproject.toml

Repository: cortexlinux/cortex

Length of output: 5758


🏁 Script executed:

grep -r "packaging" . --include="*.py" --include="*.txt" --include="*.toml"

Repository: cortexlinux/cortex

Length of output: 441


🏁 Script executed:

sed -n '1,30p' cortex/config_manager.py | grep -A 50 "packaging"

Repository: cortexlinux/cortex

Length of output: 44


🏁 Script executed:

grep -B 5 -A 10 "from packaging import version" cortex/config_manager.py

Repository: cortexlinux/cortex

Length of output: 505


🏁 Script executed:

pip show packaging 2>/dev/null || python -c "import packaging; print(packaging.__version__)"

Repository: cortexlinux/cortex

Length of output: 148


🏁 Script executed:

grep -r "packaging" pyproject.toml requirements.txt 2>/dev/null

Repository: cortexlinux/cortex

Length of output: 44


🏁 Script executed:

cat tests/test_conflict_predictor.py | head -100

Repository: cortexlinux/cortex

Length of output: 3339


🏁 Script executed:

wc -l tests/test_conflict_predictor.py

Repository: cortexlinux/cortex

Length of output: 98


Fix version comparison to use semantic versioning instead of lexicographic comparison.

Line 561: installed_ver >= max_ver uses string comparison which produces incorrect results for version numbers (e.g., "1.10" < "1.9" lexicographically). This causes false positives/negatives in conflict detection.

Use packaging.version.Version for accurate semantic version comparison. However, note that packaging is not currently declared as a dependency in pyproject.toml. Either add it to the main dependencies list or use a conditional import with fallback (similar to the pattern in cortex/config_manager.py).

🛠️ Suggested approach

Add "packaging>=21.0" to dependencies in pyproject.toml, then update the comparison:

from packaging.version import Version, InvalidVersion

# At line 561:
try:
    if Version(installed_ver) >= Version(max_ver):
        prediction.conflicts.append(...)
except InvalidVersion:
    logger.debug("Unable to parse pip versions for %s", dep_name)
🤖 Prompt for AI Agents
In `@cortex/conflict_predictor.py` around lines 552 - 574, The current
lexicographic comparison in the pip conflict check (involving PIP_CONFLICTS,
_pip_cache and creating PredictedConflict/ConflictType entries) must be replaced
with semantic version comparison using packaging.version.Version: import Version
and InvalidVersion (or use the project's conditional import/fallback pattern
like in config_manager.py), parse installed_ver and max_ver with Version and
compare (Version(installed_ver) >= Version(max_ver)); catch InvalidVersion and
log/debug a message instead of raising, and ensure packaging (e.g.,
"packaging>=21.0") is added to dependencies in pyproject.toml or handled via the
conditional import fallback.

@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
3 Security Hotspots

See analysis details on SonarQube Cloud

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.

2 participants