diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..365d7be --- /dev/null +++ b/.env.example @@ -0,0 +1,9 @@ +# NotebookLM Skill - Environment Variables +# This skill primarily uses browser-based Google authentication (no API keys needed). +# The following are optional configuration overrides. + +# Browser configuration (optional) +# PLAYWRIGHT_BROWSERS_PATH= # Custom path for Playwright browser binaries + +# Debug mode (optional) +# NOTEBOOKLM_DEBUG=false diff --git a/.gitignore b/.gitignore index 4d7e1c3..01c75f1 100755 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ scripts/*.pyc # Environment .env *.env +!.env.example .env.* # Browser/Auth state (if accidentally placed outside data/) diff --git a/README.md b/README.md index 0a46e9c..1db834f 100755 --- a/README.md +++ b/README.md @@ -403,6 +403,18 @@ git clone https://github.com/PleasePrompto/notebooklm-skill notebooklm --- +## Troubleshooting + +| Issue | Solution | +|-------|----------| +| Browser doesn't open | Ensure Chrome/Chromium is installed and `BROWSER_PATH` is set if non-standard | +| Auth fails repeatedly | Delete `~/.claude/skills/notebooklm/.browser_data` and re-authenticate | +| "Skill not found" | Verify the clone is at `~/.claude/skills/notebooklm/` (not nested) | +| Timeout on queries | Increase timeout in config; long documents may need 120s+ | +| Running in web UI | This skill requires **local** Claude Code — web UI sandboxes block browser automation | + +--- +
Built as a Claude Code Skill adaptation of my [NotebookLM MCP Server](https://github.com/PleasePrompto/notebooklm-mcp) diff --git a/scripts/__init__.py b/scripts/__init__.py index e77fffc..b5ebe82 100755 --- a/scripts/__init__.py +++ b/scripts/__init__.py @@ -33,9 +33,9 @@ def ensure_venv_and_run(): # We need to set up or switch to our venv if not venv_dir.exists(): - print("šŸ”§ First-time setup detected...") - print(" Creating isolated environment for NotebookLM skill...") - print(" This ensures clean dependency management...") + print("[SETUP] First-time setup detected...") + print(" Creating isolated environment for NotebookLM skill...") + print(" This ensures clean dependency management...") # Create venv import venv @@ -68,11 +68,11 @@ def ensure_venv_and_run(): capture_output=True ) - print("āœ… Environment ready! All dependencies isolated in .venv/") + print("[OK] Environment ready! All dependencies isolated in .venv/") # If we're here and not in the venv, we should recommend using the venv if not in_venv: - print("\nāš ļø Running outside virtual environment") + print("\n[WARN] Running outside virtual environment") print(" Recommended: Use scripts/run.py to ensure clean execution") print(" Or activate: source .venv/bin/activate") diff --git a/scripts/ask_question.py b/scripts/ask_question.py index aa47e4b..8f29b22 100755 --- a/scripts/ask_question.py +++ b/scripts/ask_question.py @@ -52,11 +52,11 @@ def ask_notebooklm(question: str, notebook_url: str, headless: bool = True) -> s auth = AuthManager() if not auth.is_authenticated(): - print("āš ļø Not authenticated. Run: python auth_manager.py setup") + print("[WARN] Not authenticated. Run: python auth_manager.py setup") return None - print(f"šŸ’¬ Asking: {question}") - print(f"šŸ“š Notebook: {notebook_url}") + print(f"[ASK] Asking: {question}") + print(f"[NOTEBOOK] Notebook: {notebook_url}") playwright = None context = None @@ -73,14 +73,14 @@ def ask_notebooklm(question: str, notebook_url: str, headless: bool = True) -> s # Navigate to notebook page = context.new_page() - print(" 🌐 Opening notebook...") + print(" [OPEN] Opening notebook...") page.goto(notebook_url, wait_until="domcontentloaded") # Wait for NotebookLM page.wait_for_url(re.compile(r"^https://notebooklm\.google\.com/"), timeout=10000) # Wait for query input (MCP approach) - print(" ā³ Waiting for query input...") + print(" [WAIT] Waiting for query input...") query_element = None for selector in QUERY_INPUT_SELECTORS: @@ -91,31 +91,31 @@ def ask_notebooklm(question: str, notebook_url: str, headless: bool = True) -> s state="visible" # Only check visibility, not disabled! ) if query_element: - print(f" āœ“ Found input: {selector}") + print(f" [OK] Found input: {selector}") break except: continue if not query_element: - print(" āŒ Could not find query input") + print(" [ERROR] Could not find query input") return None # Type question (human-like, fast) - print(" ā³ Typing question...") + print(" [WAIT] Typing question...") # Use primary selector for typing input_selector = QUERY_INPUT_SELECTORS[0] StealthUtils.human_type(page, input_selector, question) # Submit - print(" šŸ“¤ Submitting...") + print(" [SUBMIT] Submitting...") page.keyboard.press("Enter") # Small pause StealthUtils.random_delay(500, 1500) # Wait for response (MCP approach: poll for stable text) - print(" ā³ Waiting for answer...") + print(" [WAIT] Waiting for answer...") answer = None stable_count = 0 @@ -159,15 +159,15 @@ def ask_notebooklm(question: str, notebook_url: str, headless: bool = True) -> s time.sleep(1) if not answer: - print(" āŒ Timeout waiting for answer") + print(" [ERROR] Timeout waiting for answer") return None - print(" āœ… Got answer!") + print(" [OK] Got answer!") # Add follow-up reminder to encourage Claude to ask more questions return answer + FOLLOW_UP_REMINDER except Exception as e: - print(f" āŒ Error: {e}") + print(f" [ERROR] {e}") import traceback traceback.print_exc() return None @@ -206,7 +206,7 @@ def main(): if notebook: notebook_url = notebook['url'] else: - print(f"āŒ Notebook '{args.notebook_id}' not found") + print(f"[ERROR] Notebook '{args.notebook_id}' not found") return 1 if not notebook_url: @@ -215,19 +215,19 @@ def main(): active = library.get_active_notebook() if active: notebook_url = active['url'] - print(f"šŸ“š Using active notebook: {active['name']}") + print(f"[NOTEBOOK] Using active notebook: {active['name']}") else: # Show available notebooks notebooks = library.list_notebooks() if notebooks: - print("\nšŸ“š Available notebooks:") + print("\n[NOTEBOOKS] Available notebooks:") for nb in notebooks: mark = " [ACTIVE]" if nb.get('id') == library.active_notebook_id else "" - print(f" {nb['id']}: {nb['name']}{mark}") + print(f" {nb['id']}: {nb['name']}{mark}") print("\nSpecify with --notebook-id or set active:") print("python scripts/run.py notebook_manager.py activate --id ID") else: - print("āŒ No notebooks in library. Add one first:") + print("[ERROR] No notebooks in library. Add one first:") print("python scripts/run.py notebook_manager.py add --url URL --name NAME --description DESC --topics TOPICS") return 1 @@ -248,7 +248,7 @@ def main(): print("=" * 60) return 0 else: - print("\nāŒ Failed to get answer") + print("\n[ERROR] Failed to get answer") return 1 diff --git a/scripts/auth_manager.py b/scripts/auth_manager.py index 54c8b3b..200b80c 100755 --- a/scripts/auth_manager.py +++ b/scripts/auth_manager.py @@ -57,7 +57,7 @@ def is_authenticated(self) -> bool: # Check if state file is not too old (7 days) age_days = (time.time() - self.state_file.stat().st_mtime) / 86400 if age_days > 7: - print(f"āš ļø Browser state is {age_days:.1f} days old, may need re-authentication") + print(f"[WARN] Browser state is {age_days:.1f} days old, may need re-authentication") return True @@ -94,8 +94,8 @@ def setup_auth(self, headless: bool = False, timeout_minutes: int = 10) -> bool: Returns: True if authentication successful """ - print("šŸ” Starting authentication setup...") - print(f" Timeout: {timeout_minutes} minutes") + print("[AUTH] Starting authentication setup...") + print(f" Timeout: {timeout_minutes} minutes") playwright = None context = None @@ -115,20 +115,20 @@ def setup_auth(self, headless: bool = False, timeout_minutes: int = 10) -> bool: # Check if already authenticated if "notebooklm.google.com" in page.url and "accounts.google.com" not in page.url: - print(" āœ… Already authenticated!") + print(" [OK] Already authenticated!") self._save_browser_state(context) return True # Wait for manual login - print("\n ā³ Please log in to your Google account...") - print(f" ā±ļø Waiting up to {timeout_minutes} minutes for login...") + print("\n [WAIT] Please log in to your Google account...") + print(f" [WAIT] Waiting up to {timeout_minutes} minutes for login...") try: # Wait for URL to change to NotebookLM (regex ensures it's the actual domain, not a parameter) timeout_ms = int(timeout_minutes * 60 * 1000) page.wait_for_url(re.compile(r"^https://notebooklm\.google\.com/"), timeout=timeout_ms) - print(f" āœ… Login successful!") + print(f" [OK] Login successful!") # Save authentication state self._save_browser_state(context) @@ -136,11 +136,11 @@ def setup_auth(self, headless: bool = False, timeout_minutes: int = 10) -> bool: return True except Exception as e: - print(f" āŒ Authentication timeout: {e}") + print(f" [ERROR] Authentication timeout: {e}") return False except Exception as e: - print(f" āŒ Error: {e}") + print(f" [ERROR] {e}") return False finally: @@ -162,9 +162,9 @@ def _save_browser_state(self, context: BrowserContext): try: # Save storage state (cookies, localStorage) context.storage_state(path=str(self.state_file)) - print(f" šŸ’¾ Saved browser state to: {self.state_file}") + print(f" [SAVE] Saved browser state to: {self.state_file}") except Exception as e: - print(f" āŒ Failed to save browser state: {e}") + print(f" [ERROR] Failed to save browser state: {e}") raise def _save_auth_info(self): @@ -186,29 +186,29 @@ def clear_auth(self) -> bool: Returns: True if cleared successfully """ - print("šŸ—‘ļø Clearing authentication data...") + print("[CLEAR] Clearing authentication data...") try: # Remove browser state if self.state_file.exists(): self.state_file.unlink() - print(" āœ… Removed browser state") + print(" [OK] Removed browser state") # Remove auth info if self.auth_info_file.exists(): self.auth_info_file.unlink() - print(" āœ… Removed auth info") + print(" [OK] Removed auth info") # Clear entire browser state directory if self.browser_state_dir.exists(): shutil.rmtree(self.browser_state_dir) self.browser_state_dir.mkdir(parents=True, exist_ok=True) - print(" āœ… Cleared browser data") + print(" [OK] Cleared browser data") return True except Exception as e: - print(f" āŒ Error clearing auth: {e}") + print(f" [ERROR] Error clearing auth: {e}") return False def re_auth(self, headless: bool = False, timeout_minutes: int = 10) -> bool: @@ -222,7 +222,7 @@ def re_auth(self, headless: bool = False, timeout_minutes: int = 10) -> bool: Returns: True if successful """ - print("šŸ”„ Starting re-authentication...") + print("[REAUTH] Starting re-authentication...") # Clear existing auth self.clear_auth() @@ -241,7 +241,7 @@ def validate_auth(self) -> bool: if not self.is_authenticated(): return False - print("šŸ” Validating authentication...") + print("[VALIDATE] Validating authentication...") playwright = None context = None @@ -261,14 +261,14 @@ def validate_auth(self) -> bool: # Check if we can access NotebookLM if "notebooklm.google.com" in page.url and "accounts.google.com" not in page.url: - print(" āœ… Authentication is valid") + print(" [OK] Authentication is valid") return True else: - print(" āŒ Authentication is invalid (redirected to login)") + print(" [ERROR] Authentication is invalid (redirected to login)") return False except Exception as e: - print(f" āŒ Validation failed: {e}") + print(f" [ERROR] Validation failed: {e}") return False finally: @@ -316,21 +316,21 @@ def main(): # Execute command if args.command == 'setup': if auth.setup_auth(headless=args.headless, timeout_minutes=args.timeout): - print("\nāœ… Authentication setup complete!") + print("\n[OK] Authentication setup complete!") print("You can now use ask_question.py to query NotebookLM") else: - print("\nāŒ Authentication setup failed") + print("\n[ERROR] Authentication setup failed") exit(1) elif args.command == 'status': info = auth.get_auth_info() - print("\nšŸ” Authentication Status:") - print(f" Authenticated: {'Yes' if info['authenticated'] else 'No'}") + print("\n[STATUS] Authentication Status:") + print(f" Authenticated: {'Yes' if info['authenticated'] else 'No'}") if info.get('state_age_hours'): - print(f" State age: {info['state_age_hours']:.1f} hours") + print(f" State age: {info['state_age_hours']:.1f} hours") if info.get('authenticated_at_iso'): - print(f" Last auth: {info['authenticated_at_iso']}") - print(f" State file: {info['state_file']}") + print(f" Last auth: {info['authenticated_at_iso']}") + print(f" State file: {info['state_file']}") elif args.command == 'validate': if auth.validate_auth(): @@ -345,9 +345,9 @@ def main(): elif args.command == 'reauth': if auth.re_auth(timeout_minutes=args.timeout): - print("\nāœ… Re-authentication complete!") + print("\n[OK] Re-authentication complete!") else: - print("\nāŒ Re-authentication failed") + print("\n[ERROR] Re-authentication failed") exit(1) else: diff --git a/scripts/browser_session.py b/scripts/browser_session.py index b121af8..e2fcda6 100755 --- a/scripts/browser_session.py +++ b/scripts/browser_session.py @@ -50,11 +50,11 @@ def __init__(self, session_id: str, context: BrowserContext, notebook_url: str): def _initialize(self): """Initialize the browser session and navigate to NotebookLM""" - print(f"šŸš€ Creating session {self.id}...") + print(f"[SESSION] Creating session {self.id}...") # Create new page (tab) in context self.page = self.context.new_page() - print(f" 🌐 Navigating to NotebookLM...") + print(f" [NAV] Navigating to NotebookLM...") try: # Navigate to notebook @@ -71,10 +71,10 @@ def _initialize(self): self.stealth.random_mouse_movement(self.page) self.stealth.random_delay(300, 600) - print(f"āœ… Session {self.id} ready!") + print(f" [OK] Session {self.id} ready!") except Exception as e: - print(f"āŒ Failed to initialize session: {e}") + print(f" [ERROR] Failed to initialize session: {e}") if self.page: self.page.close() raise @@ -102,7 +102,7 @@ def ask(self, question: str) -> Dict[str, Any]: self.last_activity = time.time() self.message_count += 1 - print(f"šŸ’¬ [{self.id}] Asking: {question}") + print(f"[ASK] [{self.id}] Asking: {question}") # Snapshot current answer to detect new response previous_answer = self._snapshot_latest_response() @@ -126,7 +126,7 @@ def ask(self, question: str) -> Dict[str, Any]: self.page.keyboard.press("Enter") # Wait for response - print(" ā³ Waiting for response...") + print(" [WAIT] Waiting for response...") self.stealth.random_delay(1500, 3000) # Get new answer @@ -135,7 +135,7 @@ def ask(self, question: str) -> Dict[str, Any]: if not answer: raise Exception("Empty response from NotebookLM") - print(f" āœ… Got response ({len(answer)} chars)") + print(f" [OK] Got response ({len(answer)} chars)") return { "status": "success", @@ -146,7 +146,7 @@ def ask(self, question: str) -> Dict[str, Any]: } except Exception as e: - print(f" āŒ Error: {e}") + print(f" [ERROR] {e}") return { "status": "error", "question": question, @@ -208,7 +208,7 @@ def _wait_for_latest_answer(self, previous_answer: Optional[str], timeout: int = def reset(self): """Reset the chat by reloading the page""" - print(f"šŸ”„ Resetting session {self.id}...") + print(f"[RESET] Resetting session {self.id}...") self.page.reload(wait_until="domcontentloaded") self._wait_for_ready() @@ -217,20 +217,20 @@ def reset(self): self.message_count = 0 self.last_activity = time.time() - print(f"āœ… Session reset (cleared {previous_count} messages)") + print(f"[OK] Session reset (cleared {previous_count} messages)") return previous_count def close(self): """Close this session and clean up resources""" - print(f"šŸ›‘ Closing session {self.id}...") + print(f"[CLOSE] Closing session {self.id}...") if self.page: try: self.page.close() except Exception as e: - print(f" āš ļø Error closing page: {e}") + print(f" [WARN] Error closing page: {e}") - print(f"āœ… Session {self.id} closed") + print(f"[OK] Session {self.id} closed") def get_info(self) -> Dict[str, Any]: """Get information about this session""" diff --git a/scripts/browser_utils.py b/scripts/browser_utils.py index 60a1210..ef38e7e 100755 --- a/scripts/browser_utils.py +++ b/scripts/browser_utils.py @@ -51,9 +51,9 @@ def _inject_cookies(context: BrowserContext): state = json.load(f) if 'cookies' in state and len(state['cookies']) > 0: context.add_cookies(state['cookies']) - # print(f" šŸ”§ Injected {len(state['cookies'])} cookies from state.json") + # print(f" [INFO] Injected {len(state['cookies'])} cookies from state.json") except Exception as e: - print(f" āš ļø Could not load state.json: {e}") + print(f" [WARN] Could not load state.json: {e}") class StealthUtils: @@ -76,7 +76,7 @@ def human_type(page: Page, selector: str, text: str, wpm_min: int = 320, wpm_max pass if not element: - print(f"āš ļø Element not found for typing: {selector}") + print(f"[WARN] Element not found for typing: {selector}") return # Click to focus diff --git a/scripts/cleanup_manager.py b/scripts/cleanup_manager.py index c4a8fc2..6fcd3d3 100755 --- a/scripts/cleanup_manager.py +++ b/scripts/cleanup_manager.py @@ -175,13 +175,13 @@ def perform_cleanup( path.unlink() deleted_items.append(str(path)) deleted_size += item_info['size'] - print(f" āœ… Deleted: {path.name}") + print(f" [OK] Deleted: {path.name}") except Exception as e: failed_items.append({ 'path': str(path), 'error': str(e) }) - print(f" āŒ Failed: {path.name} ({e})") + print(f" [ERROR] Failed: {path.name} ({e})") # Recreate browser_state dir if everything was deleted if not preserve_library and not failed_items: @@ -200,16 +200,16 @@ def print_cleanup_preview(self, preserve_library: bool = False): """Print a preview of what will be cleaned""" data = self.get_cleanup_paths(preserve_library) - print("\nšŸ” Cleanup Preview") + print("\n[PREVIEW] Cleanup Preview") print("=" * 60) for category, items in data['categories'].items(): if items: - print(f"\nšŸ“ {category.replace('_', ' ').title()}:") + print(f"\n[{category.replace('_', ' ').title()}]:") for item in items: path = Path(item['path']) size_str = self._format_size(item['size']) - type_icon = "šŸ“‚" if item['type'] == 'dir' else "šŸ“„" + type_icon = "[DIR]" if item['type'] == 'dir' else "[FILE]" print(f" {type_icon} {path.name:<30} {size_str:>10}") print("\n" + "=" * 60) @@ -217,7 +217,7 @@ def print_cleanup_preview(self, preserve_library: bool = False): print(f"Total size: {self._format_size(data['total_size'])}") if preserve_library: - print("\nšŸ“š Library will be preserved") + print("\n[INFO] Library will be preserved") print("\nThis preview shows what would be deleted.") print("Use --confirm to actually perform the cleanup.") @@ -272,8 +272,8 @@ def main(): if not args.force: manager.print_cleanup_preview(args.preserve_library) - print("\nāš ļø WARNING: This will delete the files shown above!") - print(" Note: .venv is preserved (part of skill infrastructure)") + print("\n[WARN] WARNING: This will delete the files shown above!") + print(" Note: .venv is preserved (part of skill infrastructure)") response = input("Are you sure? (yes/no): ") if response.lower() != 'yes': @@ -281,20 +281,20 @@ def main(): return # Perform cleanup - print("\nšŸ—‘ļø Performing cleanup...") + print("\n[CLEANUP] Performing cleanup...") result = manager.perform_cleanup(args.preserve_library, dry_run=False) - print(f"\nāœ… Cleanup complete!") - print(f" Deleted: {result['deleted_count']} items") - print(f" Freed: {manager._format_size(result['deleted_size'])}") + print(f"\n[OK] Cleanup complete!") + print(f" Deleted: {result['deleted_count']} items") + print(f" Freed: {manager._format_size(result['deleted_size'])}") if result['failed_count'] > 0: - print(f" āš ļø Failed: {result['failed_count']} items") + print(f" [WARN] Failed: {result['failed_count']} items") else: # Just show preview manager.print_cleanup_preview(args.preserve_library) - print("\nšŸ’” Note: Virtual environment (.venv) is never deleted") + print("\n[NOTE] Virtual environment (.venv) is never deleted") print(" It's part of the skill infrastructure, not user data") diff --git a/scripts/notebook_manager.py b/scripts/notebook_manager.py index e10e156..1b15d98 100755 --- a/scripts/notebook_manager.py +++ b/scripts/notebook_manager.py @@ -39,9 +39,9 @@ def _load_library(self): data = json.load(f) self.notebooks = data.get('notebooks', {}) self.active_notebook_id = data.get('active_notebook_id') - print(f"šŸ“š Loaded library with {len(self.notebooks)} notebooks") + print(f"[LIBRARY] Loaded library with {len(self.notebooks)} notebooks") except Exception as e: - print(f"āš ļø Error loading library: {e}") + print(f"[WARN] Error loading library: {e}") self.notebooks = {} self.active_notebook_id = None else: @@ -58,7 +58,7 @@ def _save_library(self): with open(self.library_file, 'w') as f: json.dump(data, f, indent=2) except Exception as e: - print(f"āŒ Error saving library: {e}") + print(f"[ERROR] Error saving library: {e}") def add_notebook( self, @@ -117,7 +117,7 @@ def add_notebook( self._save_library() - print(f"āœ… Added notebook: {name} ({notebook_id})") + print(f"[OK] Added notebook: {name} ({notebook_id})") return notebook def remove_notebook(self, notebook_id: str) -> bool: @@ -141,10 +141,10 @@ def remove_notebook(self, notebook_id: str) -> bool: self.active_notebook_id = list(self.notebooks.keys())[0] self._save_library() - print(f"āœ… Removed notebook: {notebook_id}") + print(f"[OK] Removed notebook: {notebook_id}") return True - print(f"āš ļø Notebook not found: {notebook_id}") + print(f"[WARN] Notebook not found: {notebook_id}") return False def update_notebook( @@ -192,7 +192,7 @@ def update_notebook( notebook['updated_at'] = datetime.now().isoformat() self._save_library() - print(f"āœ… Updated notebook: {notebook['name']}") + print(f"[OK] Updated notebook: {notebook['name']}") return notebook def get_notebook(self, notebook_id: str) -> Optional[Dict[str, Any]]: @@ -248,7 +248,7 @@ def select_notebook(self, notebook_id: str) -> Dict[str, Any]: self._save_library() notebook = self.notebooks[notebook_id] - print(f"āœ… Activated notebook: {notebook['name']}") + print(f"[OK] Activated notebook: {notebook['name']}") return notebook def get_active_notebook(self) -> Optional[Dict[str, Any]]: @@ -362,25 +362,25 @@ def main(): elif args.command == 'list': notebooks = library.list_notebooks() if notebooks: - print("\nšŸ“š Notebook Library:") + print("\n[LIBRARY] Notebook Library:") for notebook in notebooks: active = " [ACTIVE]" if notebook['id'] == library.active_notebook_id else "" - print(f"\n šŸ““ {notebook['name']}{active}") - print(f" ID: {notebook['id']}") - print(f" Topics: {', '.join(notebook['topics'])}") - print(f" Uses: {notebook['use_count']}") + print(f"\n [NOTEBOOK] {notebook['name']}{active}") + print(f" ID: {notebook['id']}") + print(f" Topics: {', '.join(notebook['topics'])}") + print(f" Uses: {notebook['use_count']}") else: - print("šŸ“š Library is empty. Add notebooks with: notebook_manager.py add") + print("[LIBRARY] Library is empty. Add notebooks with: notebook_manager.py add") elif args.command == 'search': results = library.search_notebooks(args.query) if results: - print(f"\nšŸ” Found {len(results)} notebooks:") + print(f"\n[SEARCH] Found {len(results)} notebooks:") for notebook in results: - print(f"\n šŸ““ {notebook['name']} ({notebook['id']})") - print(f" {notebook['description']}") + print(f"\n [NOTEBOOK] {notebook['name']} ({notebook['id']})") + print(f" {notebook['description']}") else: - print(f"šŸ” No notebooks found for: {args.query}") + print(f"[SEARCH] No notebooks found for: {args.query}") elif args.command == 'activate': notebook = library.select_notebook(args.id) @@ -392,7 +392,7 @@ def main(): elif args.command == 'stats': stats = library.get_stats() - print("\nšŸ“Š Library Statistics:") + print("\n[STATS] Library Statistics:") print(f" Total notebooks: {stats['total_notebooks']}") print(f" Total topics: {stats['total_topics']}") print(f" Total uses: {stats['total_use_count']}") diff --git a/scripts/run.py b/scripts/run.py index 7c47a92..a8dc5d1 100755 --- a/scripts/run.py +++ b/scripts/run.py @@ -31,16 +31,16 @@ def ensure_venv(): # Check if venv exists if not venv_dir.exists(): - print("šŸ”§ First-time setup: Creating virtual environment...") - print(" This may take a minute...") + print("[SETUP] First-time setup: Creating virtual environment...") + print(" This may take a minute...") # Run setup with system Python result = subprocess.run([sys.executable, str(setup_script)]) if result.returncode != 0: - print("āŒ Failed to set up environment") + print("[ERROR] Failed to set up environment") sys.exit(1) - print("āœ… Environment ready!") + print("[OK] Environment ready!") return get_venv_python() @@ -74,10 +74,10 @@ def main(): script_path = skill_dir / "scripts" / script_name if not script_path.exists(): - print(f"āŒ Script not found: {script_name}") - print(f" Working directory: {Path.cwd()}") - print(f" Skill directory: {skill_dir}") - print(f" Looked for: {script_path}") + print(f"[ERROR] Script not found: {script_name}") + print(f" Working directory: {Path.cwd()}") + print(f" Skill directory: {skill_dir}") + print(f" Looked for: {script_path}") sys.exit(1) # Ensure venv exists and get Python executable @@ -91,10 +91,10 @@ def main(): result = subprocess.run(cmd) sys.exit(result.returncode) except KeyboardInterrupt: - print("\nāš ļø Interrupted by user") + print("\n[WARN] Interrupted by user") sys.exit(130) except Exception as e: - print(f"āŒ Error: {e}") + print(f"[ERROR] {e}") sys.exit(1) diff --git a/scripts/setup_environment.py b/scripts/setup_environment.py index a4167d0..f64b863 100755 --- a/scripts/setup_environment.py +++ b/scripts/setup_environment.py @@ -33,22 +33,22 @@ def ensure_venv(self) -> bool: # Check if we're already in the correct venv if self.is_in_skill_venv(): - print("āœ… Already running in skill virtual environment") + print("[OK] Already running in skill virtual environment") return True # Create venv if it doesn't exist if not self.venv_dir.exists(): - print(f"šŸ”§ Creating virtual environment in {self.venv_dir.name}/") + print(f"[SETUP] Creating virtual environment in {self.venv_dir.name}/") try: venv.create(self.venv_dir, with_pip=True) - print("āœ… Virtual environment created") + print("[OK] Virtual environment created") except Exception as e: - print(f"āŒ Failed to create venv: {e}") + print(f"[ERROR] Failed to create venv: {e}") return False # Install/update dependencies if self.requirements_file.exists(): - print("šŸ“¦ Installing dependencies...") + print("[INFO] Installing dependencies...") try: # Upgrade pip first subprocess.run( @@ -65,12 +65,12 @@ def ensure_venv(self) -> bool: capture_output=True, text=True ) - print("āœ… Dependencies installed") + print("[OK] Dependencies installed") # Install Chrome for Patchright (not Chromium!) # Using real Chrome ensures cross-platform reliability and consistent browser fingerprinting # See: https://github.com/Kaliiiiiiiiii-Vinyzu/patchright-python#anti-detection - print("🌐 Installing Google Chrome for Patchright...") + print("[INFO] Installing Google Chrome for Patchright...") try: subprocess.run( [str(self.venv_python), "-m", "patchright", "install", "chrome"], @@ -78,19 +78,19 @@ def ensure_venv(self) -> bool: capture_output=True, text=True ) - print("āœ… Chrome installed") + print("[OK] Chrome installed") except subprocess.CalledProcessError as e: - print(f"āš ļø Warning: Failed to install Chrome: {e}") - print(" You may need to run manually: python -m patchright install chrome") - print(" Chrome is required (not Chromium) for reliability!") + print(f"[WARN] Warning: Failed to install Chrome: {e}") + print(" You may need to run manually: python -m patchright install chrome") + print(" Chrome is required (not Chromium) for reliability!") return True except subprocess.CalledProcessError as e: - print(f"āŒ Failed to install dependencies: {e}") - print(f" Output: {e.output if hasattr(e, 'output') else 'No output'}") + print(f"[ERROR] Failed to install dependencies: {e}") + print(f" Output: {e.output if hasattr(e, 'output') else 'No output'}") return False else: - print("āš ļø No requirements.txt found, skipping dependency installation") + print("[WARN] No requirements.txt found, skipping dependency installation") return True def is_in_skill_venv(self) -> bool: @@ -112,12 +112,12 @@ def run_script(self, script_name: str, args: list = None) -> int: script_path = self.skill_dir / "scripts" / script_name if not script_path.exists(): - print(f"āŒ Script not found: {script_path}") + print(f"[ERROR] Script not found: {script_path}") return 1 # Ensure venv is set up if not self.ensure_venv(): - print("āŒ Failed to set up environment") + print("[ERROR] Failed to set up environment") return 1 # Build command @@ -125,14 +125,14 @@ def run_script(self, script_name: str, args: list = None) -> int: if args: cmd.extend(args) - print(f"šŸš€ Running: {script_name} with venv Python") + print(f"[RUN] Running: {script_name} with venv Python") try: # Run the script with venv Python result = subprocess.run(cmd) return result.returncode except Exception as e: - print(f"āŒ Failed to run script: {e}") + print(f"[ERROR] Failed to run script: {e}") return 1 def activate_instructions(self) -> str: @@ -176,12 +176,12 @@ def main(): if args.check: if env.venv_dir.exists(): - print(f"āœ… Virtual environment exists: {env.venv_dir}") - print(f" Python: {env.get_python_executable()}") - print(f" To activate manually: {env.activate_instructions()}") + print(f"[OK] Virtual environment exists: {env.venv_dir}") + print(f" Python: {env.get_python_executable()}") + print(f" To activate manually: {env.activate_instructions()}") else: - print(f"āŒ No virtual environment found") - print(f" Run setup_environment.py to create it") + print(f"[ERROR] No virtual environment found") + print(f" Run setup_environment.py to create it") return if args.run: @@ -190,13 +190,13 @@ def main(): # Default: ensure environment is set up if env.ensure_venv(): - print("\nāœ… Environment ready!") - print(f" Virtual env: {env.venv_dir}") - print(f" Python: {env.get_python_executable()}") + print("\n[OK] Environment ready!") + print(f" Virtual env: {env.venv_dir}") + print(f" Python: {env.get_python_executable()}") print(f"\nTo activate manually: {env.activate_instructions()}") print(f"Or run scripts directly: python setup_environment.py --run script_name.py") else: - print("\nāŒ Environment setup failed") + print("\n[ERROR] Environment setup failed") return 1