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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 56 additions & 8 deletions cortex/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,39 @@ def install(
self._print_error(error)
return 1

# 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
Comment on lines +838 to +845
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 +832 to +845
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.


prediction = predictor.predict_conflicts(package_name)
if prediction.conflicts:
console.print()
predictor.display_prediction(prediction)
console.print()

if not execute:
console.print(
"[yellow]Conflicts detected. Use --execute to proceed anyway, "
"or resolve conflicts first.[/yellow]"
)
return 1
else:
console.print("[yellow]Proceeding despite conflicts (--execute flag)...[/yellow]")
except Exception as e:
# Don't block installation if prediction fails
self._debug(f"Conflict prediction skipped: {e}")
Comment on lines +861 to +863
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}")


# Special-case the ml-cpu stack:
# The LLM sometimes generates outdated torch==1.8.1+cpu installs
# which fail on modern Python. For the "pytorch-cpu jupyter numpy pandas"
Expand Down Expand Up @@ -3866,8 +3899,8 @@ def main():
"action",
nargs="?",
default="analyze",
choices=["analyze", "parse", "check", "compare"],
help="Action to perform (default: analyze)",
choices=["analyze", "parse", "check", "compare", "predict"],
help="Action to perform (default: analyze). Use 'predict' for AI conflict prediction.",
)
deps_parser.add_argument(
"packages",
Expand Down Expand Up @@ -4027,13 +4060,28 @@ def main():
verbose=getattr(args, "verbose", False),
)
elif args.command == "deps":
from cortex.semver_resolver import run_semver_resolver
action = getattr(args, "action", "analyze")
packages = getattr(args, "packages", None)
verbose = getattr(args, "verbose", False)

return run_semver_resolver(
action=getattr(args, "action", "analyze"),
packages=getattr(args, "packages", None),
verbose=getattr(args, "verbose", False),
)
if action == "predict":
from cortex.conflict_predictor import run_conflict_predictor

if not packages:
console.print("[yellow]Usage: cortex deps predict <package_name>[/yellow]")
return 1
return run_conflict_predictor(
package_name=packages[0],
verbose=verbose,
)
else:
from cortex.semver_resolver import run_semver_resolver

return run_semver_resolver(
action=action,
packages=packages,
verbose=verbose,
)
elif args.command == "health":
from cortex.health_score import run_health_check

Expand Down
Loading
Loading