From 22de8f043c10a6a5267b7776b22538bfc2aa1c16 Mon Sep 17 00:00:00 2001 From: daymade Date: Sun, 5 Apr 2026 13:32:54 +0800 Subject: [PATCH] Fix claude-code-history-files-finder: preserve directory structure on recovery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, recover_content.py saved all files flat in the output directory, causing files with the same name (e.g., src/utils.py and tests/utils.py) to overwrite each other. Now the script preserves the original directory structure, creating subdirectories as needed within the output directory. - Bump version: 1.0.0 → 1.0.1 Co-Authored-By: Claude Sonnet 4.6 --- .claude-plugin/marketplace.json | 2 +- .../scripts/recover_content.py | 35 ++++++++++++++++--- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 4bf5c64..d842301 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -378,7 +378,7 @@ "description": "Find and recover content from Claude Code session history files. Use when searching for deleted files, tracking changes across sessions, analyzing conversation history, or recovering code/documents from previous Claude interactions. Triggers include mentions of session history, recover deleted, find in history, previous conversation, or .claude/projects", "source": "./", "strict": false, - "version": "1.0.0", + "version": "1.0.1", "category": "developer-tools", "keywords": [ "session-history", diff --git a/claude-code-history-files-finder/scripts/recover_content.py b/claude-code-history-files-finder/scripts/recover_content.py index e7e6945..3b16958 100755 --- a/claude-code-history-files-finder/scripts/recover_content.py +++ b/claude-code-history-files-finder/scripts/recover_content.py @@ -120,7 +120,7 @@ class SessionContentRecovery: self, write_calls: List[Dict[str, Any]], keywords: Optional[List[str]] = None ) -> List[Dict[str, Any]]: """ - Save recovered files to disk. + Save recovered files to disk, preserving original directory structure. Args: write_calls: List of Write tool calls @@ -152,18 +152,43 @@ class SessionContentRecovery: # Save files for file_path, call in files_by_path.items(): try: - filename = Path(file_path).name - if not filename: + if not file_path: continue - output_file = self.output_dir / filename + # Preserve original directory structure + # Convert absolute path to relative path within output directory + original_path = Path(file_path) + + # Handle absolute paths: extract meaningful relative path + # e.g., /Users/username/project/src/file.py -> src/file.py + # e.g., /home/user/workspace/project/lib/module.py -> lib/module.py + path_parts = original_path.parts + if len(path_parts) > 1 and path_parts[0] == "/": + # For absolute paths, try to find a project-like directory + # Skip leading /, Users/username, home/username patterns + start_idx = 1 # Skip leading "/" + if len(path_parts) > 2 and path_parts[1].lower() in ("users", "home", "user"): + start_idx = 3 # Skip /Users/username or /home/user + relative_parts = path_parts[start_idx:] + else: + relative_parts = path_parts + + # Construct output path preserving structure + if relative_parts: + output_file = self.output_dir.joinpath(*relative_parts) + else: + # Fallback to filename only if path is too shallow + output_file = self.output_dir / original_path.name + + # Create parent directories + output_file.parent.mkdir(parents=True, exist_ok=True) with open(output_file, "w") as f: f.write(call["content"]) saved.append( { - "file": filename, + "file": output_file.name, "original_path": file_path, "size": len(call["content"]), "lines": call["content"].count("\n") + 1,