From c022dc80477d7f06133ab5798409225f8fd7a88f Mon Sep 17 00:00:00 2001 From: GH05TCREW Date: Thu, 11 Dec 2025 07:02:43 -0700 Subject: [PATCH] refactor: organize loot directory with subdirectories --- ghostcrew/agents/ghostcrew_agent/system_prompt.jinja | 5 ++++- ghostcrew/interface/cli.py | 8 ++++---- ghostcrew/interface/main.py | 4 ++-- ghostcrew/interface/tui.py | 9 +++++---- ghostcrew/runtime/runtime.py | 9 ++++++--- ghostcrew/tools/notes/__init__.py | 2 +- 6 files changed, 22 insertions(+), 15 deletions(-) diff --git a/ghostcrew/agents/ghostcrew_agent/system_prompt.jinja b/ghostcrew/agents/ghostcrew_agent/system_prompt.jinja index 0c15daa..6baef90 100644 --- a/ghostcrew/agents/ghostcrew_agent/system_prompt.jinja +++ b/ghostcrew/agents/ghostcrew_agent/system_prompt.jinja @@ -21,7 +21,10 @@ The task is not complete until you explicitly call `finish`. - OS: {{ environment.os }} ({{ environment.os_version }}) - Architecture: {{ environment.architecture }} - Shell: {{ environment.shell }} -- Output: loot/ +- Output directories: + - loot/notes.json (working notes) + - loot/reports/ (generated reports) + - loot/artifacts/ (screenshots, captured files) {% endif %} {% if target %} diff --git a/ghostcrew/interface/cli.py b/ghostcrew/interface/cli.py index bd8cfd6..cc8baf0 100644 --- a/ghostcrew/interface/cli.py +++ b/ghostcrew/interface/cli.py @@ -35,7 +35,7 @@ async def run_cli( target: Target to test model: LLM model to use task: Optional task description - report: Report path ("auto" for loot/_.md) + report: Report path ("auto" for loot/reports/_.md) max_tools: Max tool calls before stopping use_docker: Run tools in Docker container """ @@ -253,11 +253,11 @@ async def run_cli( # Determine path if report == "auto": - loot_dir = Path("loot") - loot_dir.mkdir(exist_ok=True) + reports_dir = Path("loot/reports") + reports_dir.mkdir(parents=True, exist_ok=True) safe_target = target.replace("://", "_").replace("/", "_").replace(":", "_") timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - report_path = loot_dir / f"{safe_target}_{timestamp}.md" + report_path = reports_dir / f"{safe_target}_{timestamp}.md" else: report_path = Path(report) report_path.parent.mkdir(parents=True, exist_ok=True) diff --git a/ghostcrew/interface/main.py b/ghostcrew/interface/main.py index 4c107df..53edc09 100644 --- a/ghostcrew/interface/main.py +++ b/ghostcrew/interface/main.py @@ -75,13 +75,13 @@ Examples: # Task for non-interactive mode parser.add_argument("--task", help="Task to run in non-interactive mode") - # Report output (saves to loot/ by default) + # Report output (saves to loot/reports/ by default) parser.add_argument( "--report", "-r", nargs="?", const="auto", - help="Generate report (default: loot/_.md)", + help="Generate report (default: loot/reports/_.md)", ) # Max tool calls limit diff --git a/ghostcrew/interface/tui.py b/ghostcrew/interface/tui.py index 1cc3978..467dfb2 100644 --- a/ghostcrew/interface/tui.py +++ b/ghostcrew/interface/tui.py @@ -926,6 +926,7 @@ class GhostCrewTUI(App): else: lines.append(f"[{key}] {value}") lines.append("\nFile: loot/notes.json") + lines.append("Reports: loot/reports/") self._add_system("\n".join(lines)) @@ -1080,12 +1081,12 @@ Be concise. Use the actual data from notes.""" ) return - # Save to loot/ - loot_dir = Path("loot") - loot_dir.mkdir(exist_ok=True) + # Save to loot/reports/ + reports_dir = Path("loot/reports") + reports_dir.mkdir(parents=True, exist_ok=True) timestamp = datetime.now().strftime("%Y-%m-%d_%H%M%S") - report_path = loot_dir / f"report_{timestamp}.md" + report_path = reports_dir / f"report_{timestamp}.md" report_path.write_text(report_content, encoding="utf-8") self._add_system(f"+ Report saved: {report_path}") diff --git a/ghostcrew/runtime/runtime.py b/ghostcrew/runtime/runtime.py index 33ce3c0..4e6a493 100644 --- a/ghostcrew/runtime/runtime.py +++ b/ghostcrew/runtime/runtime.py @@ -174,8 +174,11 @@ class LocalRuntime(Runtime): async def start(self): """Start the local runtime.""" self._running = True - # Create loot directory for scan output + # Create organized loot directory structure Path("loot").mkdir(exist_ok=True) + Path("loot/reports").mkdir(exist_ok=True) + Path("loot/artifacts").mkdir(exist_ok=True) + Path("loot/artifacts/screenshots").mkdir(exist_ok=True) async def stop(self): """Stop the local runtime gracefully.""" @@ -315,8 +318,8 @@ class LocalRuntime(Runtime): kwargs["url"], timeout=timeout, wait_until="domcontentloaded" ) - # Save screenshot to loot directory - output_dir = Path("loot/screenshots") + # Save screenshot to loot/artifacts/screenshots/ + output_dir = Path("loot/artifacts/screenshots") output_dir.mkdir(parents=True, exist_ok=True) filename = f"screenshot_{int(__import__('time').time())}.png" diff --git a/ghostcrew/tools/notes/__init__.py b/ghostcrew/tools/notes/__init__.py index c8212aa..8cb5386 100644 --- a/ghostcrew/tools/notes/__init__.py +++ b/ghostcrew/tools/notes/__init__.py @@ -6,7 +6,7 @@ from typing import Dict from ..registry import ToolSchema, register_tool -# Notes storage +# Notes storage - kept at loot root for easy access _notes: Dict[str, str] = {} _notes_file: Path = Path("loot/notes.json")