Automate creation and maintenance of per-executive NotebookLMs from a Google Sheet feeder.
For each row in the feeder sheet, ebcprep:
- Looks up (or creates) a notebook for that executive.
- Adds the meeting's prep doc as a Drive source to the notebook.
- Renames the source so it reads
{company} — {date} {start}–{end}. - Shares the notebook with the executive (once, on first sync).
Re-runs are idempotent — meeting rows are deduplicated by content hash, so unchanged rows are skipped.
- docs/USER_GUIDE.md — step-by-step guide for non-technical operators (install, GCP setup, sheet preparation, daily use, troubleshooting).
- docs/ARCHITECTURE.md — design decisions, module responsibilities, sync algorithm, idempotency model, extension points.
The rest of this README is a quick reference.
Header row 1, data starting row 2.
| Column | Meaning |
|---|---|
| B | company name |
| D | meeting date |
| E | start time |
| F | end time |
| H | executive name |
| I | Google Doc URL |
| L | executive email |
Other columns are ignored.
-
nlmCLI installed and authenticated under a named profile:uv tool install notebooklm-mcp-cli nlm login --profile ebcprep
The notebooks are created in this Google account; collaborators are invited from it.
-
GCP service account with Sheets read access:
- Create a service account in any GCP project.
- Generate a JSON key, save it locally.
- In Google Sheets, share the feeder sheet with the service account's email
(
...@<project>.iam.gserviceaccount.com) as Viewer.
cd ebcprep-orchestrator
uv tool install --from . ebcprep
# or for development:
uv pip install -e .cp .env.example .env
# edit .env: set EBCPREP_SERVICE_ACCOUNT_JSON, EBCPREP_SHEET_ID, EBCPREP_NLM_PROFILE
ebcprep doctorIf you want always-included collaborators (people who get added to every
notebook in addition to the executive) or default sources (URLs added to
every notebook), create a defaults.toml:
cp defaults.toml.example defaults.toml
# edit defaults.toml
ebcprep doctor # confirm the entries are picked upRe-running ebcprep sync is safe — invites and sources are only added if not
already present on the notebook.
If you want to manage notebook access only via always_collaborator (e.g. a
shared ebc-team@ group) and not invite the executives directly:
- One-time, per run:
ebcprep sync --no-invite-execs - Permanently in
.env:EBCPREP_SKIP_EXEC_INVITE=true
# Preview what would happen, no writes
ebcprep sync --dry-run
# Real sync
ebcprep sync
# Limit to one exec
ebcprep sync --only-exec "Jane Doe"
# Show last run + per-exec totals
ebcprep status
# List exec → notebook URL
ebcprep execs
# Show one exec's meetings (from local state)
ebcprep meetings "Jane Doe"Local SQLite DB at data/ebcprep.db:
executives— name, email, notebook_id, share statusmeetings— one row per (exec, doc_url); content-hash primary key for dedupsync_runs— one row perebcprep syncinvocation
The DB is the source of truth for "what's already been ingested." Delete it to force a full re-sync (existing notebooks will be matched by title and reused, not duplicated).
- Remove sources when a row is deleted from the sheet (history is preserved).
- Update source titles when sheet metadata changes for an existing row.
- Generate AI briefings (planned for Phase 3).
- Web portal (planned for Phase 2 —
ebcprep[portal]extra).
Google Sheet (feeder) ──gspread──▶ ebcprep ──notebooklm_tools.services──▶ NotebookLM
│
├─ SQLite (state, dedup, sync history)
└─ rich-formatted CLI output
ebcprep does not subprocess nlm — it imports notebooklm_tools.services directly and calls them with a client obtained via notebooklm_tools.cli.utils.get_client(profile).