From 26e38aea6e7df7ce47e988e9930cc3eafedb5d8e Mon Sep 17 00:00:00 2001 From: Hardik Zinzuvadiya <25708027+Z4nzu@users.noreply.github.com> Date: Sun, 15 Mar 2026 18:07:40 +0530 Subject: [PATCH] =?UTF-8?q?Feature:=20Show=20installed=20status=20(?= =?UTF-8?q?=E2=9C=94/=E2=9C=98)=20next=20to=20each=20tool?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit core.py: - Add is_installed property to HackingTool class Checks: (1) shutil.which() for binary from first RUN_COMMAND, (2) os.path.isdir() for git clone target directory Handles "cd foo && binary" and "sudo binary" patterns - Add status column to HackingToolsCollection.show_options() table ✔ (green) = installed, ✘ (dim) = not installed - Archived/back rows updated for extra column --- core.py | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/core.py b/core.py index 21d6942..2de4a44 100644 --- a/core.py +++ b/core.py @@ -1,4 +1,5 @@ import os +import shutil import sys import webbrowser from collections.abc import Callable @@ -106,6 +107,32 @@ class HackingTool: self.OPTIONS.append(("Run", self.run)) self.OPTIONS.extend(options) + @property + def is_installed(self) -> bool: + """Check if the tool's binary is on PATH or its clone dir exists.""" + if self.RUN_COMMANDS: + cmd = self.RUN_COMMANDS[0] + # Handle "cd foo && binary --help" pattern + if "&&" in cmd: + cmd = cmd.split("&&")[-1].strip() + if cmd.startswith("sudo "): + cmd = cmd[5:].strip() + binary = cmd.split()[0] if cmd else "" + if binary and binary not in (".", "echo", "cd"): + if shutil.which(binary): + return True + # Check if git clone target dir exists + if self.INSTALL_COMMANDS: + for ic in self.INSTALL_COMMANDS: + if "git clone" in ic: + parts = ic.split() + repo_url = [p for p in parts if p.startswith("http")] + if repo_url: + dirname = repo_url[0].rstrip("/").rsplit("/", 1)[-1].replace(".git", "") + if os.path.isdir(dirname): + return True + return False + def show_info(self): desc = f"[cyan]{self.DESCRIPTION}[/cyan]" if self.PROJECT_URL: @@ -293,21 +320,22 @@ class HackingToolsCollection: table = Table(title="Available Tools", box=box.SIMPLE_HEAD, show_lines=True) table.add_column("No.", justify="center", style="bold cyan", width=6) + table.add_column("", width=2) # installed indicator table.add_column("Tool", style="bold yellow", min_width=24) table.add_column("Description", style="white", overflow="fold") for index, tool in enumerate(active, start=1): desc = getattr(tool, "DESCRIPTION", "") or "—" - # Show only first line of description to keep rows compact desc = desc.splitlines()[0] if desc != "—" else "—" - table.add_row(str(index), tool.TITLE, desc) + status = "[green]✔[/green]" if tool.is_installed else "[dim]✘[/dim]" + table.add_row(str(index), status, tool.TITLE, desc) if archived: - table.add_row("[dim]98[/dim]", f"[archived]Archived tools ({len(archived)})[/archived]", "") + table.add_row("[dim]98[/dim]", "", f"[archived]Archived tools ({len(archived)})[/archived]", "") if incompatible: console.print(f"[dim]({len(incompatible)} tools hidden — not supported on current OS)[/dim]") - table.add_row("99", f"Back to {parent.TITLE if parent else 'Main Menu'}", "") + table.add_row("99", "", f"Back to {parent.TITLE if parent else 'Main Menu'}", "") console.print(table) console.print( "[dim] Enter number · [bold cyan]?[/bold cyan] help"