diff --git a/CHANGELOG.md b/CHANGELOG.md index a60278e..02dff0f 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,83 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.0.0] - 2025-12-20 + +### BREAKING CHANGES + +**UV Shebang Conversion** +- Converted from manual venv management to UV shebang scripts with PEP 723 inline metadata +- Command pattern changed: `python scripts/run.py script.py` → `./scripts/script.py` or `uv run scripts/script.py` +- Requires UV installation: `curl -LsSf https://astral.sh/uv/install.sh | sh` + +### Migration Guide + +**1. Install UV:** +```bash +# macOS/Linux +curl -LsSf https://astral.sh/uv/install.sh | sh + +# Windows (PowerShell) +powershell -c "irm https://astral.sh/uv/install.ps1 | iex" +``` + +**2. Update skill:** +```bash +cd ~/.claude/skills/notebooklm +git pull +``` + +**3. Clean old environment (optional):** +```bash +rm -rf .venv # No longer needed +``` + +**4. Make scripts executable (Unix/Mac only):** +```bash +chmod +x scripts/*.py +``` + +**Updated Commands:** + +| Old Command | New Command (Unix/Mac) | New Command (Windows) | +|-------------|------------------------|------------------------| +| `python scripts/run.py auth_manager.py status` | `./scripts/auth_manager.py status` | `uv run scripts\auth_manager.py status` | +| `python scripts/run.py notebook_manager.py list` | `./scripts/notebook_manager.py list` | `uv run scripts\notebook_manager.py list` | +| `python scripts/run.py ask_question.py --question "..."` | `./scripts/ask_question.py --question "..."` | `uv run scripts\ask_question.py --question "..."` | + +**First run:** Dependencies install automatically (one-time, ~30 seconds) + +**Troubleshooting:** + +- **"uv: command not found"**: Install uv and restart terminal +- **"Permission denied"**: Run `chmod +x scripts/*.py` (Unix/Mac) +- **Windows users**: Always use `uv run scripts\script.py` syntax + +**Rollback to v1.3.0:** +```bash +cd ~/.claude/skills/notebooklm +git checkout v1.3.0 +python -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt +python -m patchright install chrome +``` + +### Added +- UV shebang with PEP 723 inline metadata in all entry point scripts +- Automatic dependency installation on first run +- Dependency caching for instant subsequent runs + +### Removed +- `scripts/run.py` - Replaced by UV shebang +- `scripts/setup_environment.py` - Replaced by UV automatic setup +- `requirements.txt` - Dependencies now inline in scripts + +### Changed +- All scripts now directly executable with UV shebang +- Updated documentation for UV usage pattern +- Simplified installation process (no manual venv setup) + ## [1.3.0] - 2025-11-21 ### Added diff --git a/README.md b/README.md index 0a46e9c..cc330d7 100755 --- a/README.md +++ b/README.md @@ -66,27 +66,48 @@ Your Task → Claude asks NotebookLM → Gemini synthesizes answer → Claude wr ## Installation -### The simplest installation ever: +### Prerequisites +**Install uv** (one-time): ```bash -# 1. Create skills directory (if it doesn't exist) -mkdir -p ~/.claude/skills +# macOS/Linux +curl -LsSf https://astral.sh/uv/install.sh | sh + +# Windows (PowerShell) +powershell -c "irm https://astral.sh/uv/install.ps1 | iex" +``` -# 2. Clone this repository +### Install Skill + +```bash +mkdir -p ~/.claude/skills cd ~/.claude/skills git clone https://github.com/PleasePrompto/notebooklm-skill notebooklm +``` + +**Unix/Mac only:** +```bash +cd notebooklm +chmod +x scripts/*.py +``` -# 3. That's it! Open Claude Code and say: +That's it! Dependencies install automatically on first use. + +### First Run + +Tell Claude Code: +``` "What are my skills?" ``` -When you first use the skill, it automatically: -- Creates an isolated Python environment (`.venv`) -- Installs all dependencies including **Google Chrome** -- Sets up browser automation with Chrome (not Chromium) for maximum reliability -- Everything stays contained in the skill folder +When you first use NotebookLM, dependencies install automatically: +```bash +./scripts/auth_manager.py status +# Installing dependencies... (one-time, ~30 seconds) +# ✅ Not authenticated +``` -**Note:** The setup uses real Chrome instead of Chromium for cross-platform reliability, consistent browser fingerprinting, and better anti-detection with Google services +**No manual venv setup needed** - uv handles everything. --- @@ -254,9 +275,14 @@ Uses realistic typing speeds and interaction patterns to avoid detection. Note: The MCP server uses the same Patchright library but via TypeScript/npm ecosystem. ### Dependencies + +Dependencies are declared inline in each script using PEP 723 metadata: + +**Main scripts:** - **patchright==1.55.2**: Browser automation - **python-dotenv==1.0.0**: Environment configuration -- Automatically installed in `.venv` on first use + +Automatically installed by uv on first run. ### Data Storage @@ -342,12 +368,12 @@ Say: `"Clear NotebookLM browser data"` ### Dependencies issues ```bash -# Manual reinstall if needed -cd ~/.claude/skills/notebooklm -rm -rf .venv -python -m venv .venv -source .venv/bin/activate # or .venv\Scripts\activate on Windows -pip install -r requirements.txt +# Dependencies are managed automatically by uv +# If you encounter issues, ensure uv is installed: +curl -LsSf https://astral.sh/uv/install.sh | sh + +# Restart terminal and try again: +./scripts/auth_manager.py status ``` --- diff --git a/SKILL.md b/SKILL.md index 2be7e16..99ca2b8 100755 --- a/SKILL.md +++ b/SKILL.md @@ -1,6 +1,6 @@ --- name: notebooklm -description: Use this skill to query your Google NotebookLM notebooks directly from Claude Code for source-grounded, citation-backed answers from Gemini. Browser automation, library management, persistent auth. Drastically reduced hallucinations through document-only responses. +description: Query Google NotebookLM notebooks for source-grounded, citation-backed answers from Gemini. Use when user mentions NotebookLM, shares notebook URLs, asks to query docs/notebooks, or wants document-only answers to avoid hallucinations. Browser automation with persistent auth. --- # NotebookLM Research Assistant Skill @@ -23,10 +23,10 @@ When user wants to add a notebook without providing details: **SMART ADD (Recommended)**: Query the notebook first to discover its content: ```bash # Step 1: Query the notebook about its content -python scripts/run.py ask_question.py --question "What is the content of this notebook? What topics are covered? Provide a complete overview briefly and concisely" --notebook-url "[URL]" +./scripts/ask_question.py --question "What is the content of this notebook? What topics are covered? Provide a complete overview briefly and concisely" --notebook-url "[URL]" # Step 2: Use the discovered information to add it -python scripts/run.py notebook_manager.py add --url "[URL]" --name "[Based on content]" --description "[Based on content]" --topics "[Based on content]" +./scripts/notebook_manager.py add --url "[URL]" --name "[Based on content]" --description "[Based on content]" --topics "[Based on content]" ``` **MANUAL ADD**: If user provides all details: @@ -37,31 +37,39 @@ python scripts/run.py notebook_manager.py add --url "[URL]" --name "[Based on co NEVER guess or use generic descriptions! If details missing, use Smart Add to discover them. -## Critical: Always Use run.py Wrapper +## Running Scripts -**NEVER call scripts directly. ALWAYS use `python scripts/run.py [script]`:** +All scripts use UV shebang for automatic dependency management. +**Unix/Mac (direct execution):** ```bash -# ✅ CORRECT - Always use run.py: -python scripts/run.py auth_manager.py status -python scripts/run.py notebook_manager.py list -python scripts/run.py ask_question.py --question "..." +./scripts/auth_manager.py status +./scripts/notebook_manager.py list +./scripts/ask_question.py --question "Your question here" +``` -# ❌ WRONG - Never call directly: -python scripts/auth_manager.py status # Fails without venv! +**All platforms (explicit uv):** +```bash +uv run scripts/auth_manager.py status +uv run scripts/notebook_manager.py list +uv run scripts/ask_question.py --question "Your question here" +``` + +**Windows:** +```bash +uv run scripts\auth_manager.py status +uv run scripts\notebook_manager.py list +uv run scripts\ask_question.py --question "Your question here" ``` -The `run.py` wrapper automatically: -1. Creates `.venv` if needed -2. Installs all dependencies -3. Activates environment -4. Executes script properly +**First run:** Dependencies install automatically (one-time, ~30 seconds) +**Subsequent runs:** Instant execution (uv caches dependencies) ## Core Workflow ### Step 1: Check Authentication Status ```bash -python scripts/run.py auth_manager.py status +./scripts/auth_manager.py status ``` If not authenticated, proceed to setup. @@ -69,7 +77,7 @@ If not authenticated, proceed to setup. ### Step 2: Authenticate (One-Time Setup) ```bash # Browser MUST be visible for manual Google login -python scripts/run.py auth_manager.py setup +./scripts/auth_manager.py setup ``` **Important:** @@ -82,47 +90,47 @@ python scripts/run.py auth_manager.py setup ```bash # List all notebooks -python scripts/run.py notebook_manager.py list +./scripts/notebook_manager.py list # BEFORE ADDING: Ask user for metadata if unknown! # "What does this notebook contain?" # "What topics should I tag it with?" # Add notebook to library (ALL parameters are REQUIRED!) -python scripts/run.py notebook_manager.py add \ +./scripts/notebook_manager.py add \ --url "https://notebooklm.google.com/notebook/..." \ --name "Descriptive Name" \ --description "What this notebook contains" \ # REQUIRED - ASK USER IF UNKNOWN! --topics "topic1,topic2,topic3" # REQUIRED - ASK USER IF UNKNOWN! # Search notebooks by topic -python scripts/run.py notebook_manager.py search --query "keyword" +./scripts/notebook_manager.py search --query "keyword" # Set active notebook -python scripts/run.py notebook_manager.py activate --id notebook-id +./scripts/notebook_manager.py activate --id notebook-id # Remove notebook -python scripts/run.py notebook_manager.py remove --id notebook-id +./scripts/notebook_manager.py remove --id notebook-id ``` ### Quick Workflow -1. Check library: `python scripts/run.py notebook_manager.py list` -2. Ask question: `python scripts/run.py ask_question.py --question "..." --notebook-id ID` +1. Check library: `./scripts/notebook_manager.py list` +2. Ask question: `./scripts/ask_question.py --question "..." --notebook-id ID` ### Step 4: Ask Questions ```bash # Basic query (uses active notebook if set) -python scripts/run.py ask_question.py --question "Your question here" +./scripts/ask_question.py --question "Your question here" # Query specific notebook -python scripts/run.py ask_question.py --question "..." --notebook-id notebook-id +./scripts/ask_question.py --question "..." --notebook-id notebook-id # Query with notebook URL directly -python scripts/run.py ask_question.py --question "..." --notebook-url "https://..." +./scripts/ask_question.py --question "..." --notebook-url "https://..." # Show browser for debugging -python scripts/run.py ask_question.py --question "..." --show-browser +./scripts/ask_question.py --question "..." --show-browser ``` ## Follow-Up Mechanism (CRITICAL) @@ -135,7 +143,7 @@ Every NotebookLM answer ends with: **"EXTREMELY IMPORTANT: Is that ALL you need 3. **IDENTIFY GAPS** - Determine if more information needed 4. **ASK FOLLOW-UP** - If gaps exist, immediately ask: ```bash - python scripts/run.py ask_question.py --question "Follow-up with context..." + ./scripts/ask_question.py --question "Follow-up with context..." ``` 5. **REPEAT** - Continue until information is complete 6. **SYNTHESIZE** - Combine all answers before responding to user @@ -144,48 +152,32 @@ Every NotebookLM answer ends with: **"EXTREMELY IMPORTANT: Is that ALL you need ### Authentication Management (`auth_manager.py`) ```bash -python scripts/run.py auth_manager.py setup # Initial setup (browser visible) -python scripts/run.py auth_manager.py status # Check authentication -python scripts/run.py auth_manager.py reauth # Re-authenticate (browser visible) -python scripts/run.py auth_manager.py clear # Clear authentication +./scripts/auth_manager.py setup # Initial setup (browser visible) +./scripts/auth_manager.py status # Check authentication +./scripts/auth_manager.py reauth # Re-authenticate (browser visible) +./scripts/auth_manager.py clear # Clear authentication ``` ### Notebook Management (`notebook_manager.py`) ```bash -python scripts/run.py notebook_manager.py add --url URL --name NAME --description DESC --topics TOPICS -python scripts/run.py notebook_manager.py list -python scripts/run.py notebook_manager.py search --query QUERY -python scripts/run.py notebook_manager.py activate --id ID -python scripts/run.py notebook_manager.py remove --id ID -python scripts/run.py notebook_manager.py stats +./scripts/notebook_manager.py add --url URL --name NAME --description DESC --topics TOPICS +./scripts/notebook_manager.py list +./scripts/notebook_manager.py search --query QUERY +./scripts/notebook_manager.py activate --id ID +./scripts/notebook_manager.py remove --id ID +./scripts/notebook_manager.py stats ``` ### Question Interface (`ask_question.py`) ```bash -python scripts/run.py ask_question.py --question "..." [--notebook-id ID] [--notebook-url URL] [--show-browser] +./scripts/ask_question.py --question "..." [--notebook-id ID] [--notebook-url URL] [--show-browser] ``` ### Data Cleanup (`cleanup_manager.py`) ```bash -python scripts/run.py cleanup_manager.py # Preview cleanup -python scripts/run.py cleanup_manager.py --confirm # Execute cleanup -python scripts/run.py cleanup_manager.py --preserve-library # Keep notebooks -``` - -## Environment Management - -The virtual environment is automatically managed: -- First run creates `.venv` automatically -- Dependencies install automatically -- Chromium browser installs automatically -- Everything isolated in skill directory - -Manual setup (only if automatic fails): -```bash -python -m venv .venv -source .venv/bin/activate # Linux/Mac -pip install -r requirements.txt -python -m patchright install chromium +./scripts/cleanup_manager.py # Preview cleanup +./scripts/cleanup_manager.py --confirm # Execute cleanup +./scripts/cleanup_manager.py --preserve-library # Keep notebooks ``` ## Data Storage @@ -214,15 +206,15 @@ DEFAULT_NOTEBOOK_ID= # Default notebook ``` User mentions NotebookLM ↓ -Check auth → python scripts/run.py auth_manager.py status +Check auth → ./scripts/auth_manager.py status ↓ -If not authenticated → python scripts/run.py auth_manager.py setup +If not authenticated → ./scripts/auth_manager.py setup ↓ -Check/Add notebook → python scripts/run.py notebook_manager.py list/add (with --description) +Check/Add notebook → ./scripts/notebook_manager.py list/add (with --description) ↓ -Activate notebook → python scripts/run.py notebook_manager.py activate --id ID +Activate notebook → ./scripts/notebook_manager.py activate --id ID ↓ -Ask question → python scripts/run.py ask_question.py --question "..." +Ask question → ./scripts/ask_question.py --question "..." ↓ See "Is that ALL you need?" → Ask follow-ups until complete ↓ @@ -236,12 +228,12 @@ Synthesize and respond to user | ModuleNotFoundError | Use `run.py` wrapper | | Authentication fails | Browser must be visible for setup! --show-browser | | Rate limit (50/day) | Wait or switch Google account | -| Browser crashes | `python scripts/run.py cleanup_manager.py --preserve-library` | +| Browser crashes | `./scripts/cleanup_manager.py --preserve-library` | | Notebook not found | Check with `notebook_manager.py list` | ## Best Practices -1. **Always use run.py** - Handles environment automatically +1. **Direct execution** - Scripts are self-contained with UV shebang 2. **Check auth first** - Before any operations 3. **Follow-up questions** - Don't stop at first answer 4. **Browser visible for auth** - Required for manual login @@ -265,5 +257,4 @@ Synthesize and respond to user - `api_reference.md` - Detailed API documentation for all scripts - `troubleshooting.md` - Common issues and solutions - `usage_patterns.md` - Best practices and workflow examples -- `.venv/` - Isolated Python environment (auto-created on first run) - `.gitignore` - Protects sensitive data from being committed diff --git a/references/api_reference.md b/references/api_reference.md index a0ce65e..4cd26fd 100755 --- a/references/api_reference.md +++ b/references/api_reference.md @@ -2,35 +2,45 @@ Complete API documentation for all NotebookLM skill modules. -## Important: Always Use run.py Wrapper +## Prerequisites -**All commands must use the `run.py` wrapper to ensure proper environment:** +**All commands require UV to be installed:** ```bash -# ✅ CORRECT: -python scripts/run.py [script_name].py [arguments] +# macOS/Linux +curl -LsSf https://astral.sh/uv/install.sh | sh -# ❌ WRONG: -python scripts/[script_name].py [arguments] # Will fail without venv! +# Windows (PowerShell) +powershell -c "irm https://astral.sh/uv/install.ps1 | iex" ``` +**Command patterns:** +- **Unix/Mac**: `./scripts/script.py` (direct execution) +- **All platforms**: `uv run scripts/script.py` (explicit uv) +- **Windows**: `uv run scripts\script.py` (backslash paths) + +**First run**: Dependencies install automatically (~30 seconds, one-time) + ## Core Scripts ### ask_question.py Query NotebookLM with automated browser interaction. ```bash -# Basic usage -python scripts/run.py ask_question.py --question "Your question" +# Basic usage (Unix/Mac) +./scripts/ask_question.py --question "Your question" + +# All platforms +uv run scripts/ask_question.py --question "Your question" # With specific notebook -python scripts/run.py ask_question.py --question "..." --notebook-id notebook-id +./scripts/ask_question.py --question "..." --notebook-id notebook-id # With direct URL -python scripts/run.py ask_question.py --question "..." --notebook-url "https://..." +./scripts/ask_question.py --question "..." --notebook-url "https://..." # Show browser (debugging) -python scripts/run.py ask_question.py --question "..." --show-browser +./scripts/ask_question.py --question "..." --show-browser ``` **Parameters:** @@ -46,35 +56,35 @@ Manage notebook library with CRUD operations. ```bash # Smart Add (discover content first) -python scripts/run.py ask_question.py --question "What is the content of this notebook? What topics are covered? Provide a complete overview briefly and concisely" --notebook-url "[URL]" +./scripts/ask_question.py --question "What is the content of this notebook? What topics are covered? Provide a complete overview briefly and concisely" --notebook-url "[URL]" # Then add with discovered info -python scripts/run.py notebook_manager.py add \ +./scripts/notebook_manager.py add \ --url "https://notebooklm.google.com/notebook/..." \ --name "Name" \ --description "Description" \ --topics "topic1,topic2" # Direct add (when you know the content) -python scripts/run.py notebook_manager.py add \ +./scripts/notebook_manager.py add \ --url "https://notebooklm.google.com/notebook/..." \ --name "Name" \ --description "What it contains" \ --topics "topic1,topic2" # List notebooks -python scripts/run.py notebook_manager.py list +./scripts/notebook_manager.py list # Search notebooks -python scripts/run.py notebook_manager.py search --query "keyword" +./scripts/notebook_manager.py search --query "keyword" # Activate notebook -python scripts/run.py notebook_manager.py activate --id notebook-id +./scripts/notebook_manager.py activate --id notebook-id # Remove notebook -python scripts/run.py notebook_manager.py remove --id notebook-id +./scripts/notebook_manager.py remove --id notebook-id # Show statistics -python scripts/run.py notebook_manager.py stats +./scripts/notebook_manager.py stats ``` **Commands:** @@ -90,16 +100,16 @@ Handle Google authentication and browser state. ```bash # Setup (browser visible for login) -python scripts/run.py auth_manager.py setup +./scripts/auth_manager.py setup # Check status -python scripts/run.py auth_manager.py status +./scripts/auth_manager.py status # Re-authenticate -python scripts/run.py auth_manager.py reauth +./scripts/auth_manager.py reauth # Clear authentication -python scripts/run.py auth_manager.py clear +./scripts/auth_manager.py clear ``` **Commands:** @@ -113,16 +123,16 @@ Clean skill data with preservation options. ```bash # Preview cleanup -python scripts/run.py cleanup_manager.py +./scripts/cleanup_manager.py # Execute cleanup -python scripts/run.py cleanup_manager.py --confirm +./scripts/cleanup_manager.py --confirm # Keep library -python scripts/run.py cleanup_manager.py --confirm --preserve-library +./scripts/cleanup_manager.py --confirm --preserve-library # Force without prompt -python scripts/run.py cleanup_manager.py --confirm --force +./scripts/cleanup_manager.py --confirm --force ``` **Options:** @@ -130,35 +140,39 @@ python scripts/run.py cleanup_manager.py --confirm --force - `--preserve-library`: Keep notebook library - `--force`: Skip confirmation prompt -### run.py -Script wrapper that handles environment setup. +### UV Automatic Dependency Management -```bash -# Usage -python scripts/run.py [script_name].py [arguments] +All scripts use UV shebang with PEP 723 inline metadata for automatic dependency management. -# Examples -python scripts/run.py auth_manager.py status -python scripts/run.py ask_question.py --question "..." -``` +**How it works:** +1. First run: UV installs dependencies (~30 seconds) +2. Subsequent runs: Instant execution (cached) +3. No manual venv setup needed +4. Dependencies declared inline in each script -**Automatic actions:** -1. Creates `.venv` if missing -2. Installs dependencies -3. Activates environment -4. Executes target script +**Making scripts executable (Unix/Mac only):** +```bash +chmod +x scripts/*.py +``` ## Python API Usage -### Using subprocess with run.py +### Using subprocess with UV ```python import subprocess import json -# Always use run.py wrapper +# Direct execution (Unix/Mac) result = subprocess.run([ - "python", "scripts/run.py", "ask_question.py", + "./scripts/ask_question.py", + "--question", "Your question", + "--notebook-id", "notebook-id" +], capture_output=True, text=True) + +# Or explicit uv (all platforms) +result = subprocess.run([ + "uv", "run", "scripts/ask_question.py", "--question", "Your question", "--notebook-id", "notebook-id" ], capture_output=True, text=True) @@ -166,18 +180,23 @@ result = subprocess.run([ answer = result.stdout ``` -### Direct imports (after venv exists) +### Direct imports with uv run ```python -# Only works if venv is already created and activated -from notebook_manager import NotebookLibrary -from auth_manager import AuthManager +# Using uv run for module imports +import subprocess +# Execute in UV environment +result = subprocess.run([ + "uv", "run", "python", "-c", + """ +from scripts.notebook_manager import NotebookLibrary library = NotebookLibrary() -notebooks = library.list_notebooks() +print(len(library.list_notebooks())) +""" +], capture_output=True, text=True, cwd="~/.claude/skills/notebooklm") -auth = AuthManager() -is_auth = auth.is_authenticated() +print(result.stdout) ``` ## Data Storage @@ -212,9 +231,9 @@ DEFAULT_NOTEBOOK_ID= # Default notebook Common patterns: ```python -# Using run.py prevents most errors +# UV handles dependencies automatically result = subprocess.run([ - "python", "scripts/run.py", "ask_question.py", + "./scripts/ask_question.py", "--question", "Question" ], capture_output=True, text=True) @@ -225,7 +244,7 @@ if result.returncode != 0: pass elif "not authenticated" in error.lower(): # Run auth setup - subprocess.run(["python", "scripts/run.py", "auth_manager.py", "setup"]) + subprocess.run(["./scripts/auth_manager.py", "setup"]) ``` ## Rate Limits @@ -247,7 +266,7 @@ import subprocess def query(question, notebook_id): result = subprocess.run([ - "python", "scripts/run.py", "ask_question.py", + "./scripts/ask_question.py", "--question", question, "--notebook-id", notebook_id ], capture_output=True, text=True) @@ -269,7 +288,7 @@ def batch_research(questions, notebook_id): results = [] for question in questions: result = subprocess.run([ - "python", "scripts/run.py", "ask_question.py", + "./scripts/ask_question.py", "--question", question, "--notebook-id", notebook_id ], capture_output=True, text=True) @@ -302,8 +321,10 @@ def batch_research(questions, notebook_id): ## Best Practices -1. **Always use run.py** - Ensures environment -2. **Check auth first** - Before operations -3. **Handle rate limits** - Implement retries -4. **Include context** - Questions are independent -5. **Clean sessions** - Use cleanup_manager \ No newline at end of file +1. **Install UV first** - Required for all operations +2. **Make scripts executable** - `chmod +x scripts/*.py` (Unix/Mac) +3. **Check auth first** - Before operations +4. **Handle rate limits** - Implement retries +5. **Include context** - Questions are independent +6. **Clean sessions** - Use cleanup_manager +7. **First run delay** - Dependencies install automatically (~30s) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt deleted file mode 100755 index 6e38008..0000000 --- a/requirements.txt +++ /dev/null @@ -1,10 +0,0 @@ -# NotebookLM Skill Dependencies -# These will be installed in the skill's local .venv - -# Core browser automation with anti-detection -# Note: After installation, run: patchright install chrome -# (Chrome is required, not Chromium, for cross-platform reliability) -patchright==1.55.2 - -# Environment management -python-dotenv==1.0.0 \ No newline at end of file diff --git a/scripts/ask_question.py b/scripts/ask_question.py index aa47e4b..64a5dda 100755 --- a/scripts/ask_question.py +++ b/scripts/ask_question.py @@ -1,4 +1,11 @@ -#!/usr/bin/env python3 +#!/usr/bin/env -S uv run --quiet --script +# /// script +# requires-python = ">=3.8" +# dependencies = [ +# "patchright==1.55.2", +# "python-dotenv==1.0.0", +# ] +# /// """ Simple NotebookLM Question Interface Based on MCP server implementation - simplified without sessions @@ -17,9 +24,6 @@ from patchright.sync_api import sync_playwright -# Add parent directory to path -sys.path.insert(0, str(Path(__file__).parent)) - from auth_manager import AuthManager from notebook_manager import NotebookLibrary from config import QUERY_INPUT_SELECTORS, RESPONSE_SELECTORS diff --git a/scripts/auth_manager.py b/scripts/auth_manager.py index 54c8b3b..c03232f 100755 --- a/scripts/auth_manager.py +++ b/scripts/auth_manager.py @@ -1,4 +1,11 @@ -#!/usr/bin/env python3 +#!/usr/bin/env -S uv run --quiet --script +# /// script +# requires-python = ">=3.8" +# dependencies = [ +# "patchright==1.55.2", +# "python-dotenv==1.0.0", +# ] +# /// """ Authentication Manager for NotebookLM Handles Google login and browser state persistence @@ -21,9 +28,6 @@ from patchright.sync_api import sync_playwright, BrowserContext -# Add parent directory to path -sys.path.insert(0, str(Path(__file__).parent)) - from config import BROWSER_STATE_DIR, STATE_FILE, AUTH_INFO_FILE, DATA_DIR from browser_utils import BrowserFactory diff --git a/scripts/cleanup_manager.py b/scripts/cleanup_manager.py index c4a8fc2..2132b0f 100755 --- a/scripts/cleanup_manager.py +++ b/scripts/cleanup_manager.py @@ -1,4 +1,8 @@ -#!/usr/bin/env python3 +#!/usr/bin/env -S uv run --quiet --script +# /// script +# requires-python = ">=3.8" +# dependencies = [] +# /// """ Cleanup Manager for NotebookLM Skill Manages cleanup of skill data and browser state diff --git a/scripts/notebook_manager.py b/scripts/notebook_manager.py index e10e156..9b4dd33 100755 --- a/scripts/notebook_manager.py +++ b/scripts/notebook_manager.py @@ -1,4 +1,10 @@ -#!/usr/bin/env python3 +#!/usr/bin/env -S uv run --quiet --script +# /// script +# requires-python = ">=3.8" +# dependencies = [ +# "python-dotenv==1.0.0", +# ] +# /// """ Notebook Library Management for NotebookLM Manages a library of NotebookLM notebooks with metadata diff --git a/scripts/run.py b/scripts/run.py deleted file mode 100755 index 7c47a92..0000000 --- a/scripts/run.py +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env python3 -""" -Universal runner for NotebookLM skill scripts -Ensures all scripts run with the correct virtual environment -""" - -import os -import sys -import subprocess -from pathlib import Path - - -def get_venv_python(): - """Get the virtual environment Python executable""" - skill_dir = Path(__file__).parent.parent - venv_dir = skill_dir / ".venv" - - if os.name == 'nt': # Windows - venv_python = venv_dir / "Scripts" / "python.exe" - else: # Unix/Linux/Mac - venv_python = venv_dir / "bin" / "python" - - return venv_python - - -def ensure_venv(): - """Ensure virtual environment exists""" - skill_dir = Path(__file__).parent.parent - venv_dir = skill_dir / ".venv" - setup_script = skill_dir / "scripts" / "setup_environment.py" - - # Check if venv exists - if not venv_dir.exists(): - print("🔧 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") - sys.exit(1) - - print("✅ Environment ready!") - - return get_venv_python() - - -def main(): - """Main runner""" - if len(sys.argv) < 2: - print("Usage: python run.py [args...]") - print("\nAvailable scripts:") - print(" ask_question.py - Query NotebookLM") - print(" notebook_manager.py - Manage notebook library") - print(" session_manager.py - Manage sessions") - print(" auth_manager.py - Handle authentication") - print(" cleanup_manager.py - Clean up skill data") - sys.exit(1) - - script_name = sys.argv[1] - script_args = sys.argv[2:] - - # Handle both "scripts/script.py" and "script.py" formats - if script_name.startswith('scripts/'): - # Remove the scripts/ prefix if provided - script_name = script_name[8:] # len('scripts/') = 8 - - # Ensure .py extension - if not script_name.endswith('.py'): - script_name += '.py' - - # Get script path - skill_dir = Path(__file__).parent.parent - 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}") - sys.exit(1) - - # Ensure venv exists and get Python executable - venv_python = ensure_venv() - - # Build command - cmd = [str(venv_python), str(script_path)] + script_args - - # Run the script - try: - result = subprocess.run(cmd) - sys.exit(result.returncode) - except KeyboardInterrupt: - print("\n⚠️ Interrupted by user") - sys.exit(130) - except Exception as e: - print(f"❌ Error: {e}") - sys.exit(1) - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/scripts/setup_environment.py b/scripts/setup_environment.py deleted file mode 100755 index a4167d0..0000000 --- a/scripts/setup_environment.py +++ /dev/null @@ -1,204 +0,0 @@ -#!/usr/bin/env python3 -""" -Environment Setup for NotebookLM Skill -Manages virtual environment and dependencies automatically -""" - -import os -import sys -import subprocess -import venv -from pathlib import Path - - -class SkillEnvironment: - """Manages skill-specific virtual environment""" - - def __init__(self): - # Skill directory paths - self.skill_dir = Path(__file__).parent.parent - self.venv_dir = self.skill_dir / ".venv" - self.requirements_file = self.skill_dir / "requirements.txt" - - # Python executable in venv - if os.name == 'nt': # Windows - self.venv_python = self.venv_dir / "Scripts" / "python.exe" - self.venv_pip = self.venv_dir / "Scripts" / "pip.exe" - else: # Unix/Linux/Mac - self.venv_python = self.venv_dir / "bin" / "python" - self.venv_pip = self.venv_dir / "bin" / "pip" - - def ensure_venv(self) -> bool: - """Ensure virtual environment exists and is set up""" - - # Check if we're already in the correct venv - if self.is_in_skill_venv(): - print("✅ 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}/") - try: - venv.create(self.venv_dir, with_pip=True) - print("✅ Virtual environment created") - except Exception as e: - print(f"❌ Failed to create venv: {e}") - return False - - # Install/update dependencies - if self.requirements_file.exists(): - print("📦 Installing dependencies...") - try: - # Upgrade pip first - subprocess.run( - [str(self.venv_pip), "install", "--upgrade", "pip"], - check=True, - capture_output=True, - text=True - ) - - # Install requirements - result = subprocess.run( - [str(self.venv_pip), "install", "-r", str(self.requirements_file)], - check=True, - capture_output=True, - text=True - ) - print("✅ 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...") - try: - subprocess.run( - [str(self.venv_python), "-m", "patchright", "install", "chrome"], - check=True, - capture_output=True, - text=True - ) - print("✅ 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!") - - 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'}") - return False - else: - print("⚠️ No requirements.txt found, skipping dependency installation") - return True - - def is_in_skill_venv(self) -> bool: - """Check if we're already running in the skill's venv""" - if hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix): - # We're in a venv, check if it's ours - venv_path = Path(sys.prefix) - return venv_path == self.venv_dir - return False - - def get_python_executable(self) -> str: - """Get the correct Python executable to use""" - if self.venv_python.exists(): - return str(self.venv_python) - return sys.executable - - def run_script(self, script_name: str, args: list = None) -> int: - """Run a script with the virtual environment""" - script_path = self.skill_dir / "scripts" / script_name - - if not script_path.exists(): - print(f"❌ Script not found: {script_path}") - return 1 - - # Ensure venv is set up - if not self.ensure_venv(): - print("❌ Failed to set up environment") - return 1 - - # Build command - cmd = [str(self.venv_python), str(script_path)] - if args: - cmd.extend(args) - - print(f"🚀 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}") - return 1 - - def activate_instructions(self) -> str: - """Get instructions for manual activation""" - if os.name == 'nt': - activate = self.venv_dir / "Scripts" / "activate.bat" - return f"Run: {activate}" - else: - activate = self.venv_dir / "bin" / "activate" - return f"Run: source {activate}" - - -def main(): - """Main entry point for environment setup""" - import argparse - - parser = argparse.ArgumentParser( - description='Setup NotebookLM skill environment' - ) - - parser.add_argument( - '--check', - action='store_true', - help='Check if environment is set up' - ) - - parser.add_argument( - '--run', - help='Run a script with the venv (e.g., --run ask_question.py)' - ) - - parser.add_argument( - 'args', - nargs='*', - help='Arguments to pass to the script' - ) - - args = parser.parse_args() - - env = SkillEnvironment() - - 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()}") - else: - print(f"❌ No virtual environment found") - print(f" Run setup_environment.py to create it") - return - - if args.run: - # Run a script with venv - return env.run_script(args.run, args.args) - - # 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(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") - return 1 - - -if __name__ == "__main__": - sys.exit(main() or 0) \ No newline at end of file