From 7375fcee98610de2e12df0b44e002d8db42235cb Mon Sep 17 00:00:00 2001 From: GH05TCREW Date: Sun, 14 Dec 2025 03:18:45 -0700 Subject: [PATCH] feat(runtime,prompts): discover & expose available CLI tools, detect WSL/Windows shells --- ghostcrew/agents/prompts/ghost_agent.jinja | 2 + ghostcrew/agents/prompts/ghost_assist.jinja | 2 + ghostcrew/agents/prompts/ghost_crew.jinja | 9 +- ghostcrew/runtime/runtime.py | 154 ++++++++++++++++++-- 4 files changed, 154 insertions(+), 13 deletions(-) diff --git a/ghostcrew/agents/prompts/ghost_agent.jinja b/ghostcrew/agents/prompts/ghost_agent.jinja index 465a42a..eceec6e 100644 --- a/ghostcrew/agents/prompts/ghost_agent.jinja +++ b/ghostcrew/agents/prompts/ghost_agent.jinja @@ -42,6 +42,8 @@ Do NOT skip calling finish between steps. - OS: {{ environment.os }} ({{ environment.os_version }}) - Architecture: {{ environment.architecture }} - Shell: {{ environment.shell }} +- Available CLI Tools: +{{ environment.available_tools|join(', ') }} - Output directories: - loot/notes.json (working notes) - loot/reports/ (generated reports) diff --git a/ghostcrew/agents/prompts/ghost_assist.jinja b/ghostcrew/agents/prompts/ghost_assist.jinja index 135fffa..f33eb0f 100644 --- a/ghostcrew/agents/prompts/ghost_assist.jinja +++ b/ghostcrew/agents/prompts/ghost_assist.jinja @@ -18,6 +18,8 @@ You are operating in an authorized penetration testing engagement. The user has - OS: {{ environment.os }} ({{ environment.os_version }}) - Architecture: {{ environment.architecture }} - Shell: {{ environment.shell }} +- Available CLI Tools: +{{ environment.available_tools|join(', ') }} - Output directories: - loot/notes.json (working notes) - loot/reports/ (generated reports) diff --git a/ghostcrew/agents/prompts/ghost_crew.jinja b/ghostcrew/agents/prompts/ghost_crew.jinja index c7e564e..9645186 100644 --- a/ghostcrew/agents/prompts/ghost_crew.jinja +++ b/ghostcrew/agents/prompts/ghost_crew.jinja @@ -36,9 +36,14 @@ You manage agents using these tools: - **synthesize_findings**: Compile all results into a final concise report (call this when done) {% if worker_tools %} -## Worker Agent Tools -Workers have access to: +## Worker Agent Capabilities +Workers have access to the following tools: {{ worker_tools }} + +{% if environment.available_tools %} +Workers also have access to these CLI tools via terminal: +{{ environment.available_tools }} +{% endif %} {% endif %} IMPORTANT: When spawning agents, be specific about which tool to use (e.g., "Use mcp_nmap_scan to..." or "Use mcp_metasploit_run_module to..."). Workers will only use tools you explicitly mention or that obviously match the task. diff --git a/ghostcrew/runtime/runtime.py b/ghostcrew/runtime/runtime.py index bb5e700..35b183a 100644 --- a/ghostcrew/runtime/runtime.py +++ b/ghostcrew/runtime/runtime.py @@ -1,15 +1,93 @@ """Runtime abstraction for GhostCrew.""" import platform +import shutil from abc import ABC, abstractmethod -from dataclasses import dataclass +from dataclasses import dataclass, field from pathlib import Path -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING, List, Optional if TYPE_CHECKING: from ..mcp import MCPManager +# Categorized list of tools to check for +INTERESTING_TOOLS = { + "network_scan": [ + "nmap", + "masscan", + "rustscan", + "naabu", + "unicornscan", + ], + "web_scan": [ + "nikto", + "gobuster", + "dirb", + "dirbuster", + "ffuf", + "feroxbuster", + "wpscan", + "nuclei", + "whatweb", + ], + "exploitation": [ + "msfconsole", + "searchsploit", + "sqlmap", + "commix", + ], + "password_attacks": [ + "hydra", + "medusa", + "john", + "hashcat", + "crackmapexec", + "thc-hydra", + ], + "network_analysis": [ + "tcpdump", + "tshark", + "wireshark", + "ngrep", + ], + "tunneling": [ + "proxychains", + "proxychains4", + "socat", + "chisel", + "ligolo", + ], + "utilities": [ + "curl", + "wget", + "nc", + "netcat", + "ncat", + "ssh", + "git", + "docker", + "kubectl", + "jq", + "python3", + "perl", + "ruby", + "gcc", + "g++", + "make", + ], +} + + +@dataclass +class ToolInfo: + """Information about an available tool.""" + + name: str + path: str + category: str + + @dataclass class EnvironmentInfo: """System environment information.""" @@ -18,31 +96,85 @@ class EnvironmentInfo: os_version: str shell: str # "powershell", "bash", "zsh", etc. architecture: str # "x86_64", "arm64", etc. + available_tools: List[ToolInfo] = field(default_factory=list) def __str__(self) -> str: """Concise string representation for prompts.""" - return f"{self.os} ({self.architecture}), shell: {self.shell}" + # Group tools by category for cleaner output + grouped = {} + for tool in self.available_tools: + if tool.category not in grouped: + grouped[tool.category] = [] + grouped[tool.category].append(tool.name) + + tools_str = "" + if grouped: + lines = [] + for cat, tools in grouped.items(): + lines.append(f" - {cat}: {', '.join(tools)}") + tools_str = "\n" + "\n".join(lines) + else: + tools_str = " None" + + return ( + f"{self.os} ({self.architecture}), shell: {self.shell}\n" + f"Available CLI Tools:{tools_str}" + ) def detect_environment() -> EnvironmentInfo: """Detect the current system environment.""" + import os + os_name = platform.system() os_version = platform.release() arch = platform.machine() + shell = "unknown" - # Detect shell + # Detect shell and OS nuances if os_name == "Windows": - # Check for PowerShell vs CMD - shell = "powershell" + # Better Windows shell detection + comspec = os.environ.get("COMSPEC", "").lower() + if "powershell" in comspec: + shell = "powershell" + elif "cmd.exe" in comspec: + shell = "cmd" + else: + # Fallback: check if we are in a PS session via env vars + if "PSModulePath" in os.environ: + shell = "powershell" + else: + shell = "cmd" # Default to cmd on Windows if unsure else: - # Unix-like: check common shells - import os - + # Unix-like shell_path = os.environ.get("SHELL", "/bin/sh") - shell = shell_path.split("/")[-1] # Extract shell name + shell = shell_path.split("/")[-1] + + # WSL Detection + if os_name == "Linux": + try: + with open("/proc/version", "r") as f: + if "microsoft" in f.read().lower(): + os_name = "Linux (WSL)" + except Exception: + pass + + # Detect available tools with categories + available_tools = [] + for category, tools in INTERESTING_TOOLS.items(): + for tool_name in tools: + tool_path = shutil.which(tool_name) + if tool_path: + available_tools.append( + ToolInfo(name=tool_name, path=tool_path, category=category) + ) return EnvironmentInfo( - os=os_name, os_version=os_version, shell=shell, architecture=arch + os=os_name, + os_version=os_version, + shell=shell, + architecture=arch, + available_tools=available_tools, )