{"owner":"teng-lin","repo":"notebooklm-py","hasSkills":true,"hasMcp":false,"mcpConfig":null,"found":["CLAUDE.md","AGENTS.md"],"files":{"CLAUDE.md":"# CLAUDE.md\n\nGuidance for Claude Code working in this repo. Also follow the file/naming conventions in [CONTRIBUTING.md](CONTRIBUTING.md).\n\n## Project Overview\n\n`notebooklm-py` is an unofficial **async** Python client for Google NotebookLM. It drives Google's internal `batchexecute` RPC protocol to automate notebooks, sources, AI querying, and studio artifacts (podcasts, videos, quizzes, …).\n\n**Critical constraint:** the obfuscated RPC method IDs in `src/notebooklm/rpc/types.py` are undocumented and can break whenever Google changes them — the #1 breakage class.\n\n## Development Commands\n\n```bash\n# Canonical contributor install — matches the extras CI's test job installs.\n# Full guide: docs/installation.md\nuv sync --frozen --extra browser --extra dev --extra markdown \\\n        --extra mcp --extra server --extra impersonate\nsource .venv/bin/activate\nuv run playwright install chromium\n\nuv run pytest                     # all tests (e2e excluded by default)\nuv run pytest --cov               # with coverage\nuv run pytest tests/e2e -m e2e    # e2e (requires auth)\nuv run notebooklm --help          # CLI\n```\n\n**Install the full extras set.** Omitting `mcp`/`server` does not fail — it\n*skips*. `tests/unit/mcp/` and the server suites are `importorskip`-guarded, so\nwithout `fastmcp`/`fastapi` they vanish silently and their source counts as\nuncovered. Measured on one commit: the three-extra install runs **13,939** tests\nat **84.39%** coverage and fails the 90% gate, while CI's set runs **15,478** at\n**96.61%** and passes. That ~1,500-test blind spot hid a real MCP-adapter defect\nthrough eight red CI jobs on #2198.\n\nTo reproduce a CI test run exactly (`.github/workflows/test.yml`):\n\n```bash\nuv run pytest -n auto --dist loadgroup --cov=src/notebooklm \\\n  --cov-report=term-missing --cov-fail-under=90\n```\n\n`--dist loadgroup` matters: it honors `@pytest.mark.xdist_group`, and at least\none test fails under plain `-n auto` but passes under `loadgroup`. Check the\nexit status directly rather than through a pipe — piping into `tail`/`grep`\nreports the *pipeline's* status, which has masked real failures here before.\n\n## Before Pushing\n\nThe pre-commit hook runs ruff (format + lint) on staged files. Also run these manually — CI fails otherwise:\n\n```bash\nuv run mypy src/notebooklm scripts/_live_auth_scenarios --ignore-missing-imports\nuv run pytest\n```\n\n## Architecture\n\n`cli/` (Click) → `_app/` (transport-neutral business logic, reusable by MCP/HTTP adapters) → `client.py` + `_*.py` (client runtime) → `rpc/` (batchexecute encode/decode).\n\nSee **[docs/architecture.md](docs/architecture.md)** for the layered design, call flows, cross-cutting policies (loop affinity, idempotency, schema validation), the per-file index, and the full repository tree.\n\n## Common Pitfalls\n\n1. **RPC method IDs change** — re-capture network traffic and update `rpc/types.py`.\n2. **Position-sensitive nested params** — copy the shape from an existing implementation; source-id nesting varies (`[id]` / `[[id]]` / `[[[id]]]` / `[[[[id]]]]`).\n3. **CSRF tokens expire** — call `client.refresh_auth()` or re-run `notebooklm login`.\n4. **Rate limiting** — add delays between bulk operations.\n5. **Concurrency** — one `NotebookLMClient` is bound to its `open()`-time event loop: create one per thread, never reuse across event loops or `AuthTokens` tenants. See the [concurrency contract](docs/python-api.md#concurrency-contract).\n\n## Usage\n\n```python\nasync with NotebookLMClient.from_storage() as client:\n    notebooks = await client.notebooks.list()\n    await client.sources.add_url(nb_id, url)\n    answer = await client.chat.ask(nb_id, question)\n    status = await client.artifacts.generate_audio(nb_id)\n```\n\nCLI: top-level commands (`login`, `use`, `status`, `list`, `ask`) plus grouped subcommands (`source add`, `label list`, `artifact list`, `generate audio`, `download video`, `note create`, `mcp install <client>`, …). Full reference: [docs/cli-reference.md](docs/cli-reference.md).\n\nAn opt-in MCP server (`mcp` extra, console script `notebooklm-mcp`) exposes the same `_app/` business logic over the Model Context Protocol; `notebooklm mcp install <client>` wires it into Claude Desktop/Code, Cursor, or Windsurf, and `desktop-extension/` packages a one-click `.mcpb` bundle.\n\nAn opt-in single-tenant REST server (`server` extra, console script `notebooklm-server`) exposes guarded `/v1` FastAPI routes over the same `_app/` layer. It is experimental, loopback-bound by default, and requires `NOTEBOOKLM_SERVER_TOKEN`; see [docs/installation.md#rest-api-server](docs/installation.md#rest-api-server).\n\n## Testing\n\nUnit (`tests/unit/`, no network; includes `_app`, CLI, server, and guardrail tests) · integration (`tests/integration/`, VCR cassette replay) · e2e (`tests/e2e/`, real API, `@pytest.mark.e2e`). VCR cassettes match on the full tuple `[\"method\", \"scheme\", \"host\", \"port\", \"path\", \"rpcids\", \"freq\"]` (`tests/vcr_config.py`) — note that `host` is part of the match, so replay is pinned to the host a cassette was recorded against, and `freq` compares the decoded `f.req` form body. Details: [docs/development.md](docs/development.md).\n\n## Docs\n\n`docs/`: installation · cli-reference · python-api · configuration · troubleshooting · development · architecture · mcp-guide · rpc-development · rpc-reference · stability · adr/.\n\n## Pull Request Workflow (required)\n\nAfter opening a PR, drive it to merge:\n\n1. Poll `gh pr checks <PR>` until all pass; investigate and fix any failures.\n2. Address every review comment (especially `gemini-code-assist`): make the fix, push, then reply on the thread (`Addressed in <SHA>: …`). Unreplied threads block merge.\n3. Not done until all checks pass, all threads addressed, and `mergeStateStatus` is `CLEAN`.\n\nClaude review is **not** automatic — comment `@claude review` on the PR to trigger the `.github/workflows/claude.yml` workflow. Treat `claude[bot]` as a first-class reviewer the merge gate **waits on**, alongside `gemini-code-assist` / `coderabbitai`:\n\n- It posts inline review-thread comments **plus** a sticky summary comment (\"**Claude finished … task**\"). The action does **not** submit a formal GitHub review, so `claude[bot]` never appears in `gh pr view --json reviews` / `reviewDecision` and is **not** a required check — do not infer \"claude reviewed\" from those.\n- `gh pr checks` may show a `claude` entry as **skipping**: every comment (incl. other bots') fires `claude.yml`, and runs not from a `teng-lin` `@claude` comment correctly skip via the job `if:` gate. That skip is **not** the review run — find the real one with `gh run list --workflow=claude.yml --json event,conclusion` (look for the `success` run) or just read the comment below.\n- Before merging, confirm the review landed and address it. The two halves live on different endpoints: the sticky summary is an **issue** comment (`gh api /repos/<owner>/<repo>/issues/<PR>/comments`), and the inline findings are **pull-request review** comments on the diff (`gh api /repos/<owner>/<repo>/pulls/<PR>/comments`) — filter either with `--jq '.[]|select(.user.login==\"claude[bot]\").body'`. Resolve any inline `claude[bot]` threads like any other bot's.\n","AGENTS.md":"# Repository Guidelines\n\n**Status:** Active\n**Last Updated:** 2026-06-11\n\n## Project Structure & Module Organization\n\n`src/notebooklm/` contains the async client and typed APIs. Internal feature modules use `_` prefixes such as `_sources.py`, `_artifacts.py`, `_app/`, and `_runtime/`; `src/notebooklm/cli/` holds Click adapters, `src/notebooklm/mcp/` and `src/notebooklm/server/` hold the opt-in MCP and REST adapters, and `src/notebooklm/rpc/` handles protocol encoding and decoding. Tests are split by scope: `tests/unit/`, `tests/integration/`, `tests/server/`, and `tests/e2e/`. Recorded HTTP fixtures live in `tests/cassettes/`. Examples are in `examples/`, and diagnostics live in `scripts/`.\n\n## Build, Test, and Development Commands\n\nCanonical contributor install (full guide: [docs/installation.md](docs/installation.md)):\n\n```bash\nuv sync --frozen --extra browser --extra dev --extra markdown\nsource .venv/bin/activate\nuv run playwright install chromium\nuv run pytest\nuv run pytest -n auto --dist=worksteal  # optional faster local run\nuv run ruff check .\nuv run ruff format .\nuv run mypy src/notebooklm\nuv run pre-commit run --all-files\n```\n\nRun `uv run pytest tests/e2e -m readonly` only after `notebooklm login` and setting test notebook env vars.\n\n## Coding Style & Naming Conventions\n\nTarget Python 3.10+, 4-space indentation, and double quotes. Ruff enforces formatting and import order with a 100-character line length. Keep module and test file names in `snake_case`; prefer descriptive Click command names that match existing groups such as `source`, `label`, `artifact`, and `research`. Preserve the internal/public split: `_*.py` and `_*/` for implementation, exported types in `src/notebooklm/__init__.py`.\n\n## Testing Guidelines\n\nPut pure logic in `tests/unit/`, REST adapter coverage in `tests/server/`, VCR-backed flows in `tests/integration/`, and authenticated NotebookLM coverage in `tests/e2e/`. Name tests `test_<behavior>.py` and record cassettes with `NOTEBOOKLM_VCR_RECORD=1 uv run pytest tests/integration/ -v` (the integration suite uses `vcrpy` throughout — there is no `test_vcr_*.py` glob). Coverage is expected to stay at or above the configured 90% threshold.\n\n## Commit, PR, and Agent Notes\n\nFollow the existing commit style: `feat(cli): ...`, `fix(cli): ...`, `refactor(test): ...`, `style: ...`. PRs should include a short summary, linked issue when relevant, and the commands run locally.\n\nFor Codex or other parallel agents:\n\n- Prefer `--json` output and pass explicit notebook IDs instead of relying on `notebooklm use`.\n- Isolate concurrent runs with `NOTEBOOKLM_PROFILE=agent-<id>` so each agent gets its own context file under `~/.notebooklm/profiles/<name>/`. Fall back to `NOTEBOOKLM_HOME=/tmp/agent-<id>` only when separate home directories are required.\n- In headless environments where Playwright login is impractical, authenticate with `notebooklm login --browser-cookies <browser>` (requires `pip install \"notebooklm-py[cookies]\"`).\n"}}