#!/usr/bin/env python3 """ Claude Context Import Script Command-line tool to bulk import conversation contexts from Claude project folders. Usage: python scripts/import-claude-context.py --folder "C:/Users/MikeSwanson/claude-projects" --dry-run python scripts/import-claude-context.py --folder "C:/Users/MikeSwanson/claude-projects" --execute """ import argparse import json import os import sys from pathlib import Path import requests from dotenv import load_dotenv def load_jwt_token() -> str: """ Load JWT token from .claude/context-recall-config.env Returns: JWT token string Raises: SystemExit: If token cannot be loaded """ # Try multiple possible locations possible_paths = [ Path(".claude/context-recall-config.env"), Path("D:/ClaudeTools/.claude/context-recall-config.env"), Path(__file__).parent.parent / ".claude" / "context-recall-config.env", ] for env_path in possible_paths: if env_path.exists(): load_dotenv(env_path) token = os.getenv("JWT_TOKEN") if token: print(f"[OK] Loaded JWT token from {env_path}") return token print("[ERROR] Could not find JWT_TOKEN in .claude/context-recall-config.env") print("\nTried locations:") for path in possible_paths: print(f" - {path} ({'exists' if path.exists() else 'not found'})") print("\nPlease create .claude/context-recall-config.env with:") print(" JWT_TOKEN=your_token_here") sys.exit(1) def get_api_base_url() -> str: """ Get API base URL from environment or use default. Returns: API base URL string """ return os.getenv("API_BASE_URL", "http://localhost:8000") def call_bulk_import_api( folder_path: str, jwt_token: str, dry_run: bool = True, project_id: str = None, session_id: str = None, ) -> dict: """ Call the bulk import API endpoint. Args: folder_path: Path to folder containing Claude conversations jwt_token: JWT authentication token dry_run: Preview mode without saving project_id: Optional project ID to associate contexts with session_id: Optional session ID to associate contexts with Returns: API response dictionary Raises: requests.exceptions.RequestException: If API call fails """ api_url = f"{get_api_base_url()}/api/bulk-import/import-folder" headers = { "Authorization": f"Bearer {jwt_token}", "Content-Type": "application/json", } params = { "folder_path": folder_path, "dry_run": dry_run, } if project_id: params["project_id"] = project_id if session_id: params["session_id"] = session_id print(f"\n[API] Calling: {api_url}") print(f" Mode: {'DRY RUN' if dry_run else 'EXECUTE'}") print(f" Folder: {folder_path}") response = requests.post(api_url, headers=headers, params=params, timeout=300) response.raise_for_status() return response.json() def display_progress(result: dict): """ Display import progress and results. Args: result: API response dictionary """ print("\n" + "=" * 70) print("IMPORT RESULTS") print("=" * 70) # Summary print(f"\n{result.get('summary', 'No summary available')}") # Statistics print(f"\n[STATS]") print(f" Files scanned: {result.get('files_scanned', 0)}") print(f" Files processed: {result.get('files_processed', 0)}") print(f" Contexts created: {result.get('contexts_created', 0)}") print(f" Errors: {len(result.get('errors', []))}") # Context preview contexts_preview = result.get("contexts_preview", []) if contexts_preview: print(f"\n[PREVIEW] Contexts (showing {min(5, len(contexts_preview))} of {len(contexts_preview)}):") for i, ctx in enumerate(contexts_preview[:5], 1): print(f"\n {i}. {ctx.get('title', 'Untitled')}") print(f" Type: {ctx.get('type', 'unknown')}") print(f" Messages: {ctx.get('message_count', 0)}") print(f" Tags: {', '.join(ctx.get('tags', []))}") print(f" Relevance: {ctx.get('relevance_score', 0.0):.1f}/10.0") # Errors errors = result.get("errors", []) if errors: print(f"\n[WARNING] Errors ({len(errors)}):") for i, error in enumerate(errors[:5], 1): print(f"\n {i}. File: {error.get('file', 'unknown')}") print(f" Error: {error.get('error', 'unknown error')}") if len(errors) > 5: print(f"\n ... and {len(errors) - 5} more errors") print("\n" + "=" * 70) def main(): """Main entry point for the import script.""" parser = argparse.ArgumentParser( description="Import Claude conversation contexts from project folders", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: # Preview import without saving python scripts/import-claude-context.py --folder "C:\\Users\\MikeSwanson\\claude-projects" --dry-run # Execute import and save to database python scripts/import-claude-context.py --folder "C:\\Users\\MikeSwanson\\claude-projects" --execute # Associate with a specific project python scripts/import-claude-context.py --folder "C:\\Users\\MikeSwanson\\claude-projects" --execute --project-id abc-123 """ ) parser.add_argument( "--folder", required=True, help="Path to Claude projects folder containing .jsonl conversation files" ) mode_group = parser.add_mutually_exclusive_group(required=True) mode_group.add_argument( "--dry-run", action="store_true", help="Preview import without saving to database" ) mode_group.add_argument( "--execute", action="store_true", help="Execute import and save to database" ) parser.add_argument( "--project-id", help="Associate all imported contexts with this project ID" ) parser.add_argument( "--session-id", help="Associate all imported contexts with this session ID" ) parser.add_argument( "--api-url", help="API base URL (default: http://localhost:8000)" ) args = parser.parse_args() # Set API URL if provided if args.api_url: os.environ["API_BASE_URL"] = args.api_url # Validate folder path folder_path = Path(args.folder) if not folder_path.exists(): print(f"[ERROR] Folder does not exist: {folder_path}") sys.exit(1) print("=" * 70) print("CLAUDE CONTEXT IMPORT TOOL") print("=" * 70) # Load JWT token try: jwt_token = load_jwt_token() except Exception as e: print(f"[ERROR] Error loading JWT token: {e}") sys.exit(1) # Determine mode dry_run = args.dry_run # Call API try: result = call_bulk_import_api( folder_path=str(folder_path), jwt_token=jwt_token, dry_run=dry_run, project_id=args.project_id, session_id=args.session_id, ) # Display results display_progress(result) # Success message if dry_run: print("\n[SUCCESS] Dry run completed successfully!") print(" Run with --execute to save contexts to database") else: print(f"\n[SUCCESS] Import completed successfully!") print(f" Created {result.get('contexts_created', 0)} contexts") sys.exit(0) except requests.exceptions.HTTPError as e: print(f"\n[ERROR] API Error: {e}") if e.response is not None: try: error_detail = e.response.json() print(f" Detail: {error_detail.get('detail', 'No details available')}") except: print(f" Response: {e.response.text}") sys.exit(1) except requests.exceptions.RequestException as e: print(f"\n[ERROR] Network Error: {e}") print(" Make sure the API server is running") sys.exit(1) except Exception as e: print(f"\n[ERROR] Unexpected Error: {e}") import traceback traceback.print_exc() sys.exit(1) if __name__ == "__main__": main()