Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
147 changes: 0 additions & 147 deletions cortex-cleanup.sh

This file was deleted.

133 changes: 119 additions & 14 deletions cortex/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@
validate_installation_id,
ValidationError
)
# Import the new Notification Manager
# Import Notification Manager
from cortex.notification_manager import NotificationManager
from cortex.optimizer import CleanupOptimizer


class CortexCLI:
Expand Down Expand Up @@ -112,10 +113,9 @@
sys.stdout.write('\r\033[K')
sys.stdout.flush()

# --- New Notification Method ---
# --- Notification Method ---
def notify(self, args):
"""Handle notification commands"""
# Addressing CodeRabbit feedback: Handle missing subcommand gracefully
if not args.notify_action:
self._print_error("Please specify a subcommand (config/enable/disable/dnd/send)")
return 1
Expand All @@ -132,24 +132,21 @@

elif args.notify_action == 'enable':
mgr.config["enabled"] = True
# Addressing CodeRabbit feedback: Ideally should use a public method instead of private _save_config,
# but keeping as is for a simple fix (or adding a save method to NotificationManager would be best).
mgr._save_config()
mgr.save_config()
self._print_success("Notifications enabled")
return 0

elif args.notify_action == 'disable':
mgr.config["enabled"] = False
mgr._save_config()
cx_print("Notifications disabled (Critical alerts will still show)", "warning")
mgr.save_config()
self._print_success("Notifications disabled (Critical alerts will still show)")
return 0

elif args.notify_action == 'dnd':
if not args.start or not args.end:
self._print_error("Please provide start and end times (HH:MM)")
return 1

# Addressing CodeRabbit feedback: Add time format validation
try:
datetime.strptime(args.start, "%H:%M")
datetime.strptime(args.end, "%H:%M")
Expand All @@ -159,7 +156,7 @@

mgr.config["dnd_start"] = args.start
mgr.config["dnd_end"] = args.end
mgr._save_config()
mgr.save_config()
self._print_success(f"DND Window updated: {args.start} - {args.end}")
return 0

Expand All @@ -174,7 +171,99 @@
else:
self._print_error("Unknown notify command")
return 1
# -------------------------------


def cleanup(self, args):
"""Run system cleanup optimization"""
optimizer = CleanupOptimizer()

if args.cleanup_action == 'scan':
return self._cleanup_scan(optimizer)

elif args.cleanup_action == 'run':
return self._cleanup_run(args, optimizer)

else:
self._print_error("Unknown cleanup action")
return 1

def _cleanup_scan(self, optimizer):

Check failure on line 190 in cortex/cli.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this method to not always return the same value.

See more on https://sonarcloud.io/project/issues?id=cortexlinux_cortex&issues=AZsR8ysN3XHNufa59wat&open=AZsR8ysN3XHNufa59wat&pullRequest=295
self._print_status("🔍", "Scanning for cleanup opportunities...")
opportunities = optimizer.scan()

if not opportunities:
self._print_success("No cleanup opportunities found! system is clean.")
return 0

total_bytes = sum(o.size_bytes for o in opportunities)
total_mb = total_bytes / (1024 * 1024)

console.print()
cx_header(f"Cleanup Scan Results ({total_mb:.1f} MB Reclaimable)")

from rich.table import Table
table = Table(box=None)
table.add_column("Type", style="cyan")
table.add_column("Description")
table.add_column("Size", justify="right", style="green")

for opp in opportunities:
size_mb = opp.size_bytes / (1024 * 1024)
table.add_row(
opp.type.replace('_', ' ').title(),
opp.description,
f"{size_mb:.1f} MB"
)

console.print(table)
console.print()
console.print("[dim]Run 'cortex cleanup run' to clean these items.[/dim]")
return 0

def _cleanup_run(self, args, optimizer):
safe_mode = not args.force

self._print_status("🔍", "Preparing cleanup plan...")
commands = optimizer.get_cleanup_plan()

if not commands:
self._print_success("Nothing to clean!")
return 0

console.print("[bold]Proposed Cleanup Operations:[/bold]")
for i, cmd in enumerate(commands, 1):
console.print(f" {i}. {cmd}")

if getattr(args, 'dry_run', False):
console.print("\n[dim](Dry run mode - no changes made)[/dim]")
return 0

if not args.yes:
if not safe_mode:
console.print("\n[bold red]WARNING: Running in FORCE mode (no backups)[/bold red]")

confirm = input("\nProceed with cleanup? (y/n): ")
if confirm.lower() != 'y':
print("Operation cancelled.")
return 0

# Use InstallationCoordinator for execution
def progress_callback(current, total, step):
print(f"[{current}/{total}] {step.description}")

coordinator = InstallationCoordinator(
commands=commands,
descriptions=[f"Cleanup Step {i+1}" for i in range(len(commands))],
progress_callback=progress_callback
)

result = coordinator.execute()
if result.success:
self._print_success("Cleanup completed successfully!")
return 0
else:
self._print_error("Cleanup encountered errors.")
return 1
Comment on lines +223 to +266
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:

find . -name "cli.py" -path "*/cortex/*" -type f

Repository: cortexlinux/cortex

Length of output: 77


🏁 Script executed:

wc -l ./cortex/cli.py

Repository: cortexlinux/cortex

Length of output: 81


🏁 Script executed:

cat -n ./cortex/cli.py | sed -n '1,50p'

Repository: cortexlinux/cortex

Length of output: 1751


🏁 Script executed:

cat -n ./cortex/cli.py | sed -n '210,280p'

Repository: cortexlinux/cortex

Length of output: 3040


🏁 Script executed:

find . -name "optimizer.py" -path "*/cortex/*" -type f

Repository: cortexlinux/cortex

Length of output: 83


🏁 Script executed:

wc -l ./cortex/optimizer.py

Repository: cortexlinux/cortex

Length of output: 87


🏁 Script executed:

cat -n ./cortex/optimizer.py

Repository: cortexlinux/cortex

Length of output: 10005


🏁 Script executed:

find . -name "coordinator.py" -path "*/cortex/*" -type f

Repository: cortexlinux/cortex

Length of output: 85


🏁 Script executed:

wc -l ./cortex/coordinator.py

Repository: cortexlinux/cortex

Length of output: 89


🏁 Script executed:

grep -n "class InstallationCoordinator\|def execute" ./cortex/coordinator.py

Repository: cortexlinux/cortex

Length of output: 144


🏁 Script executed:

cat -n ./cortex/coordinator.py | sed -n '65,120p'

Repository: cortexlinux/cortex

Length of output: 2462


🏁 Script executed:

cat -n ./cortex/coordinator.py | sed -n '252,330p'

Repository: cortexlinux/cortex

Length of output: 3819


🏁 Script executed:

grep -n "_rollback\|def.*backup" ./cortex/coordinator.py

Repository: cortexlinux/cortex

Length of output: 625


🏁 Script executed:

cat -n ./cortex/coordinator.py | sed -n '230,250p'

Repository: cortexlinux/cortex

Length of output: 953


🏁 Script executed:

find . -name "*.md" -type f | head -20

Repository: cortexlinux/cortex

Length of output: 562


🏁 Script executed:

grep -i "safe.*mode\|cleanup.*safe\|safe.*backup" ./CHANGELOG.md ./README.md 2>/dev/null | head -20

Repository: cortexlinux/cortex

Length of output: 44


safe_mode only affects messaging; ensure it controls actual backup/undo behavior.
The variable is set but never passed to optimizer.get_cleanup_plan() or InstallationCoordinator, and the warning message on line 243 claims "no backups" in force mode, yet no backup logic is invoked in this execution flow. Either pass safe_mode to enable the coordinator's enable_rollback parameter when in safe mode, or implement explicit backup creation before cleanup commands execute.

🤖 Prompt for AI Agents
In cortex/cli.py around lines 223 to 266, safe_mode is only used for messaging
but not passed into the cleanup flow; update the call-sites so safe_mode
actually controls backups/rollback: pass safe_mode (or its inverse force) into
optimizer.get_cleanup_plan(...) if the optimizer supports it, and when
constructing InstallationCoordinator include enable_rollback=safe_mode (or
equivalent param) so the coordinator will create backups or enable undo
behavior; if the coordinator lacks such a flag, add explicit backup
creation/registration steps before calling coordinator.execute(), and ensure the
warning text reflects the actual behavior (i.e., only show "no backups" when
safe_mode is False and backups are truly disabled).


Comment on lines +176 to 267
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

Add type hints for new CLI entrypoints (per guidelines).
At minimum: cleanup(self, args: argparse.Namespace) -> int, _cleanup_scan(self, optimizer: CleanupOptimizer) -> int, _cleanup_run(self, args: argparse.Namespace, optimizer: CleanupOptimizer) -> int.

🧰 Tools
🪛 GitHub Check: SonarCloud Code Analysis

[failure] 190-190: Refactor this method to not always return the same value.

See more on https://sonarcloud.io/project/issues?id=cortexlinux_cortex&issues=AZsR8ysN3XHNufa59wat&open=AZsR8ysN3XHNufa59wat&pullRequest=295

🤖 Prompt for AI Agents
In cortex/cli.py around lines 176 to 267, the new CLI methods lack type hints;
update the signatures to: cleanup(self, args: argparse.Namespace) -> int,
_cleanup_scan(self, optimizer: "CleanupOptimizer") -> int, and
_cleanup_run(self, args: argparse.Namespace, optimizer: "CleanupOptimizer") ->
int. Add or update imports: import argparse (or from argparse import Namespace)
and either import CleanupOptimizer or use a forward reference with a
TYPE_CHECKING block (from typing import TYPE_CHECKING; if TYPE_CHECKING: from
<module> import CleanupOptimizer) so annotations don’t cause runtime import
cycles; keep return types as int and leave existing logic unchanged.

def install(self, software: str, execute: bool = False, dry_run: bool = False):
# Validate input first
Expand Down Expand Up @@ -543,7 +632,7 @@
table.add_row("install <pkg>", "Install software")
table.add_row("history", "View history")
table.add_row("rollback <id>", "Undo installation")
table.add_row("notify", "Manage desktop notifications") # Added this line
table.add_row("notify", "Manage desktop notifications")

console.print(table)
console.print()
Expand Down Expand Up @@ -598,7 +687,7 @@
edit_pref_parser.add_argument('key', nargs='?')
edit_pref_parser.add_argument('value', nargs='?')

# --- New Notify Command ---
# --- Notify Command ---
notify_parser = subparsers.add_parser('notify', help='Manage desktop notifications')
notify_subs = notify_parser.add_subparsers(dest='notify_action', help='Notify actions')

Expand All @@ -615,6 +704,19 @@
send_parser.add_argument('--title', default='Cortex Notification')
send_parser.add_argument('--level', choices=['low', 'normal', 'critical'], default='normal')
send_parser.add_argument('--actions', nargs='*', help='Action buttons')


# --- Cleanup Command ---
cleanup_parser = subparsers.add_parser('cleanup', help='Optimize disk space')
cleanup_subs = cleanup_parser.add_subparsers(dest='cleanup_action', help='Cleanup actions')

cleanup_subs.add_parser('scan', help='Scan for cleanable items')

run_parser = cleanup_subs.add_parser('run', help='Execute cleanup')
run_parser.add_argument('--safe', action='store_true', default=True, help='Run safely (with backups)')
run_parser.add_argument('--force', action='store_true', help='Force cleanup (no backups)')
run_parser.add_argument('--yes', '-y', action='store_true', help='Skip confirmation')
run_parser.add_argument('--dry-run', action='store_true', help='Show proposed changes without executing')
# --------------------------

args = parser.parse_args()
Expand Down Expand Up @@ -642,9 +744,12 @@
return cli.check_pref(key=args.key)
elif args.command == 'edit-pref':
return cli.edit_pref(action=args.action, key=args.key, value=args.value)
# Handle the new notify command
elif args.command == 'notify':
return cli.notify(args)
# Handle new command

elif args.command == 'cleanup':
return cli.cleanup(args)
else:
parser.print_help()
return 1
Expand Down
Loading
Loading