From 043fa0e7b712e264c126a5ca918fe22dd84c1df2 Mon Sep 17 00:00:00 2001 From: Hardik Zinzuvadiya <25708027+Z4nzu@users.noreply.github.com> Date: Sun, 15 Mar 2026 18:05:11 +0530 Subject: [PATCH] Feature: Search tools by name or keyword (/ or s) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add _collect_all_tools() — walks all collections recursively and returns (tool_instance, category_name) pairs for 185 tools - Add search_tools() — prompts for query, matches against TITLE and DESCRIPTION (case-insensitive), shows results table with category, user selects a result number to jump directly into tool.show_options() - Wire / and s commands into interact_menu() - Add / search to help overlay and hint bar - Fix ToolManager row number: was hardcoded 18, now computed dynamically from len(categories) + 1 (currently 21) --- hackingtool.py | 85 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/hackingtool.py b/hackingtool.py index 4cdd4dc..0652f8f 100755 --- a/hackingtool.py +++ b/hackingtool.py @@ -121,6 +121,7 @@ def show_help(): (" ─────────────────────────────────────\n", "dim"), (" 1–20 ", "bold cyan"), ("open a category\n", "white"), (" 21 ", "bold cyan"), ("Update / Uninstall hackingtool\n", "white"), + (" / or s ", "bold cyan"), ("search tools by name or keyword\n", "white"), (" ? ", "bold cyan"), ("show this help\n", "white"), (" q ", "bold cyan"), ("quit hackingtool\n\n", "white"), (" Inside a category\n", "bold white"), @@ -305,20 +306,96 @@ def build_menu(): )) # ── ToolManager row ── + tm_num = len(categories) + 1 console.print( - f" [bold magenta] 18[/bold magenta] {update_def[1]} " + f" [bold magenta] {tm_num}[/bold magenta] {update_def[1]} " f"[magenta]{update_def[2]}[/magenta]" ) # ── Hint bar ── console.print(Rule(style="dim magenta")) console.print( - " [dim]Enter number to open · " + " [dim]Enter number · " + "[bold cyan]/[/bold cyan] search · " "[bold cyan]?[/bold cyan] help · " "[bold cyan]q[/bold cyan] quit[/dim]\n" ) +# ── Search ───────────────────────────────────────────────────────────────────── + +def _collect_all_tools() -> list[tuple]: + """Walk all collections and return (tool_instance, category_name) pairs.""" + from core import HackingTool, HackingToolsCollection + results = [] + + def _walk(items, parent_title=""): + for item in items: + if isinstance(item, HackingToolsCollection): + _walk(item.TOOLS, item.TITLE) + elif isinstance(item, HackingTool): + results.append((item, parent_title)) + + _walk(all_tools) + return results + + +def search_tools(): + """Interactive search — user types query, results update, select to jump.""" + query = Prompt.ask("[bold cyan]/ Search[/bold cyan]", default="").strip().lower() + if not query: + return + + all_tool_list = _collect_all_tools() + + # Match against title + description + matches = [] + for tool, category in all_tool_list: + title = (tool.TITLE or "").lower() + desc = (tool.DESCRIPTION or "").lower() + if query in title or query in desc: + matches.append((tool, category)) + + if not matches: + console.print(f"[dim]No tools found matching '{query}'[/dim]") + Prompt.ask("[dim]Press Enter to return[/dim]", default="") + return + + # Display results + table = Table( + title=f"Search results for '{query}'", + box=box.SIMPLE_HEAD, show_lines=True, + ) + table.add_column("No.", justify="center", style="bold cyan", width=5) + table.add_column("Tool", style="bold yellow", min_width=20) + table.add_column("Category", style="magenta", min_width=15) + table.add_column("Description", style="white", overflow="fold") + + for i, (tool, cat) in enumerate(matches, start=1): + desc = (tool.DESCRIPTION or "—").splitlines()[0] + table.add_row(str(i), tool.TITLE, cat, desc) + + table.add_row("99", "Back to main menu", "", "") + console.print(table) + + raw = Prompt.ask("[bold cyan]>[/bold cyan]", default="").strip().lower() + if not raw or raw == "99": + return + + try: + idx = int(raw) + except ValueError: + return + + if 1 <= idx <= len(matches): + tool, cat = matches[idx - 1] + console.print(Panel( + f"[bold magenta]{tool.TITLE}[/bold magenta] [dim]({cat})[/dim]", + border_style="magenta", box=box.ROUNDED, + )) + tool.show_options() + + # ── Main interaction loop ────────────────────────────────────────────────────── def interact_menu(): @@ -334,6 +411,10 @@ def interact_menu(): show_help() continue + if raw.startswith("/") or raw in ("s", "search"): + search_tools() + continue + if raw in ("q", "quit", "exit"): console.print(Panel( "[bold white on magenta] Goodbye — Come Back Safely [/bold white on magenta]",