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