Commit 30c4fce6 authored by Hardik Zinzuvadiya's avatar Hardik Zinzuvadiya
Browse files

Feature: Open Folder option to manually access tool directory

HackingTool:
- Add _get_tool_dir() — finds the tool's local directory by checking:
  1. Git clone target dir (from INSTALL_COMMANDS)
  2. "cd foo && bar" pattern in RUN_COMMANDS
  3. Binary location via shutil.which() → dirname
- Add open_folder() — opens the tool's directory in a new shell ($SHELL)
  so the user can inspect files, run manual install steps, or debug
  If dir not found: shows a helpful message with git clone command
- Added as option 4 in every tool menu (Install, Run, Update, Open Folder)
- Updated inline help to mention the tool menu options
parent d22a941a
Loading
Loading
Loading
Loading
+51 −0
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ def _show_inline_help():
            ("  ─────────────────────────────────\n", "dim"),
            ("  1–N    ", "bold cyan"), ("select item\n", "white"),
            ("  97     ", "bold cyan"), ("install all (in category)\n", "white"),
            ("\n  Tool menu: Install, Run, Update, Open Folder\n", "dim"),
            ("  99     ", "bold cyan"), ("go back\n", "white"),
            ("  98     ", "bold cyan"), ("open project page / archived\n", "white"),
            ("  ?      ", "bold cyan"), ("show this help\n", "white"),
@@ -110,6 +111,7 @@ class HackingTool:
        if runnable:
            self.OPTIONS.append(("Run", self.run))
        self.OPTIONS.append(("Update", self.update))
        self.OPTIONS.append(("Open Folder", self.open_folder))
        self.OPTIONS.extend(options)

    @property
@@ -269,6 +271,55 @@ class HackingTool:
        else:
            console.print("[dim]No automatic update method available for this tool.[/dim]")

    def _get_tool_dir(self) -> str | None:
        """Find the tool's local directory — clone target, pip location, or binary path."""
        # 1. Check git clone target dir
        for ic in (self.INSTALL_COMMANDS or []):
            if "git clone" in ic:
                parts = ic.split()
                # If last arg is not a URL, it's a custom dir name
                repo_urls = [p for p in parts if p.startswith("http")]
                if repo_urls:
                    dirname = repo_urls[0].rstrip("/").rsplit("/", 1)[-1].replace(".git", "")
                    # Check custom target dir (arg after URL)
                    url_idx = parts.index(repo_urls[0])
                    if url_idx + 1 < len(parts):
                        dirname = parts[url_idx + 1]
                    if os.path.isdir(dirname):
                        return os.path.abspath(dirname)

        # 2. Check binary location via which
        if self.RUN_COMMANDS:
            cmd = self.RUN_COMMANDS[0]
            if "&&" in cmd:
                # "cd foo && bar" → check "foo"
                cd_part = cmd.split("&&")[0].strip()
                if cd_part.startswith("cd "):
                    d = cd_part[3:].strip()
                    if os.path.isdir(d):
                        return os.path.abspath(d)
            binary = cmd.split()[0] if cmd else ""
            if binary.startswith("sudo"):
                binary = cmd.split()[1] if len(cmd.split()) > 1 else ""
            path = shutil.which(binary) if binary else None
            if path:
                return os.path.dirname(os.path.realpath(path))

        return None

    def open_folder(self):
        """Open the tool's directory in a new shell so the user can work manually."""
        tool_dir = self._get_tool_dir()
        if tool_dir:
            console.print(f"[success]Opening folder: {tool_dir}[/success]")
            console.print("[dim]Type 'exit' to return to hackingtool.[/dim]")
            os.system(f'cd "{tool_dir}" && $SHELL')
        else:
            console.print("[warning]Tool directory not found.[/warning]")
            if self.PROJECT_URL:
                console.print(f"[dim]You can clone it manually:[/dim]")
                console.print(f"[cyan]  git clone {self.PROJECT_URL}.git[/cyan]")

    def before_run(self): pass

    def run(self):