Feature: Show installed status (✔/✘) next to each tool

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
This commit is contained in:
Hardik Zinzuvadiya 2026-03-15 18:07:40 +05:30
parent 043fa0e7b7
commit 26e38aea6e

36
core.py
View file

@ -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"