← back to sergeeey__Claude-cod-top-2026

Function bodies 108 total

All specs Real LLM only Function bodies
emit_hook_result function · python · L206-L217 (12 LOC)
hooks/utils.py
def emit_hook_result(event_name: str, context: str) -> None:
    """Print hook result JSON to stdout (Claude Code protocol).

    WHY: Almost every hook constructs this dict manually — 30+ lines saved.
    """
    result = {
        "hookSpecificOutput": {
            "hookEventName": event_name,
            "additionalContext": context,
        }
    }
    print(json.dumps(result))
sanitize_text function · python · L220-L229 (10 LOC)
hooks/utils.py
def sanitize_text(text: str, max_len: int = 200) -> str:
    """Strip newlines and limit length to prevent prompt injection.

    WHY: Duplicated in pattern_extractor (sanitize_commit_msg)
    and input_guard (sanitize). Unified version.
    """
    clean = text.replace("\n", " ").replace("\r", " ").strip()
    if len(clean) > max_len:
        clean = clean[:max_len] + "..."
    return clean
extract_tool_response function · python · L232-L244 (13 LOC)
hooks/utils.py
def extract_tool_response(data: dict) -> str:
    """Extract tool response text from hook data, handling multiple formats.

    WHY: Duplicated in memory_guard, post_commit_memory, pattern_extractor
    (identical response extraction with fallbacks).
    """
    tool_response = data.get("tool_response", data.get("tool_result", {}))
    if isinstance(tool_response, dict):
        return str(tool_response.get("stdout", tool_response.get("output", "")))
    elif isinstance(tool_response, str):
        return tool_response
    else:
        return str(tool_response)
is_failed_commit function · python · L247-L252 (6 LOC)
hooks/utils.py
def is_failed_commit(response_text: str) -> bool:
    """Check if a git commit failed based on response text.

    WHY: Duplicated in memory_guard, post_commit_memory, pattern_extractor.
    """
    return "nothing to commit" in response_text or "error" in response_text.lower()
redact function · python · L57-L66 (10 LOC)
scripts/redact.py
def redact(text: str) -> str:
    for pattern, replacement in PATTERNS:

        def replace_if_not_excluded(m, repl=replacement):
            if should_exclude(m.group(0)):
                return m.group(0)
            return repl

        text = re.sub(pattern, replace_if_not_excluded, text)
    return text
clean function · python · L69-L76 (8 LOC)
scripts/redact.py
def clean(obj):
    if isinstance(obj, str):
        return redact(obj)
    elif isinstance(obj, dict):
        return {k: clean(v) for k, v in obj.items()}
    elif isinstance(obj, list):
        return [clean(item) for item in obj]
    return obj
ask_notebooklm function · python · L40-L187 (148 LOC)
skills/extensions/notebooklm/scripts/ask_question.py
def ask_notebooklm(question: str, notebook_url: str, headless: bool = True) -> str:
    """
    Ask a question to NotebookLM

    Args:
        question: Question to ask
        notebook_url: NotebookLM notebook URL
        headless: Run browser in headless mode

    Returns:
        Answer text from NotebookLM
    """
    auth = AuthManager()

    if not auth.is_authenticated():
        print("⚠️ Not authenticated. Run: python auth_manager.py setup")
        return None

    print(f"💬 Asking: {question}")
    print(f"📚 Notebook: {notebook_url}")

    playwright = None
    context = None

    try:
        # Start playwright
        playwright = sync_playwright().start()

        # Launch persistent browser context using factory
        context = BrowserFactory.launch_persistent_context(
            playwright,
            headless=headless
        )

        # Navigate to notebook
        page = context.new_page()
        print("  🌐 Opening notebook...")
        page.goto(notebook_url,
All rows above produced by Repobility · https://repobility.com
main function · python · L190-L252 (63 LOC)
skills/extensions/notebooklm/scripts/ask_question.py
def main():
    parser = argparse.ArgumentParser(description='Ask NotebookLM a question')

    parser.add_argument('--question', required=True, help='Question to ask')
    parser.add_argument('--notebook-url', help='NotebookLM notebook URL')
    parser.add_argument('--notebook-id', help='Notebook ID from library')
    parser.add_argument('--show-browser', action='store_true', help='Show browser')

    args = parser.parse_args()

    # Resolve notebook URL
    notebook_url = args.notebook_url

    if not notebook_url and args.notebook_id:
        library = NotebookLibrary()
        notebook = library.get_notebook(args.notebook_id)
        if notebook:
            notebook_url = notebook['url']
        else:
            print(f"❌ Notebook '{args.notebook_id}' not found")
            return 1

    if not notebook_url:
        # Check for active notebook first
        library = NotebookLibrary()
        active = library.get_active_notebook()
        if active:
            notebook_url = ac
AuthManager.__init__ method · python · L42-L50 (9 LOC)
skills/extensions/notebooklm/scripts/auth_manager.py
    def __init__(self):
        """Initialize the authentication manager"""
        # Ensure directories exist
        DATA_DIR.mkdir(parents=True, exist_ok=True)
        BROWSER_STATE_DIR.mkdir(parents=True, exist_ok=True)

        self.state_file = STATE_FILE
        self.auth_info_file = AUTH_INFO_FILE
        self.browser_state_dir = BROWSER_STATE_DIR
AuthManager.is_authenticated method · python · L52-L62 (11 LOC)
skills/extensions/notebooklm/scripts/auth_manager.py
    def is_authenticated(self) -> bool:
        """Check if valid authentication exists"""
        if not self.state_file.exists():
            return False

        # 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")

        return True
AuthManager.get_auth_info method · python · L64-L84 (21 LOC)
skills/extensions/notebooklm/scripts/auth_manager.py
    def get_auth_info(self) -> Dict[str, Any]:
        """Get authentication information"""
        info = {
            'authenticated': self.is_authenticated(),
            'state_file': str(self.state_file),
            'state_exists': self.state_file.exists()
        }

        if self.auth_info_file.exists():
            try:
                with open(self.auth_info_file, 'r') as f:
                    saved_info = json.load(f)
                    info.update(saved_info)
            except Exception:
                pass

        if info['state_exists']:
            age_hours = (time.time() - self.state_file.stat().st_mtime) / 3600
            info['state_age_hours'] = age_hours

        return info
AuthManager.setup_auth method · python · L86-L158 (73 LOC)
skills/extensions/notebooklm/scripts/auth_manager.py
    def setup_auth(self, headless: bool = False, timeout_minutes: int = 10) -> bool:
        """
        Perform interactive authentication setup

        Args:
            headless: Run browser in headless mode (False for login)
            timeout_minutes: Maximum time to wait for login

        Returns:
            True if authentication successful
        """
        print("🔐 Starting authentication setup...")
        print(f"  Timeout: {timeout_minutes} minutes")

        playwright = None
        context = None

        try:
            playwright = sync_playwright().start()

            # Launch using factory
            context = BrowserFactory.launch_persistent_context(
                playwright,
                headless=headless
            )

            # Navigate to NotebookLM
            page = context.new_page()
            page.goto("https://notebooklm.google.com", wait_until="domcontentloaded")

            # Check if already authenticated
            if "notebooklm.g
AuthManager._save_browser_state method · python · L160-L168 (9 LOC)
skills/extensions/notebooklm/scripts/auth_manager.py
    def _save_browser_state(self, context: BrowserContext):
        """Save browser state to disk"""
        try:
            # Save storage state (cookies, localStorage)
            context.storage_state(path=str(self.state_file))
            print(f"  💾 Saved browser state to: {self.state_file}")
        except Exception as e:
            print(f"  ❌ Failed to save browser state: {e}")
            raise
AuthManager._save_auth_info method · python · L170-L180 (11 LOC)
skills/extensions/notebooklm/scripts/auth_manager.py
    def _save_auth_info(self):
        """Save authentication metadata"""
        try:
            info = {
                'authenticated_at': time.time(),
                'authenticated_at_iso': time.strftime('%Y-%m-%d %H:%M:%S')
            }
            with open(self.auth_info_file, 'w') as f:
                json.dump(info, f, indent=2)
        except Exception:
            pass  # Non-critical
AuthManager.clear_auth method · python · L182-L212 (31 LOC)
skills/extensions/notebooklm/scripts/auth_manager.py
    def clear_auth(self) -> bool:
        """
        Clear all authentication data

        Returns:
            True if cleared successfully
        """
        print("🗑️ Clearing authentication data...")

        try:
            # Remove browser state
            if self.state_file.exists():
                self.state_file.unlink()
                print("  ✅ Removed browser state")

            # Remove auth info
            if self.auth_info_file.exists():
                self.auth_info_file.unlink()
                print("  ✅ 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")

            return True

        except Exception as e:
            print(f"  ❌ Error clearing auth: {e}")
            return False
Repobility — same analyzer, your code, free for public repos · /scan/
AuthManager.re_auth method · python · L214-L231 (18 LOC)
skills/extensions/notebooklm/scripts/auth_manager.py
    def re_auth(self, headless: bool = False, timeout_minutes: int = 10) -> bool:
        """
        Perform re-authentication (clear and setup)

        Args:
            headless: Run browser in headless mode
            timeout_minutes: Login timeout in minutes

        Returns:
            True if successful
        """
        print("🔄 Starting re-authentication...")

        # Clear existing auth
        self.clear_auth()

        # Setup new auth
        return self.setup_auth(headless, timeout_minutes)
AuthManager.validate_auth method · python · L233-L284 (52 LOC)
skills/extensions/notebooklm/scripts/auth_manager.py
    def validate_auth(self) -> bool:
        """
        Validate that stored authentication works
        Uses persistent context to match actual usage pattern

        Returns:
            True if authentication is valid
        """
        if not self.is_authenticated():
            return False

        print("🔍 Validating authentication...")

        playwright = None
        context = None

        try:
            playwright = sync_playwright().start()

            # Launch using factory
            context = BrowserFactory.launch_persistent_context(
                playwright,
                headless=True
            )

            # Try to access NotebookLM
            page = context.new_page()
            page.goto("https://notebooklm.google.com", wait_until="domcontentloaded", timeout=30000)

            # Check if we can access NotebookLM
            if "notebooklm.google.com" in page.url and "accounts.google.com" not in page.url:
                print("  ✅ Authentication 
main function · python · L287-L354 (68 LOC)
skills/extensions/notebooklm/scripts/auth_manager.py
def main():
    """Command-line interface for authentication management"""
    parser = argparse.ArgumentParser(description='Manage NotebookLM authentication')

    subparsers = parser.add_subparsers(dest='command', help='Commands')

    # Setup command
    setup_parser = subparsers.add_parser('setup', help='Setup authentication')
    setup_parser.add_argument('--headless', action='store_true', help='Run in headless mode')
    setup_parser.add_argument('--timeout', type=float, default=10, help='Login timeout in minutes (default: 10)')

    # Status command
    subparsers.add_parser('status', help='Check authentication status')

    # Validate command
    subparsers.add_parser('validate', help='Validate authentication')

    # Clear command
    subparsers.add_parser('clear', help='Clear authentication')

    # Re-auth command
    reauth_parser = subparsers.add_parser('reauth', help='Re-authenticate (clear + setup)')
    reauth_parser.add_argument('--timeout', type=float, default=10, hel
BrowserSession.__init__ method · python · L30-L49 (20 LOC)
skills/extensions/notebooklm/scripts/browser_session.py
    def __init__(self, session_id: str, context: BrowserContext, notebook_url: str):
        """
        Initialize a new browser session

        Args:
            session_id: Unique identifier for this session
            context: Browser context (shared or dedicated)
            notebook_url: Target NotebookLM URL for this session
        """
        self.id = session_id
        self.created_at = time.time()
        self.last_activity = time.time()
        self.message_count = 0
        self.notebook_url = notebook_url
        self.context = context
        self.page = None
        self.stealth = StealthUtils()

        # Initialize the session
        self._initialize()
BrowserSession._initialize method · python · L51-L80 (30 LOC)
skills/extensions/notebooklm/scripts/browser_session.py
    def _initialize(self):
        """Initialize the browser session and navigate to NotebookLM"""
        print(f"🚀 Creating session {self.id}...")

        # Create new page (tab) in context
        self.page = self.context.new_page()
        print(f"  🌐 Navigating to NotebookLM...")

        try:
            # Navigate to notebook
            self.page.goto(self.notebook_url, wait_until="domcontentloaded", timeout=30000)

            # Check if login is needed
            if "accounts.google.com" in self.page.url:
                raise RuntimeError("Authentication required. Please run auth_manager.py setup first.")

            # Wait for page to be ready
            self._wait_for_ready()

            # Simulate human inspection
            self.stealth.random_mouse_movement(self.page)
            self.stealth.random_delay(300, 600)

            print(f"✅ Session {self.id} ready!")

        except Exception as e:
            print(f"❌ Failed to initialize session: {e}")
           
BrowserSession._wait_for_ready method · python · L82-L89 (8 LOC)
skills/extensions/notebooklm/scripts/browser_session.py
    def _wait_for_ready(self):
        """Wait for NotebookLM page to be ready"""
        try:
            # Wait for chat input
            self.page.wait_for_selector("textarea.query-box-input", timeout=10000, state="visible")
        except Exception:
            # Try alternative selector
            self.page.wait_for_selector('textarea[aria-label="Feld für Anfragen"]', timeout=5000, state="visible")
BrowserSession.ask method · python · L91-L155 (65 LOC)
skills/extensions/notebooklm/scripts/browser_session.py
    def ask(self, question: str) -> Dict[str, Any]:
        """
        Ask a question in this session

        Args:
            question: The question to ask

        Returns:
            Dict with status, question, answer, session_id
        """
        try:
            self.last_activity = time.time()
            self.message_count += 1

            print(f"💬 [{self.id}] Asking: {question}")

            # Snapshot current answer to detect new response
            previous_answer = self._snapshot_latest_response()

            # Find chat input
            chat_input_selector = "textarea.query-box-input"
            try:
                self.page.wait_for_selector(chat_input_selector, timeout=5000, state="visible")
            except Exception:
                chat_input_selector = 'textarea[aria-label="Feld für Anfragen"]'
                self.page.wait_for_selector(chat_input_selector, timeout=5000, state="visible")

            # Click and type with human-like behavior
         
BrowserSession._snapshot_latest_response method · python · L157-L166 (10 LOC)
skills/extensions/notebooklm/scripts/browser_session.py
    def _snapshot_latest_response(self) -> Optional[str]:
        """Get the current latest response text"""
        try:
            # Use correct NotebookLM selector
            responses = self.page.query_selector_all(".to-user-container .message-text-content")
            if responses:
                return responses[-1].inner_text()
        except Exception:
            pass
        return None
About: code-quality intelligence by Repobility · https://repobility.com
BrowserSession._wait_for_latest_answer method · python · L168-L207 (40 LOC)
skills/extensions/notebooklm/scripts/browser_session.py
    def _wait_for_latest_answer(self, previous_answer: Optional[str], timeout: int = 120) -> str:
        """Wait for and extract the new answer"""
        start_time = time.time()
        last_candidate = None
        stable_count = 0

        while time.time() - start_time < timeout:
            # Check if NotebookLM is still thinking (most reliable indicator)
            try:
                thinking_element = self.page.query_selector('div.thinking-message')
                if thinking_element and thinking_element.is_visible():
                    time.sleep(0.5)
                    continue
            except Exception:
                pass

            try:
                # Use correct NotebookLM selector
                responses = self.page.query_selector_all(".to-user-container .message-text-content")

                if responses:
                    latest_text = responses[-1].inner_text().strip()

                    # Check if it's a new response
                    if lat
BrowserSession.reset method · python · L209-L221 (13 LOC)
skills/extensions/notebooklm/scripts/browser_session.py
    def reset(self):
        """Reset the chat by reloading the page"""
        print(f"🔄 Resetting session {self.id}...")

        self.page.reload(wait_until="domcontentloaded")
        self._wait_for_ready()

        previous_count = self.message_count
        self.message_count = 0
        self.last_activity = time.time()

        print(f"✅ Session reset (cleared {previous_count} messages)")
        return previous_count
BrowserSession.close method · python · L223-L233 (11 LOC)
skills/extensions/notebooklm/scripts/browser_session.py
    def close(self):
        """Close this session and clean up resources"""
        print(f"🛑 Closing session {self.id}...")

        if self.page:
            try:
                self.page.close()
            except Exception as e:
                print(f"  ⚠️ Error closing page: {e}")

        print(f"✅ Session {self.id} closed")
BrowserSession.get_info method · python · L235-L245 (11 LOC)
skills/extensions/notebooklm/scripts/browser_session.py
    def get_info(self) -> Dict[str, Any]:
        """Get information about this session"""
        return {
            "id": self.id,
            "created_at": self.created_at,
            "last_activity": self.last_activity,
            "age_seconds": time.time() - self.created_at,
            "inactive_seconds": time.time() - self.last_activity,
            "message_count": self.message_count,
            "notebook_url": self.notebook_url
        }
BrowserFactory.launch_persistent_context method · python · L19-L43 (25 LOC)
skills/extensions/notebooklm/scripts/browser_utils.py
    def launch_persistent_context(
        playwright: Playwright,
        headless: bool = True,
        user_data_dir: str = str(BROWSER_PROFILE_DIR)
    ) -> BrowserContext:
        """
        Launch a persistent browser context with anti-detection features
        and cookie workaround.
        """
        # Launch persistent context
        context = playwright.chromium.launch_persistent_context(
            user_data_dir=user_data_dir,
            channel="chrome",  # Use real Chrome
            headless=headless,
            no_viewport=True,
            ignore_default_args=["--enable-automation"],
            user_agent=USER_AGENT,
            args=BROWSER_ARGS
        )

        # Cookie Workaround for Playwright bug #36139
        # Session cookies (expires=-1) don't persist in user_data_dir automatically
        BrowserFactory._inject_cookies(context)

        return context
BrowserFactory._inject_cookies method · python · L46-L56 (11 LOC)
skills/extensions/notebooklm/scripts/browser_utils.py
    def _inject_cookies(context: BrowserContext):
        """Inject cookies from state.json if available"""
        if STATE_FILE.exists():
            try:
                with open(STATE_FILE, 'r') as f:
                    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")
            except Exception as e:
                print(f"  ⚠️  Could not load state.json: {e}")
StealthUtils.human_type method · python · L68-L89 (22 LOC)
skills/extensions/notebooklm/scripts/browser_utils.py
    def human_type(page: Page, selector: str, text: str, wpm_min: int = 320, wpm_max: int = 480):
        """Type with human-like speed"""
        element = page.query_selector(selector)
        if not element:
            # Try waiting if not immediately found
            try:
                element = page.wait_for_selector(selector, timeout=2000)
            except:
                pass
        
        if not element:
            print(f"⚠️ Element not found for typing: {selector}")
            return

        # Click to focus
        element.click()
        
        # Type
        for char in text:
            element.type(char, delay=random.uniform(25, 75))
            if random.random() < 0.05:
                time.sleep(random.uniform(0.15, 0.4))
StealthUtils.realistic_click method · python · L92-L107 (16 LOC)
skills/extensions/notebooklm/scripts/browser_utils.py
    def realistic_click(page: Page, selector: str):
        """Click with realistic movement"""
        element = page.query_selector(selector)
        if not element:
            return

        # Optional: Move mouse to element (simplified)
        box = element.bounding_box()
        if box:
            x = box['x'] + box['width'] / 2
            y = box['y'] + box['height'] / 2
            page.mouse.move(x, y, steps=5)

        StealthUtils.random_delay(100, 300)
        element.click()
        StealthUtils.random_delay(100, 300)
Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
CleanupManager.get_cleanup_paths method · python · L30-L114 (85 LOC)
skills/extensions/notebooklm/scripts/cleanup_manager.py
    def get_cleanup_paths(self, preserve_library: bool = False) -> Dict[str, Any]:
        """
        Get paths that would be cleaned up

        Args:
            preserve_library: Keep library.json if True

        Returns:
            Dict with paths and sizes

        Note: .venv is NEVER deleted - it's part of the skill infrastructure
        """
        paths = {
            'browser_state': [],
            'sessions': [],
            'library': [],
            'auth': [],
            'other': []
        }

        total_size = 0

        if self.data_dir.exists():
            # Browser state
            browser_state_dir = self.data_dir / "browser_state"
            if browser_state_dir.exists():
                for item in browser_state_dir.iterdir():
                    size = self._get_size(item)
                    paths['browser_state'].append({
                        'path': str(item),
                        'size': size,
                        'type': 'dir' if item.is
CleanupManager._get_size method · python · L116-L129 (14 LOC)
skills/extensions/notebooklm/scripts/cleanup_manager.py
    def _get_size(self, path: Path) -> int:
        """Get size of file or directory in bytes"""
        if path.is_file():
            return path.stat().st_size
        elif path.is_dir():
            total = 0
            try:
                for item in path.rglob('*'):
                    if item.is_file():
                        total += item.stat().st_size
            except Exception:
                pass
            return total
        return 0
CleanupManager._format_size method · python · L131-L137 (7 LOC)
skills/extensions/notebooklm/scripts/cleanup_manager.py
    def _format_size(self, size: int) -> str:
        """Format size in human-readable form"""
        for unit in ['B', 'KB', 'MB', 'GB']:
            if size < 1024:
                return f"{size:.1f} {unit}"
            size /= 1024
        return f"{size:.1f} TB"
CleanupManager.perform_cleanup method · python · L139-L197 (59 LOC)
skills/extensions/notebooklm/scripts/cleanup_manager.py
    def perform_cleanup(
        self,
        preserve_library: bool = False,
        dry_run: bool = False
    ) -> Dict[str, Any]:
        """
        Perform the actual cleanup

        Args:
            preserve_library: Keep library.json if True
            dry_run: Preview only, don't delete

        Returns:
            Dict with cleanup results
        """
        cleanup_data = self.get_cleanup_paths(preserve_library)
        deleted_items = []
        failed_items = []
        deleted_size = 0

        if dry_run:
            return {
                'dry_run': True,
                'would_delete': cleanup_data['total_items'],
                'would_free': cleanup_data['total_size']
            }

        # Perform deletion
        for category, items in cleanup_data['categories'].items():
            for item_info in items:
                path = Path(item_info['path'])
                try:
                    if path.exists():
                        if path.is_dir():
    
CleanupManager.print_cleanup_preview method · python · L199-L223 (25 LOC)
skills/extensions/notebooklm/scripts/cleanup_manager.py
    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("=" * 60)

        for category, items in data['categories'].items():
            if items:
                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 "📄"
                    print(f"  {type_icon} {path.name:<30} {size_str:>10}")

        print("\n" + "=" * 60)
        print(f"Total items: {data['total_items']}")
        print(f"Total size: {self._format_size(data['total_size'])}")

        if preserve_library:
            print("\n📚 Library will be preserved")

        print("\nThis preview shows what would be deleted.")
        print("Use --c
main function · python · L226-L298 (73 LOC)
skills/extensions/notebooklm/scripts/cleanup_manager.py
def main():
    """Command-line interface for cleanup management"""
    parser = argparse.ArgumentParser(
        description='Clean up NotebookLM skill data',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  # Preview what will be deleted
  python cleanup_manager.py

  # Perform cleanup (delete everything)
  python cleanup_manager.py --confirm

  # Cleanup but keep library
  python cleanup_manager.py --confirm --preserve-library

  # Force cleanup without preview
  python cleanup_manager.py --confirm --force
        """
    )

    parser.add_argument(
        '--confirm',
        action='store_true',
        help='Actually perform the cleanup (without this, only preview)'
    )

    parser.add_argument(
        '--preserve-library',
        action='store_true',
        help='Keep the notebook library (library.json)'
    )

    parser.add_argument(
        '--force',
        action='store_true',
        help='Skip confirmation prompt'
    )

ensure_venv_and_run function · python · L13-L77 (65 LOC)
skills/extensions/notebooklm/scripts/__init__.py
def ensure_venv_and_run():
    """
    Ensure virtual environment exists and run the requested script.
    This is called when any script is imported or run directly.
    """
    # Only do this if we're not already in the skill's venv
    skill_dir = Path(__file__).parent.parent
    venv_dir = skill_dir / ".venv"

    # Check if we're in a venv
    in_venv = hasattr(sys, 'real_prefix') or (
        hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix
    )

    # Check if it's OUR venv
    if in_venv:
        venv_path = Path(sys.prefix)
        if venv_path == venv_dir:
            # We're already in the correct venv
            return

    # 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...")

        # Create venv
        import venv
        venv.create(venv_dir, with_pip=T
NotebookLibrary.__init__ method · python · L20-L32 (13 LOC)
skills/extensions/notebooklm/scripts/notebook_manager.py
    def __init__(self):
        """Initialize the notebook library"""
        # Store data within the skill directory
        skill_dir = Path(__file__).parent.parent
        self.data_dir = skill_dir / "data"
        self.data_dir.mkdir(parents=True, exist_ok=True)

        self.library_file = self.data_dir / "library.json"
        self.notebooks: Dict[str, Dict[str, Any]] = {}
        self.active_notebook_id: Optional[str] = None

        # Load existing library
        self._load_library()
All rows above produced by Repobility · https://repobility.com
NotebookLibrary._load_library method · python · L34-L48 (15 LOC)
skills/extensions/notebooklm/scripts/notebook_manager.py
    def _load_library(self):
        """Load library from disk"""
        if self.library_file.exists():
            try:
                with open(self.library_file, 'r') as f:
                    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")
            except Exception as e:
                print(f"⚠️ Error loading library: {e}")
                self.notebooks = {}
                self.active_notebook_id = None
        else:
            self._save_library()
NotebookLibrary._save_library method · python · L50-L61 (12 LOC)
skills/extensions/notebooklm/scripts/notebook_manager.py
    def _save_library(self):
        """Save library to disk"""
        try:
            data = {
                'notebooks': self.notebooks,
                'active_notebook_id': self.active_notebook_id,
                'updated_at': datetime.now().isoformat()
            }
            with open(self.library_file, 'w') as f:
                json.dump(data, f, indent=2)
        except Exception as e:
            print(f"❌ Error saving library: {e}")
NotebookLibrary.add_notebook method · python · L63-L121 (59 LOC)
skills/extensions/notebooklm/scripts/notebook_manager.py
    def add_notebook(
        self,
        url: str,
        name: str,
        description: str,
        topics: List[str],
        content_types: Optional[List[str]] = None,
        use_cases: Optional[List[str]] = None,
        tags: Optional[List[str]] = None
    ) -> Dict[str, Any]:
        """
        Add a new notebook to the library

        Args:
            url: NotebookLM notebook URL
            name: Display name for the notebook
            description: What's in this notebook
            topics: Topics covered
            content_types: Types of content (optional)
            use_cases: When to use this notebook (optional)
            tags: Additional tags for organization (optional)

        Returns:
            The created notebook object
        """
        # Generate ID from name
        notebook_id = name.lower().replace(' ', '-').replace('_', '-')

        # Check for duplicates
        if notebook_id in self.notebooks:
            raise ValueError(f"Notebook with
NotebookLibrary.remove_notebook method · python · L123-L148 (26 LOC)
skills/extensions/notebooklm/scripts/notebook_manager.py
    def remove_notebook(self, notebook_id: str) -> bool:
        """
        Remove a notebook from the library

        Args:
            notebook_id: ID of notebook to remove

        Returns:
            True if removed, False if not found
        """
        if notebook_id in self.notebooks:
            del self.notebooks[notebook_id]

            # Clear active if it was removed
            if self.active_notebook_id == notebook_id:
                self.active_notebook_id = None
                # Set new active if there are other notebooks
                if self.notebooks:
                    self.active_notebook_id = list(self.notebooks.keys())[0]

            self._save_library()
            print(f"✅ Removed notebook: {notebook_id}")
            return True

        print(f"⚠️ Notebook not found: {notebook_id}")
        return False
NotebookLibrary.update_notebook method · python · L150-L196 (47 LOC)
skills/extensions/notebooklm/scripts/notebook_manager.py
    def update_notebook(
        self,
        notebook_id: str,
        name: Optional[str] = None,
        description: Optional[str] = None,
        topics: Optional[List[str]] = None,
        content_types: Optional[List[str]] = None,
        use_cases: Optional[List[str]] = None,
        tags: Optional[List[str]] = None,
        url: Optional[str] = None
    ) -> Dict[str, Any]:
        """
        Update notebook metadata

        Args:
            notebook_id: ID of notebook to update
            Other args: Fields to update (None = keep existing)

        Returns:
            Updated notebook object
        """
        if notebook_id not in self.notebooks:
            raise ValueError(f"Notebook not found: {notebook_id}")

        notebook = self.notebooks[notebook_id]

        # Update fields if provided
        if name is not None:
            notebook['name'] = name
        if description is not None:
            notebook['description'] = description
        if topics is not
NotebookLibrary.search_notebooks method · python · L206-L232 (27 LOC)
skills/extensions/notebooklm/scripts/notebook_manager.py
    def search_notebooks(self, query: str) -> List[Dict[str, Any]]:
        """
        Search notebooks by query

        Args:
            query: Search query (searches name, description, topics, tags)

        Returns:
            List of matching notebooks
        """
        query_lower = query.lower()
        results = []

        for notebook in self.notebooks.values():
            # Search in various fields
            searchable = [
                notebook['name'].lower(),
                notebook['description'].lower(),
                ' '.join(notebook['topics']).lower(),
                ' '.join(notebook['tags']).lower(),
                ' '.join(notebook.get('use_cases', [])).lower()
            ]

            if any(query_lower in field for field in searchable):
                results.append(notebook)

        return results
NotebookLibrary.select_notebook method · python · L234-L252 (19 LOC)
skills/extensions/notebooklm/scripts/notebook_manager.py
    def select_notebook(self, notebook_id: str) -> Dict[str, Any]:
        """
        Set a notebook as active

        Args:
            notebook_id: ID of notebook to activate

        Returns:
            The activated notebook
        """
        if notebook_id not in self.notebooks:
            raise ValueError(f"Notebook not found: {notebook_id}")

        self.active_notebook_id = notebook_id
        self._save_library()

        notebook = self.notebooks[notebook_id]
        print(f"✅ Activated notebook: {notebook['name']}")
        return notebook
NotebookLibrary.increment_use_count method · python · L260-L278 (19 LOC)
skills/extensions/notebooklm/scripts/notebook_manager.py
    def increment_use_count(self, notebook_id: str) -> Dict[str, Any]:
        """
        Increment usage counter for a notebook

        Args:
            notebook_id: ID of notebook that was used

        Returns:
            Updated notebook
        """
        if notebook_id not in self.notebooks:
            raise ValueError(f"Notebook not found: {notebook_id}")

        notebook = self.notebooks[notebook_id]
        notebook['use_count'] += 1
        notebook['last_used'] = datetime.now().isoformat()

        self._save_library()
        return notebook
Repobility — same analyzer, your code, free for public repos · /scan/
NotebookLibrary.get_stats method · python · L280-L305 (26 LOC)
skills/extensions/notebooklm/scripts/notebook_manager.py
    def get_stats(self) -> Dict[str, Any]:
        """Get library statistics"""
        total_notebooks = len(self.notebooks)
        total_topics = set()
        total_use_count = 0

        for notebook in self.notebooks.values():
            total_topics.update(notebook['topics'])
            total_use_count += notebook['use_count']

        # Find most used
        most_used = None
        if self.notebooks:
            most_used = max(
                self.notebooks.values(),
                key=lambda n: n['use_count']
            )

        return {
            'total_notebooks': total_notebooks,
            'total_topics': len(total_topics),
            'total_use_count': total_use_count,
            'active_notebook': self.get_active_notebook(),
            'most_used_notebook': most_used,
            'library_path': str(self.library_file)
        }
main function · python · L308-L406 (99 LOC)
skills/extensions/notebooklm/scripts/notebook_manager.py
def main():
    """Command-line interface for notebook management"""
    parser = argparse.ArgumentParser(description='Manage NotebookLM library')

    subparsers = parser.add_subparsers(dest='command', help='Commands')

    # Add command
    add_parser = subparsers.add_parser('add', help='Add a notebook')
    add_parser.add_argument('--url', required=True, help='NotebookLM URL')
    add_parser.add_argument('--name', required=True, help='Display name')
    add_parser.add_argument('--description', required=True, help='Description')
    add_parser.add_argument('--topics', required=True, help='Comma-separated topics')
    add_parser.add_argument('--use-cases', help='Comma-separated use cases')
    add_parser.add_argument('--tags', help='Comma-separated tags')

    # List command
    subparsers.add_parser('list', help='List all notebooks')

    # Search command
    search_parser = subparsers.add_parser('search', help='Search notebooks')
    search_parser.add_argument('--query', required=Tr
get_venv_python function · python · L13-L23 (11 LOC)
skills/extensions/notebooklm/scripts/run.py
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
‹ prevpage 2 / 3next ›