From 173d84ea27998b4a616809af97a1fc397ef9582f Mon Sep 17 00:00:00 2001 From: liuche <1257067825@qq.com> Date: Tue, 17 Mar 2026 16:58:19 +0800 Subject: [PATCH 1/4] feat: add notebooklm agent harness scaffold --- .gitignore | 6 +- .../2026-03-17-notebooklm-harness-scaffold.md | 192 ++++++++++++ docs/plans/2026-03-17-notebooklm-pr-polish.md | 116 +++++++ notebooklm/agent-harness/ATTRIBUTION.md | 29 ++ notebooklm/agent-harness/NOTEBOOKLM.md | 44 +++ .../agent-harness/THIRD_PARTY_NOTICES.md | 26 ++ .../cli_anything/notebooklm/README.md | 112 +++++++ .../cli_anything/notebooklm/__init__.py | 3 + .../cli_anything/notebooklm/__main__.py | 7 + .../cli_anything/notebooklm/core/__init__.py | 1 + .../cli_anything/notebooklm/core/session.py | 42 +++ .../cli_anything/notebooklm/notebooklm_cli.py | 282 ++++++++++++++++++ .../cli_anything/notebooklm/skills/SKILL.md | 78 +++++ .../cli_anything/notebooklm/tests/TEST.md | 64 ++++ .../cli_anything/notebooklm/tests/__init__.py | 1 + .../notebooklm/tests/test_cli_smoke.py | 86 ++++++ .../notebooklm/tests/test_core.py | 96 ++++++ .../notebooklm/tests/test_manual_e2e.md | 11 + .../cli_anything/notebooklm/utils/__init__.py | 1 + .../notebooklm/utils/notebooklm_backend.py | 91 ++++++ notebooklm/agent-harness/setup.py | 54 ++++ 21 files changed, 1341 insertions(+), 1 deletion(-) create mode 100644 docs/plans/2026-03-17-notebooklm-harness-scaffold.md create mode 100644 docs/plans/2026-03-17-notebooklm-pr-polish.md create mode 100644 notebooklm/agent-harness/ATTRIBUTION.md create mode 100644 notebooklm/agent-harness/NOTEBOOKLM.md create mode 100644 notebooklm/agent-harness/THIRD_PARTY_NOTICES.md create mode 100644 notebooklm/agent-harness/cli_anything/notebooklm/README.md create mode 100644 notebooklm/agent-harness/cli_anything/notebooklm/__init__.py create mode 100644 notebooklm/agent-harness/cli_anything/notebooklm/__main__.py create mode 100644 notebooklm/agent-harness/cli_anything/notebooklm/core/__init__.py create mode 100644 notebooklm/agent-harness/cli_anything/notebooklm/core/session.py create mode 100644 notebooklm/agent-harness/cli_anything/notebooklm/notebooklm_cli.py create mode 100644 notebooklm/agent-harness/cli_anything/notebooklm/skills/SKILL.md create mode 100644 notebooklm/agent-harness/cli_anything/notebooklm/tests/TEST.md create mode 100644 notebooklm/agent-harness/cli_anything/notebooklm/tests/__init__.py create mode 100644 notebooklm/agent-harness/cli_anything/notebooklm/tests/test_cli_smoke.py create mode 100644 notebooklm/agent-harness/cli_anything/notebooklm/tests/test_core.py create mode 100644 notebooklm/agent-harness/cli_anything/notebooklm/tests/test_manual_e2e.md create mode 100644 notebooklm/agent-harness/cli_anything/notebooklm/utils/__init__.py create mode 100644 notebooklm/agent-harness/cli_anything/notebooklm/utils/notebooklm_backend.py create mode 100644 notebooklm/agent-harness/setup.py diff --git a/.gitignore b/.gitignore index 8a8e23618..5dc592593 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ !/README.md !/assets/ !/.claude-plugin/ +!/docs/ # Step 3: Allow cli-anything-plugin, codex-skill, and skill_generation tests !/cli-anything-plugin/ @@ -99,4 +100,7 @@ assets/gen_typing_gif.py !README_CN.md !opencode-commands/ !/qoder-plugin/ - +!/notebooklm/ +/notebooklm/* +/notebooklm/.* +!/notebooklm/agent-harness/ diff --git a/docs/plans/2026-03-17-notebooklm-harness-scaffold.md b/docs/plans/2026-03-17-notebooklm-harness-scaffold.md new file mode 100644 index 000000000..5d38a7faa --- /dev/null +++ b/docs/plans/2026-03-17-notebooklm-harness-scaffold.md @@ -0,0 +1,192 @@ +# NotebookLM Harness Scaffold Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Add an experimental `cli-anything-notebooklm` scaffold that wraps an installed `notebooklm` CLI, includes attribution files, and establishes the first round of tests and docs. + +**Architecture:** Create a service-style harness under `notebooklm/agent-harness/` that mirrors the existing CLI-Anything package layout. The backend adapter will shell out to the installed `notebooklm` command, sanitize sensitive errors, and keep notebook context in a lightweight session file for REPL use. + +**Tech Stack:** Python 3.10+, Click, prompt-toolkit, pytest, subprocess-based backend wrapper + +### Task 1: Establish the tracked scaffold area + +**Files:** +- Modify: `.gitignore` +- Create: `docs/plans/2026-03-17-notebooklm-harness-scaffold.md` + +**Step 1: Write the failing test** + +No behavior test for this task. It is repository plumbing needed to track the new harness path and plan document. + +**Step 2: Run test to verify it fails** + +Not applicable. + +**Step 3: Write minimal implementation** + +- Allow `docs/` +- Allow `notebooklm/` +- Allow `notebooklm/agent-harness/` + +**Step 4: Run test to verify it passes** + +Run: `git status --short` +Expected: new scaffold files are visible to git + +**Step 5: Commit** + +```bash +git add .gitignore docs/plans/2026-03-17-notebooklm-harness-scaffold.md +git commit -m "chore: prepare notebooklm harness scaffold" +``` + +### Task 2: Write the first failing backend tests + +**Files:** +- Create: `notebooklm/agent-harness/cli_anything/notebooklm/tests/test_core.py` +- Test: `notebooklm/agent-harness/cli_anything/notebooklm/tests/test_core.py` + +**Step 1: Write the failing test** + +Cover: +- backend finds `notebooklm` on PATH +- backend raises clear install guidance if missing +- backend builds explicit commands with `-n ` +- backend sanitizes secret-bearing auth path output +- session file stores current notebook context + +**Step 2: Run test to verify it fails** + +Run: `cd notebooklm/agent-harness && python3 -m pytest cli_anything/notebooklm/tests/test_core.py -q` +Expected: FAIL with import errors because the package does not exist yet + +**Step 3: Write minimal implementation** + +Create: +- `cli_anything/notebooklm/utils/notebooklm_backend.py` +- `cli_anything/notebooklm/core/session.py` + +**Step 4: Run test to verify it passes** + +Run: `cd notebooklm/agent-harness && python3 -m pytest cli_anything/notebooklm/tests/test_core.py -q` +Expected: PASS + +**Step 5: Commit** + +```bash +git add notebooklm/agent-harness/cli_anything/notebooklm +git commit -m "feat: add notebooklm backend scaffold" +``` + +### Task 3: Write the first failing CLI tests + +**Files:** +- Create: `notebooklm/agent-harness/cli_anything/notebooklm/tests/test_cli_smoke.py` +- Test: `notebooklm/agent-harness/cli_anything/notebooklm/tests/test_cli_smoke.py` + +**Step 1: Write the failing test** + +Cover: +- root `--help` shows experimental NotebookLM wording +- root `--help` exposes command groups +- `auth status --help` and `notebook list --help` execute + +**Step 2: Run test to verify it fails** + +Run: `cd notebooklm/agent-harness && python3 -m pytest cli_anything/notebooklm/tests/test_cli_smoke.py -q` +Expected: FAIL because `notebooklm_cli.py` is missing + +**Step 3: Write minimal implementation** + +Create: +- `cli_anything/notebooklm/notebooklm_cli.py` +- `cli_anything/notebooklm/__init__.py` +- `cli_anything/notebooklm/__main__.py` + +Use placeholder command handlers that route through the backend wrapper. + +**Step 4: Run test to verify it passes** + +Run: `cd notebooklm/agent-harness && python3 -m pytest cli_anything/notebooklm/tests/test_cli_smoke.py -q` +Expected: PASS + +**Step 5: Commit** + +```bash +git add notebooklm/agent-harness/cli_anything/notebooklm/notebooklm_cli.py +git commit -m "feat: add notebooklm cli skeleton" +``` + +### Task 4: Add packaging and attribution docs + +**Files:** +- Create: `notebooklm/agent-harness/setup.py` +- Create: `notebooklm/agent-harness/NOTEBOOKLM.md` +- Create: `notebooklm/agent-harness/ATTRIBUTION.md` +- Create: `notebooklm/agent-harness/THIRD_PARTY_NOTICES.md` +- Create: `notebooklm/agent-harness/cli_anything/notebooklm/README.md` +- Create: `notebooklm/agent-harness/cli_anything/notebooklm/skills/SKILL.md` +- Create: `notebooklm/agent-harness/cli_anything/notebooklm/tests/TEST.md` +- Create: `notebooklm/agent-harness/cli_anything/notebooklm/tests/test_manual_e2e.md` + +**Step 1: Write the failing test** + +Add assertions that: +- `setup.py` exposes `cli-anything-notebooklm` +- skill file exists +- README includes acknowledgement text + +**Step 2: Run test to verify it fails** + +Run: `cd notebooklm/agent-harness && python3 -m pytest cli_anything/notebooklm/tests/test_core.py -q` +Expected: FAIL on missing files and packaging metadata + +**Step 3: Write minimal implementation** + +Create all packaging and documentation files with explicit attribution and experimental disclaimers. + +**Step 4: Run test to verify it passes** + +Run: `cd notebooklm/agent-harness && python3 -m pytest cli_anything/notebooklm/tests/test_core.py cli_anything/notebooklm/tests/test_cli_smoke.py -q` +Expected: PASS + +**Step 5: Commit** + +```bash +git add notebooklm/agent-harness +git commit -m "docs: add notebooklm harness attribution and packaging" +``` + +### Task 5: Verify the scaffold end to end + +**Files:** +- Modify: `notebooklm/agent-harness/cli_anything/notebooklm/tests/TEST.md` + +**Step 1: Write the failing test** + +No new behavior. This task is verification and documentation. + +**Step 2: Run test to verify it fails** + +Not applicable. + +**Step 3: Write minimal implementation** + +Run: +- `cd notebooklm/agent-harness && python3 -m pytest cli_anything/notebooklm/tests/test_core.py cli_anything/notebooklm/tests/test_cli_smoke.py -q` +- `cd notebooklm/agent-harness && python3 -m cli_anything.notebooklm.notebooklm_cli --help` + +Append the verification notes to `TEST.md`. + +**Step 4: Run test to verify it passes** + +Expected: +- targeted tests PASS +- CLI help exits 0 + +**Step 5: Commit** + +```bash +git add notebooklm/agent-harness/cli_anything/notebooklm/tests/TEST.md +git commit -m "test: verify notebooklm harness scaffold" +``` diff --git a/docs/plans/2026-03-17-notebooklm-pr-polish.md b/docs/plans/2026-03-17-notebooklm-pr-polish.md new file mode 100644 index 000000000..3b2f0d536 --- /dev/null +++ b/docs/plans/2026-03-17-notebooklm-pr-polish.md @@ -0,0 +1,116 @@ +# NotebookLM PR Polish Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Tighten the experimental NotebookLM harness into a more upstream-ready contribution with stronger docs, explicit attribution, and subprocess-level CLI smoke coverage. + +**Architecture:** Keep the current service-style wrapper architecture intact. Improve PR readiness by expanding package-facing documentation and adding lightweight subprocess tests that verify the packaged CLI interface without requiring a live authenticated NotebookLM session. + +**Tech Stack:** Python 3, Click, pytest, subprocess, Markdown docs + +### Task 1: Add failing tests for PR-facing expectations + +**Files:** +- Modify: `notebooklm/agent-harness/cli_anything/notebooklm/tests/test_core.py` +- Modify: `notebooklm/agent-harness/cli_anything/notebooklm/tests/test_cli_smoke.py` + +**Step 1: Write the failing test** + +Add tests that require: +- README to document installation, test execution, safety boundaries, and references +- SKILL.md to include installation, usage, and explicit unofficial guidance +- subprocess CLI help to render through a resolved `cli-anything-notebooklm` entry path + +**Step 2: Run test to verify it fails** + +Run: `cd notebooklm/agent-harness && python3 -m pytest cli_anything/notebooklm/tests/test_core.py cli_anything/notebooklm/tests/test_cli_smoke.py -q` + +Expected: FAIL on missing README/SKILL sections or missing subprocess helper behavior + +**Step 3: Write minimal implementation** + +Expand documentation and add the smallest test helper needed for subprocess smoke coverage. + +**Step 4: Run test to verify it passes** + +Run: `cd notebooklm/agent-harness && python3 -m pytest cli_anything/notebooklm/tests/test_core.py cli_anything/notebooklm/tests/test_cli_smoke.py -q` + +Expected: PASS + +**Step 5: Commit** + +```bash +git add notebooklm/agent-harness/cli_anything/notebooklm/tests notebooklm/agent-harness/cli_anything/notebooklm/README.md notebooklm/agent-harness/cli_anything/notebooklm/skills/SKILL.md +git commit -m "test: harden notebooklm harness smoke coverage" +``` + +### Task 2: Improve docs for upstream review + +**Files:** +- Modify: `notebooklm/agent-harness/cli_anything/notebooklm/README.md` +- Modify: `notebooklm/agent-harness/cli_anything/notebooklm/skills/SKILL.md` +- Modify: `notebooklm/agent-harness/NOTEBOOKLM.md` + +**Step 1: Write the failing test** + +Covered by Task 1 doc assertions. + +**Step 2: Run test to verify it fails** + +Covered by Task 1. + +**Step 3: Write minimal implementation** + +Document: +- installation and local prerequisites +- run and test commands +- command group overview +- agent guidance and safe-by-default behavior +- explicit references and unofficial attribution boundary + +**Step 4: Run test to verify it passes** + +Run: `cd notebooklm/agent-harness && python3 -m pytest cli_anything/notebooklm/tests/test_core.py cli_anything/notebooklm/tests/test_cli_smoke.py -q` + +Expected: PASS + +**Step 5: Commit** + +```bash +git add notebooklm/agent-harness/NOTEBOOKLM.md notebooklm/agent-harness/cli_anything/notebooklm/README.md notebooklm/agent-harness/cli_anything/notebooklm/skills/SKILL.md +git commit -m "docs: polish notebooklm harness contribution docs" +``` + +### Task 3: Verify and record final state + +**Files:** +- Modify: `notebooklm/agent-harness/cli_anything/notebooklm/tests/TEST.md` + +**Step 1: Write the failing test** + +No new behavior test. This task records verification evidence. + +**Step 2: Run test to verify it fails** + +Not applicable. + +**Step 3: Write minimal implementation** + +Run: +- `cd notebooklm/agent-harness && python3 -m pytest cli_anything/notebooklm/tests -q` +- `cd notebooklm/agent-harness && python3 -m cli_anything.notebooklm.notebooklm_cli --help` + +Append fresh results to `TEST.md`. + +**Step 4: Run test to verify it passes** + +Expected: +- all NotebookLM scaffold tests PASS +- module help exits 0 and prints command groups + +**Step 5: Commit** + +```bash +git add notebooklm/agent-harness/cli_anything/notebooklm/tests/TEST.md +git commit -m "test: record notebooklm harness verification" +``` diff --git a/notebooklm/agent-harness/ATTRIBUTION.md b/notebooklm/agent-harness/ATTRIBUTION.md new file mode 100644 index 000000000..bb27d4bcb --- /dev/null +++ b/notebooklm/agent-harness/ATTRIBUTION.md @@ -0,0 +1,29 @@ +# Attribution + +This harness adapts the CLI-Anything methodology for NotebookLM. + +## Acknowledgements + +- **CLI-Anything** + - Project: https://github.com/HKUDS/CLI-Anything + - Methodology: https://github.com/HKUDS/CLI-Anything/blob/main/cli-anything-plugin/HARNESS.md + - We follow its agent-native CLI conventions, including REPL-first design, JSON output, package layout, and test documentation patterns. + +- **notebooklm-py** + - Project: https://github.com/teng-lin/notebooklm-py + - PyPI: https://pypi.org/project/notebooklm-py/ + - This harness is designed to interoperate with the installed `notebooklm` CLI distributed by notebooklm-py. + +- **Google NotebookLM** + - Product help: https://support.google.com/notebooklm/answer/16206563 + - NotebookLM is a Google product. This harness is unofficial and not affiliated with or endorsed by Google. + +## Design Boundary + +This project prefers composition over copying: + +- wrap the installed `notebooklm` CLI +- document upstream dependencies and policies +- avoid vendoring third-party NotebookLM implementation code into this repository + +If you extend this harness, preserve these acknowledgements and keep the unofficial / experimental disclaimer intact. diff --git a/notebooklm/agent-harness/NOTEBOOKLM.md b/notebooklm/agent-harness/NOTEBOOKLM.md new file mode 100644 index 000000000..c53731dab --- /dev/null +++ b/notebooklm/agent-harness/NOTEBOOKLM.md @@ -0,0 +1,44 @@ +# NotebookLM: Project-Specific Analysis & SOP + +## Architecture Summary + +NotebookLM is a hosted Google research and content-generation product. Unlike the +local GUI applications that CLI-Anything usually targets, this harness wraps an +installed `notebooklm` command-line client that already manages authentication, +source ingestion, chat, artifact generation, and downloads. + +This harness therefore behaves as a **service-style CLI wrapper**: + +1. Resolve the local `notebooklm` executable. +2. Build explicit commands with notebook context where needed. +3. Sanitize sensitive auth-related error output. +4. Persist lightweight local session state for REPL convenience. + +## Backend Strategy + +- Prefer the installed `notebooklm` CLI over reimplementing NotebookLM internals. +- Keep credentials outside the repository and outside test fixtures. +- Treat this integration as experimental and unofficial. +- Require explicit confirmation for destructive or high-impact operations. +- Preserve clear attribution to CLI-Anything, notebooklm-py, and Google NotebookLM documentation. + +## Contribution Boundary + +This harness is designed to be safe for an upstream contribution review: + +- it wraps an installed community CLI instead of vendoring third-party NotebookLM code +- it documents copyright and service-boundary concerns instead of implying official support +- it limits automated verification to non-destructive smoke coverage unless a local authenticated session is intentionally used +- it keeps end-to-end authenticated testing manual so secrets and account state stay out of CI and fixtures + +## Constraints + +- Depends on a valid local Google-authenticated NotebookLM session. +- Depends on behavior provided by the installed `notebooklm` CLI. +- Full authenticated end-to-end tests are manual by design. + +## References + +- CLI-Anything methodology: https://github.com/HKUDS/CLI-Anything +- notebooklm-py project: https://github.com/teng-lin/notebooklm-py +- Google NotebookLM help: https://support.google.com/notebooklm/answer/16206563 diff --git a/notebooklm/agent-harness/THIRD_PARTY_NOTICES.md b/notebooklm/agent-harness/THIRD_PARTY_NOTICES.md new file mode 100644 index 000000000..dea35669f --- /dev/null +++ b/notebooklm/agent-harness/THIRD_PARTY_NOTICES.md @@ -0,0 +1,26 @@ +# Third-Party Notices + +## notebooklm-py + +- Homepage: https://github.com/teng-lin/notebooklm-py +- PyPI: https://pypi.org/project/notebooklm-py/ +- License: MIT + +This harness is intended to work with the `notebooklm` CLI provided by notebooklm-py. + +## Click + +Used for CLI command structure. + +## prompt-toolkit + +Used for REPL interaction helpers. + +## Important Boundary + +Google NotebookLM is a third-party online service. This harness is an unofficial +integration layer and is not affiliated with Google. Users are responsible for: + +- complying with Google terms and product restrictions +- protecting their own credentials and local auth state +- respecting copyright for imported sources diff --git a/notebooklm/agent-harness/cli_anything/notebooklm/README.md b/notebooklm/agent-harness/cli_anything/notebooklm/README.md new file mode 100644 index 000000000..2751f83d8 --- /dev/null +++ b/notebooklm/agent-harness/cli_anything/notebooklm/README.md @@ -0,0 +1,112 @@ +# CLI-Anything NotebookLM Harness + +Experimental NotebookLM harness for CLI-Anything. + +This package wraps an installed `notebooklm` CLI and exposes a CLI-Anything-style +interface for authentication checks, notebook selection, source management, chat, +artifact generation, downloads, and sharing. + +## Status + +- Experimental +- Community-maintained +- Unofficial and not affiliated with Google + +## Requirements + +- Python 3.10+ +- An installed `notebooklm` command +- A valid local NotebookLM login session + +## Install + +```bash +cd notebooklm/agent-harness +python3 -m pip install -e . +``` + +If the upstream NotebookLM CLI is not installed yet: + +```bash +python3 -m pip install --user 'notebooklm-py[browser]' +python3 -m playwright install chromium +``` + +## Run + +```bash +# Show help +cli-anything-notebooklm --help + +# Check auth state +cli-anything-notebooklm auth status + +# List notebooks +cli-anything-notebooklm notebook list +``` + +## Run Tests + +```bash +cd notebooklm/agent-harness +python3 -m pytest cli_anything/notebooklm/tests -q +python3 -m cli_anything.notebooklm.notebooklm_cli --help +``` + +## Command Groups + +| Group | Purpose | +| --- | --- | +| `auth` | login, auth status, and lightweight auth checks | +| `notebook` | list, create, and summarize notebooks | +| `source` | inspect sources and add URL sources | +| `chat` | ask questions and inspect history | +| `artifact` | list artifacts and generate reports | +| `download` | download generated artifacts | +| `share` | inspect sharing status | + +## Common Workflows + +```bash +cli-anything-notebooklm auth status +cli-anything-notebooklm notebook list +cli-anything-notebooklm source list --notebook nb_123 +cli-anything-notebooklm chat ask "Summarize the current notebook" +cli-anything-notebooklm artifact generate-report --notebook nb_123 +``` + +## For AI Agents + +- Prefer explicit notebook IDs with `--notebook` instead of relying on ambient state. +- Use `--json` whenever the command supports machine-readable output. +- Treat NotebookLM auth state as sensitive local data and never print cookie or storage files. +- Treat this harness as a thin wrapper around `notebooklm`, not a reimplementation of NotebookLM. + +## Acknowledgements + +This harness is inspired by the CLI-Anything methodology: +https://github.com/HKUDS/CLI-Anything + +It is designed to work with the community-maintained `notebooklm` CLI from `notebooklm-py`: +https://github.com/teng-lin/notebooklm-py + +NotebookLM is a Google product: +https://support.google.com/notebooklm/answer/16206563 + +This project is unofficial and not affiliated with Google. + +## Safety Notes + +- Do not commit local auth state into the repository. +- Do not upload sensitive content without permission. +- Respect copyright and service terms for imported sources. +- Prefer review-oriented or read-only commands first when working inside a live notebook. +- Treat sharing and artifact generation as user-impacting operations that deserve explicit intent. + +## References + +- CLI-Anything: https://github.com/HKUDS/CLI-Anything +- CLI-Anything HARNESS.md: https://github.com/HKUDS/CLI-Anything/blob/main/cli-anything-plugin/HARNESS.md +- notebooklm-py: https://github.com/teng-lin/notebooklm-py +- notebooklm-py on PyPI: https://pypi.org/project/notebooklm-py/ +- Google NotebookLM help: https://support.google.com/notebooklm/answer/16206563 diff --git a/notebooklm/agent-harness/cli_anything/notebooklm/__init__.py b/notebooklm/agent-harness/cli_anything/notebooklm/__init__.py new file mode 100644 index 000000000..c8601e771 --- /dev/null +++ b/notebooklm/agent-harness/cli_anything/notebooklm/__init__.py @@ -0,0 +1,3 @@ +"""NotebookLM harness package.""" + +__version__ = "0.1.0" diff --git a/notebooklm/agent-harness/cli_anything/notebooklm/__main__.py b/notebooklm/agent-harness/cli_anything/notebooklm/__main__.py new file mode 100644 index 000000000..fd1753810 --- /dev/null +++ b/notebooklm/agent-harness/cli_anything/notebooklm/__main__.py @@ -0,0 +1,7 @@ +"""Module entry point for cli-anything-notebooklm.""" + +from cli_anything.notebooklm.notebooklm_cli import main + + +if __name__ == "__main__": + main() diff --git a/notebooklm/agent-harness/cli_anything/notebooklm/core/__init__.py b/notebooklm/agent-harness/cli_anything/notebooklm/core/__init__.py new file mode 100644 index 000000000..2c7cef76d --- /dev/null +++ b/notebooklm/agent-harness/cli_anything/notebooklm/core/__init__.py @@ -0,0 +1 @@ +"""Core modules for the NotebookLM harness.""" diff --git a/notebooklm/agent-harness/cli_anything/notebooklm/core/session.py b/notebooklm/agent-harness/cli_anything/notebooklm/core/session.py new file mode 100644 index 000000000..995e6b01c --- /dev/null +++ b/notebooklm/agent-harness/cli_anything/notebooklm/core/session.py @@ -0,0 +1,42 @@ +"""Session persistence helpers for NotebookLM CLI context.""" + +from __future__ import annotations + +import json +from pathlib import Path + + +class Session: + """Persist the active notebook for REPL and one-shot commands.""" + + def __init__(self, session_file: str | Path | None = None): + if session_file is None: + session_file = Path.home() / ".cli-anything-notebooklm" / "session.json" + self.session_file = Path(session_file) + self.session_file.parent.mkdir(parents=True, exist_ok=True) + self._data = self._load() + + def _load(self) -> dict: + if not self.session_file.exists(): + return {"active_notebook": None} + try: + return json.loads(self.session_file.read_text(encoding="utf-8")) + except json.JSONDecodeError: + return {"active_notebook": None} + + def _save(self): + self.session_file.write_text( + json.dumps(self._data, indent=2, ensure_ascii=True), + encoding="utf-8", + ) + + def get_active_notebook(self) -> str | None: + return self._data.get("active_notebook") + + def set_active_notebook(self, notebook_id: str): + self._data["active_notebook"] = notebook_id + self._save() + + def clear_active_notebook(self): + self._data["active_notebook"] = None + self._save() diff --git a/notebooklm/agent-harness/cli_anything/notebooklm/notebooklm_cli.py b/notebooklm/agent-harness/cli_anything/notebooklm/notebooklm_cli.py new file mode 100644 index 000000000..53640752b --- /dev/null +++ b/notebooklm/agent-harness/cli_anything/notebooklm/notebooklm_cli.py @@ -0,0 +1,282 @@ +#!/usr/bin/env python3 +"""NotebookLM CLI — Experimental NotebookLM wrapper for AI agents.""" + +from __future__ import annotations + +import json +import sys + +import click + +from cli_anything.notebooklm import __version__ +from cli_anything.notebooklm.core.session import Session +from cli_anything.notebooklm.utils.notebooklm_backend import run_notebooklm + +_json_output = False +_session: Session | None = None + + +def get_session() -> Session: + global _session + if _session is None: + _session = Session() + return _session + + +def emit(data, message: str = ""): + if _json_output: + click.echo(json.dumps(data, indent=2, default=str)) + elif message: + click.echo(message) + elif isinstance(data, str): + click.echo(data) + else: + click.echo(json.dumps(data, indent=2, default=str)) + + +def handle_error(func): + def wrapper(*args, **kwargs): + try: + return func(*args, **kwargs) + except Exception as exc: # pragma: no cover - scaffold behavior + if _json_output: + click.echo(json.dumps({"error": str(exc), "type": type(exc).__name__})) + else: + click.echo(f"Error: {exc}", err=True) + sys.exit(1) + + wrapper.__name__ = func.__name__ + wrapper.__doc__ = func.__doc__ + return wrapper + + +def resolve_notebook_id(notebook_id: str | None) -> str | None: + return notebook_id or get_session().get_active_notebook() + + +@click.group(invoke_without_command=True) +@click.option("--json", "use_json", is_flag=True, help="Output as JSON") +@click.option("--notebook", "notebook_id", default=None, help="Active notebook ID") +@click.pass_context +def cli(ctx, use_json, notebook_id): + """NotebookLM CLI — Experimental NotebookLM wrapper for AI agents.""" + global _json_output + _json_output = use_json + if notebook_id: + get_session().set_active_notebook(notebook_id) + if ctx.invoked_subcommand is None: + ctx.invoke(repl) + + +@cli.command() +def repl(): + """Start a minimal REPL placeholder.""" + click.echo(f"cli-anything-notebooklm v{__version__}") + click.echo("Experimental harness scaffold. Use --help to inspect command groups.") + + +@cli.group() +def auth(): + """Authentication and login helpers.""" + + +@auth.command("status") +@handle_error +def auth_status(): + """Check authentication status.""" + emit(run_notebooklm(["status"], json_output=_json_output)) + + +@auth.command("login") +@handle_error +def auth_login(): + """Open the browser login flow.""" + emit(run_notebooklm(["login"], json_output=_json_output)) + + +@auth.command("check") +@handle_error +def auth_check(): + """Run a lightweight authentication check.""" + emit(run_notebooklm(["auth", "check"], json_output=_json_output)) + + +@cli.group() +def notebook(): + """Notebook management commands.""" + + +@notebook.command("list") +@handle_error +def notebook_list(): + """List notebooks.""" + emit(run_notebooklm(["list"], json_output=_json_output)) + + +@notebook.command("create") +@click.argument("name") +@handle_error +def notebook_create(name): + """Create a notebook.""" + emit(run_notebooklm(["create", name], json_output=_json_output)) + + +@notebook.command("summary") +@handle_error +def notebook_summary(): + """Summarize the active notebook.""" + emit( + run_notebooklm( + ["summary"], + notebook_id=resolve_notebook_id(None), + json_output=_json_output, + ) + ) + + +@cli.group() +def source(): + """Source ingestion and inspection commands.""" + + +@source.command("list") +@click.option("--notebook", "notebook_id", default=None, help="Notebook ID") +@handle_error +def source_list(notebook_id): + """List sources for a notebook.""" + emit( + run_notebooklm( + ["source", "list"], + notebook_id=resolve_notebook_id(notebook_id), + json_output=_json_output, + ) + ) + + +@source.command("add-url") +@click.argument("url") +@click.option("--notebook", "notebook_id", default=None, help="Notebook ID") +@handle_error +def source_add_url(url, notebook_id): + """Add a URL source.""" + emit( + run_notebooklm( + ["source", "add", url], + notebook_id=resolve_notebook_id(notebook_id), + json_output=_json_output, + ) + ) + + +@cli.group() +def chat(): + """Chat and history commands.""" + + +@chat.command("ask") +@click.argument("prompt") +@click.option("--notebook", "notebook_id", default=None, help="Notebook ID") +@handle_error +def chat_ask(prompt, notebook_id): + """Ask a question against a notebook.""" + emit( + run_notebooklm( + ["ask", prompt], + notebook_id=resolve_notebook_id(notebook_id), + json_output=_json_output, + ) + ) + + +@chat.command("history") +@click.option("--notebook", "notebook_id", default=None, help="Notebook ID") +@handle_error +def chat_history(notebook_id): + """Show chat history.""" + emit( + run_notebooklm( + ["history"], + notebook_id=resolve_notebook_id(notebook_id), + json_output=_json_output, + ) + ) + + +@cli.group() +def artifact(): + """Artifact generation and inspection commands.""" + + +@artifact.command("list") +@click.option("--notebook", "notebook_id", default=None, help="Notebook ID") +@handle_error +def artifact_list(notebook_id): + """List notebook artifacts.""" + emit( + run_notebooklm( + ["artifact", "list"], + notebook_id=resolve_notebook_id(notebook_id), + json_output=_json_output, + ) + ) + + +@artifact.command("generate-report") +@click.option("--notebook", "notebook_id", default=None, help="Notebook ID") +@handle_error +def artifact_generate_report(notebook_id): + """Generate a report artifact.""" + emit( + run_notebooklm( + ["generate", "report", "--wait"], + notebook_id=resolve_notebook_id(notebook_id), + json_output=_json_output, + ) + ) + + +@cli.group() +def download(): + """Artifact download helpers.""" + + +@download.command("report") +@click.argument("output_path") +@click.option("--notebook", "notebook_id", default=None, help="Notebook ID") +@handle_error +def download_report(output_path, notebook_id): + """Download the latest report artifact.""" + emit( + run_notebooklm( + ["download", "report", output_path], + notebook_id=resolve_notebook_id(notebook_id), + json_output=_json_output, + ) + ) + + +@cli.group() +def share(): + """Sharing and access control commands.""" + + +@share.command("status") +@click.option("--notebook", "notebook_id", default=None, help="Notebook ID") +@handle_error +def share_status(notebook_id): + """Inspect notebook sharing state.""" + emit( + run_notebooklm( + ["share", "status"], + notebook_id=resolve_notebook_id(notebook_id), + json_output=_json_output, + ) + ) + + +def main(): + cli() + + +if __name__ == "__main__": + main() diff --git a/notebooklm/agent-harness/cli_anything/notebooklm/skills/SKILL.md b/notebooklm/agent-harness/cli_anything/notebooklm/skills/SKILL.md new file mode 100644 index 000000000..9a8c6b514 --- /dev/null +++ b/notebooklm/agent-harness/cli_anything/notebooklm/skills/SKILL.md @@ -0,0 +1,78 @@ +--- +name: cli-anything-notebooklm +description: Experimental NotebookLM harness for listing notebooks, managing sources, asking questions, generating artifacts, and downloading outputs through an installed notebooklm CLI. +--- + +# cli-anything-notebooklm + +Experimental NotebookLM harness for CLI-Anything. + +## Installation + +This package is intended to be installed from the harness directory: + +```bash +cd notebooklm/agent-harness +python3 -m pip install -e . +``` + +Install the upstream NotebookLM CLI if needed: + +```bash +python3 -m pip install --user 'notebooklm-py[browser]' +python3 -m playwright install chromium +``` + +## Requirements + +- `notebooklm` command installed locally +- Valid local NotebookLM login session + +## Usage + +### Basic Commands + +```bash +# Show help +cli-anything-notebooklm --help + +# Start with a notebook context +cli-anything-notebooklm --notebook nb_123 source list + +# Prefer JSON for agent use +cli-anything-notebooklm --json notebook list +``` + +## Command Groups + +| Group | Purpose | +| --- | --- | +| `auth` | login and auth status | +| `notebook` | notebook list, create, summary | +| `source` | source listing and URL add | +| `chat` | ask questions and inspect history | +| `artifact` | list and generate artifacts | +| `download` | fetch generated outputs | +| `share` | inspect sharing state | + +## Agent Workflow + +1. Check auth with `cli-anything-notebooklm auth status` +2. Discover notebook IDs with `cli-anything-notebooklm --json notebook list` +3. Use explicit `--notebook` for follow-up commands +4. Prefer `--json` for list and inspection commands + +## Agent Guidance + +- Prefer explicit notebook IDs with `--notebook`. +- Use `--json` for machine-readable output. +- Treat this harness as experimental and unofficial. +- Do not expose auth files or cookies in logs. +- NotebookLM is a Google product; this harness is unofficial and not affiliated with Google. + +## References + +- CLI-Anything: https://github.com/HKUDS/CLI-Anything +- CLI-Anything HARNESS.md: https://github.com/HKUDS/CLI-Anything/blob/main/cli-anything-plugin/HARNESS.md +- notebooklm-py: https://github.com/teng-lin/notebooklm-py +- Google NotebookLM help: https://support.google.com/notebooklm/answer/16206563 diff --git a/notebooklm/agent-harness/cli_anything/notebooklm/tests/TEST.md b/notebooklm/agent-harness/cli_anything/notebooklm/tests/TEST.md new file mode 100644 index 000000000..26a3afe23 --- /dev/null +++ b/notebooklm/agent-harness/cli_anything/notebooklm/tests/TEST.md @@ -0,0 +1,64 @@ +# NotebookLM Harness - Test Documentation + +## Test Inventory + +| File | Focus | +| --- | --- | +| `test_core.py` | backend discovery, command building, session persistence, packaging fixtures | +| `test_cli_smoke.py` | help output and command group exposure | +| `test_manual_e2e.md` | authenticated local smoke-test checklist | + +## Coverage Notes + +This harness has three validation layers: + +- unit tests for backend helpers and session state +- smoke tests for CLI help and command registration +- manual authenticated end-to-end verification + +Because NotebookLM depends on a live Google account session and an installed +community CLI, full authenticated E2E coverage is not run in public CI. + +## Local Verification + +Verified on 2026-03-17 in the notebooklm harness worktree. + +### Commands Run + +```bash +python3 -m pytest cli_anything/notebooklm/tests/test_core.py -q +python3 -m pytest cli_anything/notebooklm/tests/test_cli_smoke.py -q +python3 -m cli_anything.notebooklm.notebooklm_cli --help +``` + +### Results + +- `test_core.py`: 9 passed +- `test_cli_smoke.py`: 5 passed +- `python3 -m cli_anything.notebooklm.notebooklm_cli --help`: exit code 0, help text rendered correctly + +### Notes + +- Added a regression test for module execution so `python -m cli_anything.notebooklm.notebooklm_cli --help` is covered, not just Click's in-process `CliRunner`. +- The authenticated `notebooklm` backend remains intentionally manual for end-to-end verification because it depends on a local Google session. + +## PR Polish Verification + +Verified on 2026-03-17 after README, SKILL, and subprocess smoke coverage upgrades. + +### Commands Run + +```bash +python3 -m pytest cli_anything/notebooklm/tests -q +python3 -m cli_anything.notebooklm.notebooklm_cli --help +``` + +### Results + +- Full NotebookLM harness suite: 17 passed +- Module help command: exit code 0, rendered command groups correctly + +### Notes + +- Added doc-level assertions so the package README and skill file now explicitly cover installation, tests, safety boundaries, and unofficial Google attribution. +- Added `_resolve_cli`-style subprocess smoke coverage so the CLI can be exercised through a resolved command path, not only through Click's in-process test runner. diff --git a/notebooklm/agent-harness/cli_anything/notebooklm/tests/__init__.py b/notebooklm/agent-harness/cli_anything/notebooklm/tests/__init__.py new file mode 100644 index 000000000..9eb399dd0 --- /dev/null +++ b/notebooklm/agent-harness/cli_anything/notebooklm/tests/__init__.py @@ -0,0 +1 @@ +"""Tests for the NotebookLM harness.""" diff --git a/notebooklm/agent-harness/cli_anything/notebooklm/tests/test_cli_smoke.py b/notebooklm/agent-harness/cli_anything/notebooklm/tests/test_cli_smoke.py new file mode 100644 index 000000000..e9c440f64 --- /dev/null +++ b/notebooklm/agent-harness/cli_anything/notebooklm/tests/test_cli_smoke.py @@ -0,0 +1,86 @@ +"""CLI smoke tests for the NotebookLM harness scaffold.""" + +import os +from pathlib import Path +import shutil +import subprocess +import sys + +from click.testing import CliRunner + +from cli_anything.notebooklm.notebooklm_cli import cli + + +def _resolve_cli(name): + """Resolve installed CLI command; fall back to python -m for local dev.""" + force = os.environ.get("CLI_ANYTHING_FORCE_INSTALLED", "").strip() == "1" + path = shutil.which(name) + if path: + return [path], None + if force: + raise RuntimeError(f"{name} not found in PATH. Install with: pip install -e .") + + module = "cli_anything.notebooklm.notebooklm_cli" + package_root = Path(__file__).resolve().parents[3] + env = os.environ.copy() + current = env.get("PYTHONPATH", "") + env["PYTHONPATH"] = ( + f"{package_root}{os.pathsep}{current}" if current else str(package_root) + ) + return [sys.executable, "-m", module], env + + +class TestRootHelp: + def test_root_help_shows_experimental_notebooklm(self): + result = CliRunner().invoke(cli, ["--help"]) + assert result.exit_code == 0 + assert "NotebookLM CLI" in result.output + assert "Experimental" in result.output + + def test_root_help_lists_command_groups(self): + result = CliRunner().invoke(cli, ["--help"]) + assert result.exit_code == 0 + for group in ["auth", "notebook", "source", "chat", "artifact", "download", "share"]: + assert group in result.output + + +class TestSubcommandHelp: + def test_auth_status_help(self): + result = CliRunner().invoke(cli, ["auth", "status", "--help"]) + assert result.exit_code == 0 + assert "Check authentication status" in result.output + + def test_notebook_list_help(self): + result = CliRunner().invoke(cli, ["notebook", "list", "--help"]) + assert result.exit_code == 0 + assert "List notebooks" in result.output + + +class TestModuleExecution: + def test_python_m_module_help_emits_output(self): + result = subprocess.run( + [ + sys.executable, + "-m", + "cli_anything.notebooklm.notebooklm_cli", + "--help", + ], + capture_output=True, + text=True, + check=False, + ) + assert result.returncode == 0 + assert "NotebookLM CLI" in result.stdout + + def test_resolved_cli_help_emits_output(self): + command, env = _resolve_cli("cli-anything-notebooklm") + result = subprocess.run( + command + ["--help"], + capture_output=True, + text=True, + check=False, + env=env, + ) + assert result.returncode == 0 + assert "NotebookLM CLI" in result.stdout + assert "auth" in result.stdout diff --git a/notebooklm/agent-harness/cli_anything/notebooklm/tests/test_core.py b/notebooklm/agent-harness/cli_anything/notebooklm/tests/test_core.py new file mode 100644 index 000000000..7ef1a4889 --- /dev/null +++ b/notebooklm/agent-harness/cli_anything/notebooklm/tests/test_core.py @@ -0,0 +1,96 @@ +"""Unit tests for NotebookLM harness scaffold.""" + +import json +from pathlib import Path +from unittest.mock import patch + +import pytest + +from cli_anything.notebooklm.core.session import Session +from cli_anything.notebooklm.utils.notebooklm_backend import ( + build_command, + require_notebooklm, + sanitize_error, +) + + +class TestBackendDiscovery: + def test_require_notebooklm_returns_path(self): + with patch("cli_anything.notebooklm.utils.notebooklm_backend.shutil.which", return_value="/usr/local/bin/notebooklm"): + assert require_notebooklm() == "/usr/local/bin/notebooklm" + + def test_require_notebooklm_raises_with_install_guidance(self): + with patch("cli_anything.notebooklm.utils.notebooklm_backend.shutil.which", return_value=None): + with pytest.raises(RuntimeError, match="notebooklm command not found"): + require_notebooklm() + + +class TestCommandBuilder: + def test_build_command_with_notebook_id_and_json(self): + command = build_command( + ["source", "list"], + notebook_id="nb_123", + json_output=True, + ) + assert command == [ + "notebooklm", + "source", + "list", + "-n", + "nb_123", + "--json", + ] + + def test_build_command_without_notebook_id(self): + command = build_command(["list"]) + assert command == ["notebooklm", "list"] + + +class TestErrorSanitization: + def test_sanitize_error_redacts_storage_state_path(self): + raw = "Failed to open /Users/tester/.notebooklm/storage_state.json because auth expired" + assert "storage_state.json" not in sanitize_error(raw) + assert "[redacted-auth-path]" in sanitize_error(raw) + + +class TestSession: + def test_session_persists_active_notebook(self, tmp_path): + session_file = tmp_path / "session.json" + session = Session(session_file=session_file) + session.set_active_notebook("nb_abc") + + reloaded = Session(session_file=session_file) + assert reloaded.get_active_notebook() == "nb_abc" + + def test_session_clear_active_notebook(self, tmp_path): + session_file = tmp_path / "session.json" + session = Session(session_file=session_file) + session.set_active_notebook("nb_abc") + session.clear_active_notebook() + + data = json.loads(Path(session_file).read_text()) + assert data["active_notebook"] is None + + +class TestPackagingFixtures: + def test_acknowledgements_reference_external_projects(self): + readme = Path("cli_anything/notebooklm/README.md").read_text(encoding="utf-8") + assert "CLI-Anything" in readme + assert "notebooklm-py" in readme + + def test_readme_documents_install_test_and_safety_sections(self): + readme = Path("cli_anything/notebooklm/README.md").read_text(encoding="utf-8") + assert "## Install" in readme + assert "## Run Tests" in readme + assert "## Safety Notes" in readme + assert "Google NotebookLM" in readme + + def test_skill_file_contains_usage_and_boundary_guidance(self): + skill = Path("cli_anything/notebooklm/skills/SKILL.md").read_text(encoding="utf-8") + assert "## Installation" in skill + assert "## Usage" in skill + assert "unofficial" in skill.lower() + assert "not affiliated with Google" in skill + + def test_skill_file_exists(self): + assert Path("cli_anything/notebooklm/skills/SKILL.md").is_file() diff --git a/notebooklm/agent-harness/cli_anything/notebooklm/tests/test_manual_e2e.md b/notebooklm/agent-harness/cli_anything/notebooklm/tests/test_manual_e2e.md new file mode 100644 index 000000000..c0b943dd9 --- /dev/null +++ b/notebooklm/agent-harness/cli_anything/notebooklm/tests/test_manual_e2e.md @@ -0,0 +1,11 @@ +# Manual E2E Checklist + +1. Run `notebooklm login`. +2. Run `cli-anything-notebooklm auth status`. +3. Run `cli-anything-notebooklm notebook list`. +4. Create a temporary notebook with `cli-anything-notebooklm notebook create "CLI Anything Smoke Test"`. +5. Add a simple text or URL source. +6. Run `cli-anything-notebooklm chat ask "Summarize the notebook."`. +7. Generate one artifact, such as a report. +8. Download the artifact to a temporary local path. +9. Delete the temporary notebook manually after verification. diff --git a/notebooklm/agent-harness/cli_anything/notebooklm/utils/__init__.py b/notebooklm/agent-harness/cli_anything/notebooklm/utils/__init__.py new file mode 100644 index 000000000..2f7adc5be --- /dev/null +++ b/notebooklm/agent-harness/cli_anything/notebooklm/utils/__init__.py @@ -0,0 +1 @@ +"""Utility modules for the NotebookLM harness.""" diff --git a/notebooklm/agent-harness/cli_anything/notebooklm/utils/notebooklm_backend.py b/notebooklm/agent-harness/cli_anything/notebooklm/utils/notebooklm_backend.py new file mode 100644 index 000000000..4ff424097 --- /dev/null +++ b/notebooklm/agent-harness/cli_anything/notebooklm/utils/notebooklm_backend.py @@ -0,0 +1,91 @@ +"""NotebookLM backend adapter. + +This module wraps an installed `notebooklm` CLI for use inside a CLI-Anything +harness. It does not implement a Google official API client. + +References: +- CLI-Anything methodology: https://github.com/HKUDS/CLI-Anything +- notebooklm-py project: https://github.com/teng-lin/notebooklm-py + +Security rules: +- never print credential files or cookies +- never commit auth state into the repository +- prefer explicit notebook IDs +""" + +from __future__ import annotations + +import json +import re +import shutil +import subprocess + + +def require_notebooklm() -> str: + """Resolve the notebooklm command from PATH.""" + path = shutil.which("notebooklm") + if path: + return path + raise RuntimeError( + "notebooklm command not found. Install it with:\n" + " python3 -m pip install --user 'notebooklm-py[browser]'\n" + " python3 -m playwright install chromium" + ) + + +def build_command( + args: list[str], + *, + notebook_id: str | None = None, + json_output: bool = False, +) -> list[str]: + """Build a notebooklm command with explicit notebook context.""" + command = ["notebooklm", *args] + if notebook_id: + command.extend(["-n", notebook_id]) + if json_output: + command.append("--json") + return command + + +def sanitize_error(text: str) -> str: + """Redact local auth file paths from stderr/stdout.""" + patterns = [ + r"/Users/[^/\s]+/\.notebooklm/storage_state\.json", + r"/home/[^/\s]+/\.notebooklm/storage_state\.json", + r"[A-Za-z]:\\\\Users\\\\[^\\\s]+\\\\\.notebooklm\\\\storage_state\.json", + ] + sanitized = text + for pattern in patterns: + sanitized = re.sub(pattern, "[redacted-auth-path]", sanitized) + sanitized = sanitized.replace("storage_state.json", "[redacted-auth-path]") + return sanitized + + +def run_notebooklm( + args: list[str], + *, + notebook_id: str | None = None, + json_output: bool = False, +) -> dict | str: + """Run notebooklm and optionally parse JSON output.""" + command = build_command( + args, + notebook_id=notebook_id, + json_output=json_output, + ) + command[0] = require_notebooklm() + + result = subprocess.run( + command, + capture_output=True, + text=True, + check=False, + ) + + if result.returncode != 0: + raise RuntimeError(sanitize_error(result.stderr or result.stdout)) + + if json_output: + return json.loads(result.stdout or "{}") + return result.stdout.strip() diff --git a/notebooklm/agent-harness/setup.py b/notebooklm/agent-harness/setup.py new file mode 100644 index 000000000..c6679bd04 --- /dev/null +++ b/notebooklm/agent-harness/setup.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +""" +setup.py for cli-anything-notebooklm + +Install with: pip install -e . +""" + +from setuptools import setup, find_namespace_packages + +with open("cli_anything/notebooklm/README.md", "r", encoding="utf-8") as fh: + long_description = fh.read() + +setup( + name="cli-anything-notebooklm", + version="0.1.0", + author="cli-anything contributors", + author_email="", + description="Experimental CLI harness for NotebookLM via an installed notebooklm CLI. Unofficial and community-maintained.", + long_description=long_description, + long_description_content_type="text/markdown", + url="https://github.com/HKUDS/CLI-Anything", + packages=find_namespace_packages(include=["cli_anything.*"]), + classifiers=[ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Topic :: Software Development :: Libraries :: Python Modules", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + ], + python_requires=">=3.10", + install_requires=[ + "click>=8.0.0", + "prompt-toolkit>=3.0.0", + ], + extras_require={ + "dev": [ + "pytest>=7.0.0", + "pytest-cov>=4.0.0", + ], + }, + entry_points={ + "console_scripts": [ + "cli-anything-notebooklm=cli_anything.notebooklm.notebooklm_cli:main", + ], + }, + package_data={ + "cli_anything.notebooklm": ["skills/*.md"], + }, + include_package_data=True, + zip_safe=False, +) From 0a4d8237c5616101d7e3f09bfc7adcfb79e1a345 Mon Sep 17 00:00:00 2001 From: liuche <1257067825@qq.com> Date: Tue, 17 Mar 2026 20:49:31 +0800 Subject: [PATCH 2/4] fix: address notebooklm review feedback --- .../cli_anything/notebooklm/README.md | 6 ++-- .../cli_anything/notebooklm/notebooklm_cli.py | 2 +- .../cli_anything/notebooklm/skills/SKILL.md | 6 ++-- .../cli_anything/notebooklm/tests/TEST.md | 25 +++++++++++++ .../notebooklm/tests/test_cli_smoke.py | 19 ++++++++++ .../notebooklm/tests/test_core.py | 16 +++++++++ .../notebooklm/utils/notebooklm_backend.py | 35 ++++++++++++++++++- 7 files changed, 101 insertions(+), 8 deletions(-) diff --git a/notebooklm/agent-harness/cli_anything/notebooklm/README.md b/notebooklm/agent-harness/cli_anything/notebooklm/README.md index 2751f83d8..88fff930f 100644 --- a/notebooklm/agent-harness/cli_anything/notebooklm/README.md +++ b/notebooklm/agent-harness/cli_anything/notebooklm/README.md @@ -38,7 +38,7 @@ python3 -m playwright install chromium # Show help cli-anything-notebooklm --help -# Check auth state +# Check auth state (wraps `notebooklm auth check`) cli-anything-notebooklm auth status # List notebooks @@ -57,7 +57,7 @@ python3 -m cli_anything.notebooklm.notebooklm_cli --help | Group | Purpose | | --- | --- | -| `auth` | login, auth status, and lightweight auth checks | +| `auth` | login helpers and authentication checks | | `notebook` | list, create, and summarize notebooks | | `source` | inspect sources and add URL sources | | `chat` | ask questions and inspect history | @@ -78,7 +78,7 @@ cli-anything-notebooklm artifact generate-report --notebook nb_123 ## For AI Agents - Prefer explicit notebook IDs with `--notebook` instead of relying on ambient state. -- Use `--json` whenever the command supports machine-readable output. +- Use `--json` only on commands whose upstream `notebooklm` subcommand supports machine-readable output. - Treat NotebookLM auth state as sensitive local data and never print cookie or storage files. - Treat this harness as a thin wrapper around `notebooklm`, not a reimplementation of NotebookLM. diff --git a/notebooklm/agent-harness/cli_anything/notebooklm/notebooklm_cli.py b/notebooklm/agent-harness/cli_anything/notebooklm/notebooklm_cli.py index 53640752b..5da8612e6 100644 --- a/notebooklm/agent-harness/cli_anything/notebooklm/notebooklm_cli.py +++ b/notebooklm/agent-harness/cli_anything/notebooklm/notebooklm_cli.py @@ -84,7 +84,7 @@ def auth(): @handle_error def auth_status(): """Check authentication status.""" - emit(run_notebooklm(["status"], json_output=_json_output)) + emit(run_notebooklm(["auth", "check"], json_output=_json_output)) @auth.command("login") diff --git a/notebooklm/agent-harness/cli_anything/notebooklm/skills/SKILL.md b/notebooklm/agent-harness/cli_anything/notebooklm/skills/SKILL.md index 9a8c6b514..12940c3a7 100644 --- a/notebooklm/agent-harness/cli_anything/notebooklm/skills/SKILL.md +++ b/notebooklm/agent-harness/cli_anything/notebooklm/skills/SKILL.md @@ -47,7 +47,7 @@ cli-anything-notebooklm --json notebook list | Group | Purpose | | --- | --- | -| `auth` | login and auth status | +| `auth` | login and auth validation | | `notebook` | notebook list, create, summary | | `source` | source listing and URL add | | `chat` | ask questions and inspect history | @@ -60,12 +60,12 @@ cli-anything-notebooklm --json notebook list 1. Check auth with `cli-anything-notebooklm auth status` 2. Discover notebook IDs with `cli-anything-notebooklm --json notebook list` 3. Use explicit `--notebook` for follow-up commands -4. Prefer `--json` for list and inspection commands +4. Prefer `--json` only where the upstream `notebooklm` command supports it ## Agent Guidance - Prefer explicit notebook IDs with `--notebook`. -- Use `--json` for machine-readable output. +- Use `--json` for machine-readable output only on commands that support it upstream. - Treat this harness as experimental and unofficial. - Do not expose auth files or cookies in logs. - NotebookLM is a Google product; this harness is unofficial and not affiliated with Google. diff --git a/notebooklm/agent-harness/cli_anything/notebooklm/tests/TEST.md b/notebooklm/agent-harness/cli_anything/notebooklm/tests/TEST.md index 26a3afe23..24b37f86a 100644 --- a/notebooklm/agent-harness/cli_anything/notebooklm/tests/TEST.md +++ b/notebooklm/agent-harness/cli_anything/notebooklm/tests/TEST.md @@ -62,3 +62,28 @@ python3 -m cli_anything.notebooklm.notebooklm_cli --help - Added doc-level assertions so the package README and skill file now explicitly cover installation, tests, safety boundaries, and unofficial Google attribution. - Added `_resolve_cli`-style subprocess smoke coverage so the CLI can be exercised through a resolved command path, not only through Click's in-process test runner. + +## Review Fix Verification + +Verified on 2026-03-17 after addressing PR review feedback about JSON passthrough and auth command semantics. + +### Commands Run + +```bash +python3 -m pytest cli_anything/notebooklm/tests/test_core.py -q +python3 -m pytest cli_anything/notebooklm/tests/test_cli_smoke.py -q +python3 -m pytest cli_anything/notebooklm/tests -q +python3 -m cli_anything.notebooklm.notebooklm_cli --json auth login +``` + +### Results + +- targeted backend tests: pass +- targeted CLI routing tests: pass +- full NotebookLM harness suite: pass +- `--json auth login`: fails fast with a structured JSON error instead of passing an invalid `--json` flag through to upstream login + +### Notes + +- `auth status` now wraps upstream `notebooklm auth check`, which matches authentication semantics. +- JSON passthrough is now limited to wrapper commands whose upstream `notebooklm` command has verified `--json` support. diff --git a/notebooklm/agent-harness/cli_anything/notebooklm/tests/test_cli_smoke.py b/notebooklm/agent-harness/cli_anything/notebooklm/tests/test_cli_smoke.py index e9c440f64..d803d5758 100644 --- a/notebooklm/agent-harness/cli_anything/notebooklm/tests/test_cli_smoke.py +++ b/notebooklm/agent-harness/cli_anything/notebooklm/tests/test_cli_smoke.py @@ -56,6 +56,25 @@ class TestSubcommandHelp: assert "List notebooks" in result.output +class TestCommandRouting: + def test_auth_status_routes_to_auth_check(self, monkeypatch): + calls = [] + + def fake_run(args, **kwargs): + calls.append((args, kwargs)) + return {"ok": True} + + monkeypatch.setattr( + "cli_anything.notebooklm.notebooklm_cli.run_notebooklm", + fake_run, + ) + + result = CliRunner().invoke(cli, ["auth", "status"]) + + assert result.exit_code == 0 + assert calls == [(["auth", "check"], {"json_output": False})] + + class TestModuleExecution: def test_python_m_module_help_emits_output(self): result = subprocess.run( diff --git a/notebooklm/agent-harness/cli_anything/notebooklm/tests/test_core.py b/notebooklm/agent-harness/cli_anything/notebooklm/tests/test_core.py index 7ef1a4889..bc9d2f770 100644 --- a/notebooklm/agent-harness/cli_anything/notebooklm/tests/test_core.py +++ b/notebooklm/agent-harness/cli_anything/notebooklm/tests/test_core.py @@ -9,7 +9,9 @@ import pytest from cli_anything.notebooklm.core.session import Session from cli_anything.notebooklm.utils.notebooklm_backend import ( build_command, + command_supports_json, require_notebooklm, + run_notebooklm, sanitize_error, ) @@ -45,6 +47,20 @@ class TestCommandBuilder: command = build_command(["list"]) assert command == ["notebooklm", "list"] + def test_command_supports_json_for_verified_command(self): + assert command_supports_json(["download", "report"]) is True + + def test_command_supports_json_rejects_unsupported_command(self): + assert command_supports_json(["login"]) is False + + +class TestRunNotebooklm: + def test_run_notebooklm_rejects_json_for_unsupported_command(self): + with patch("cli_anything.notebooklm.utils.notebooklm_backend.subprocess.run") as run_mock: + with pytest.raises(RuntimeError, match="JSON output is not supported for command: login"): + run_notebooklm(["login"], json_output=True) + run_mock.assert_not_called() + class TestErrorSanitization: def test_sanitize_error_redacts_storage_state_path(self): diff --git a/notebooklm/agent-harness/cli_anything/notebooklm/utils/notebooklm_backend.py b/notebooklm/agent-harness/cli_anything/notebooklm/utils/notebooklm_backend.py index 4ff424097..6603d980f 100644 --- a/notebooklm/agent-harness/cli_anything/notebooklm/utils/notebooklm_backend.py +++ b/notebooklm/agent-harness/cli_anything/notebooklm/utils/notebooklm_backend.py @@ -21,6 +21,23 @@ import shutil import subprocess +JSON_SUPPORTED_COMMANDS = { + ("auth", "check"), + ("status",), + ("list",), + ("create",), + ("source", "list"), + ("source", "add"), + ("ask",), + ("history",), + ("artifact", "list"), + ("generate", "report"), + ("download", "report"), + ("share", "status"), +} +TWO_PART_COMMAND_GROUPS = {"auth", "source", "artifact", "generate", "download", "share"} + + def require_notebooklm() -> str: """Resolve the notebooklm command from PATH.""" path = shutil.which("notebooklm") @@ -33,6 +50,17 @@ def require_notebooklm() -> str: ) +def command_supports_json(args: list[str]) -> bool: + """Return whether the wrapped notebooklm command supports --json.""" + if not args: + return False + if len(args) > 1 and args[0] in TWO_PART_COMMAND_GROUPS: + key = tuple(args[:2]) + else: + key = tuple(args[:1]) + return key in JSON_SUPPORTED_COMMANDS + + def build_command( args: list[str], *, @@ -43,7 +71,7 @@ def build_command( command = ["notebooklm", *args] if notebook_id: command.extend(["-n", notebook_id]) - if json_output: + if json_output and command_supports_json(args): command.append("--json") return command @@ -69,6 +97,11 @@ def run_notebooklm( json_output: bool = False, ) -> dict | str: """Run notebooklm and optionally parse JSON output.""" + if json_output and not command_supports_json(args): + raise RuntimeError( + f"JSON output is not supported for command: {' '.join(args)}" + ) + command = build_command( args, notebook_id=notebook_id, From 8e93f3bf426517242d1e708c283815a322d518fd Mon Sep 17 00:00:00 2001 From: liuche <1257067825@qq.com> Date: Wed, 18 Mar 2026 08:50:47 +0800 Subject: [PATCH 3/4] docs: surface notebooklm harness and trim PR scope --- .gitignore | 7 +- README.md | 31 +++ .../2026-03-17-notebooklm-harness-scaffold.md | 192 ------------------ docs/plans/2026-03-17-notebooklm-pr-polish.md | 116 ----------- registry.json | 177 ++++++++++++++++ 5 files changed, 214 insertions(+), 309 deletions(-) delete mode 100644 docs/plans/2026-03-17-notebooklm-harness-scaffold.md delete mode 100644 docs/plans/2026-03-17-notebooklm-pr-polish.md create mode 100644 registry.json diff --git a/.gitignore b/.gitignore index d2c12313f..1869e1618 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,6 @@ !/CONTRIBUTING.md !/assets/ !/.claude-plugin/ -!/docs/ !/.github/ # Step 3: Allow cli-anything-plugin, codex-skill, and skill_generation tests @@ -102,6 +101,12 @@ assets/gen_typing_gif.py !README_CN.md !opencode-commands/ !/qoder-plugin/ + +# Step 10: Allow CLI Hub registry and frontend +!/registry.json +!/docs/ +/docs/* +!/docs/hub/ !/notebooklm/ /notebooklm/* /notebooklm/.* diff --git a/README.md b/README.md index d5add4e3e..9ca71c176 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ CLI-Anything: Bridging the Gap Between AI Agents and the World's Software Quick Start + CLI Hub Demos Tests License @@ -24,6 +25,8 @@ CLI-Anything: Bridging the Gap Between AI Agents and the World's Software CLI-Anything typing demo

@@ -38,6 +41,10 @@ CLI-Anything: Bridging the Gap Between AI Agents and the World's Software Thanks to all invaluable efforts from the community! More updates continuously on the way everyday.. +- **2026-03-18** 🧠 Added an **experimental NotebookLM harness scaffold** from the community. It wraps the installed `notebooklm` CLI for notebook discovery, source management, chat, artifact workflows, downloads, and sharing while preserving a clear experimental/community-maintained boundary. + +- **2026-03-17** 🌐 Launched the **[CLI-Hub](https://hkuds.github.io/CLI-Anything/hub/)** — a central registry where you can browse, search, and install any CLI with a single `pip` command. Contributors can add new CLIs or update existing ones by simply opening a PR with a `registry.json` entry. The hub updates automatically on merge. + - **2026-03-16** 🤖 Added **SKILL.md generation** (Phase 6.5) — every generated CLI now ships with an AI-discoverable skill definition inside the Python package. ReplSkin auto-detects the skill file after `pip install`, and the REPL banner displays the absolute path for agents. Includes `skill_generator.py`, Jinja2 template, `package_data` in all setup.py files, and 51 new tests. - **2026-03-15** 🐾 Support for **OpenClaw** from the community! Merged Windows `cygpath` guard to ensure CLI-Anything works reliably in Windows bash environments. Community contributions continue to strengthen cross-platform support. @@ -611,6 +618,29 @@ Each application received complete, production-ready CLI interfaces — not demo > **100% pass rate** across all 1,588 tests — 1,138 unit tests + 450 end-to-end tests. +### 🧪 Experimental / Community Harnesses + +These contributions extend CLI-Anything into adjacent agent-native surfaces while keeping their support boundary explicit. + + + + + + + + + + + + + + + + +
SoftwareDomainCLI CommandBackendTests
🧠 NotebookLMAI Research & Knowledge Workspacecli-anything-notebooklmInstalled notebooklm CLI (notebooklm-py)✅ 21
+ +> Experimental/community-maintained scaffold: wraps the installed NotebookLM CLI for auth checks, notebook selection, source ingestion, Q&A, artifact generation, downloads, and sharing. Unofficial and not affiliated with Google. + --- ## 📊 Test Results @@ -701,6 +731,7 @@ cli-anything/ ├── 📐 drawio/agent-harness/ # Draw.io CLI (138 tests) ├── 🧜 mermaid/agent-harness/ # Mermaid Live Editor CLI (10 tests) ├── ✨ anygen/agent-harness/ # AnyGen CLI (50 tests) +├── 🧠 notebooklm/agent-harness/ # NotebookLM CLI (experimental, 21 tests) └── 🖼️ comfyui/agent-harness/ # ComfyUI CLI (70 tests) ``` diff --git a/docs/plans/2026-03-17-notebooklm-harness-scaffold.md b/docs/plans/2026-03-17-notebooklm-harness-scaffold.md deleted file mode 100644 index 5d38a7faa..000000000 --- a/docs/plans/2026-03-17-notebooklm-harness-scaffold.md +++ /dev/null @@ -1,192 +0,0 @@ -# NotebookLM Harness Scaffold Implementation Plan - -> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. - -**Goal:** Add an experimental `cli-anything-notebooklm` scaffold that wraps an installed `notebooklm` CLI, includes attribution files, and establishes the first round of tests and docs. - -**Architecture:** Create a service-style harness under `notebooklm/agent-harness/` that mirrors the existing CLI-Anything package layout. The backend adapter will shell out to the installed `notebooklm` command, sanitize sensitive errors, and keep notebook context in a lightweight session file for REPL use. - -**Tech Stack:** Python 3.10+, Click, prompt-toolkit, pytest, subprocess-based backend wrapper - -### Task 1: Establish the tracked scaffold area - -**Files:** -- Modify: `.gitignore` -- Create: `docs/plans/2026-03-17-notebooklm-harness-scaffold.md` - -**Step 1: Write the failing test** - -No behavior test for this task. It is repository plumbing needed to track the new harness path and plan document. - -**Step 2: Run test to verify it fails** - -Not applicable. - -**Step 3: Write minimal implementation** - -- Allow `docs/` -- Allow `notebooklm/` -- Allow `notebooklm/agent-harness/` - -**Step 4: Run test to verify it passes** - -Run: `git status --short` -Expected: new scaffold files are visible to git - -**Step 5: Commit** - -```bash -git add .gitignore docs/plans/2026-03-17-notebooklm-harness-scaffold.md -git commit -m "chore: prepare notebooklm harness scaffold" -``` - -### Task 2: Write the first failing backend tests - -**Files:** -- Create: `notebooklm/agent-harness/cli_anything/notebooklm/tests/test_core.py` -- Test: `notebooklm/agent-harness/cli_anything/notebooklm/tests/test_core.py` - -**Step 1: Write the failing test** - -Cover: -- backend finds `notebooklm` on PATH -- backend raises clear install guidance if missing -- backend builds explicit commands with `-n ` -- backend sanitizes secret-bearing auth path output -- session file stores current notebook context - -**Step 2: Run test to verify it fails** - -Run: `cd notebooklm/agent-harness && python3 -m pytest cli_anything/notebooklm/tests/test_core.py -q` -Expected: FAIL with import errors because the package does not exist yet - -**Step 3: Write minimal implementation** - -Create: -- `cli_anything/notebooklm/utils/notebooklm_backend.py` -- `cli_anything/notebooklm/core/session.py` - -**Step 4: Run test to verify it passes** - -Run: `cd notebooklm/agent-harness && python3 -m pytest cli_anything/notebooklm/tests/test_core.py -q` -Expected: PASS - -**Step 5: Commit** - -```bash -git add notebooklm/agent-harness/cli_anything/notebooklm -git commit -m "feat: add notebooklm backend scaffold" -``` - -### Task 3: Write the first failing CLI tests - -**Files:** -- Create: `notebooklm/agent-harness/cli_anything/notebooklm/tests/test_cli_smoke.py` -- Test: `notebooklm/agent-harness/cli_anything/notebooklm/tests/test_cli_smoke.py` - -**Step 1: Write the failing test** - -Cover: -- root `--help` shows experimental NotebookLM wording -- root `--help` exposes command groups -- `auth status --help` and `notebook list --help` execute - -**Step 2: Run test to verify it fails** - -Run: `cd notebooklm/agent-harness && python3 -m pytest cli_anything/notebooklm/tests/test_cli_smoke.py -q` -Expected: FAIL because `notebooklm_cli.py` is missing - -**Step 3: Write minimal implementation** - -Create: -- `cli_anything/notebooklm/notebooklm_cli.py` -- `cli_anything/notebooklm/__init__.py` -- `cli_anything/notebooklm/__main__.py` - -Use placeholder command handlers that route through the backend wrapper. - -**Step 4: Run test to verify it passes** - -Run: `cd notebooklm/agent-harness && python3 -m pytest cli_anything/notebooklm/tests/test_cli_smoke.py -q` -Expected: PASS - -**Step 5: Commit** - -```bash -git add notebooklm/agent-harness/cli_anything/notebooklm/notebooklm_cli.py -git commit -m "feat: add notebooklm cli skeleton" -``` - -### Task 4: Add packaging and attribution docs - -**Files:** -- Create: `notebooklm/agent-harness/setup.py` -- Create: `notebooklm/agent-harness/NOTEBOOKLM.md` -- Create: `notebooklm/agent-harness/ATTRIBUTION.md` -- Create: `notebooklm/agent-harness/THIRD_PARTY_NOTICES.md` -- Create: `notebooklm/agent-harness/cli_anything/notebooklm/README.md` -- Create: `notebooklm/agent-harness/cli_anything/notebooklm/skills/SKILL.md` -- Create: `notebooklm/agent-harness/cli_anything/notebooklm/tests/TEST.md` -- Create: `notebooklm/agent-harness/cli_anything/notebooklm/tests/test_manual_e2e.md` - -**Step 1: Write the failing test** - -Add assertions that: -- `setup.py` exposes `cli-anything-notebooklm` -- skill file exists -- README includes acknowledgement text - -**Step 2: Run test to verify it fails** - -Run: `cd notebooklm/agent-harness && python3 -m pytest cli_anything/notebooklm/tests/test_core.py -q` -Expected: FAIL on missing files and packaging metadata - -**Step 3: Write minimal implementation** - -Create all packaging and documentation files with explicit attribution and experimental disclaimers. - -**Step 4: Run test to verify it passes** - -Run: `cd notebooklm/agent-harness && python3 -m pytest cli_anything/notebooklm/tests/test_core.py cli_anything/notebooklm/tests/test_cli_smoke.py -q` -Expected: PASS - -**Step 5: Commit** - -```bash -git add notebooklm/agent-harness -git commit -m "docs: add notebooklm harness attribution and packaging" -``` - -### Task 5: Verify the scaffold end to end - -**Files:** -- Modify: `notebooklm/agent-harness/cli_anything/notebooklm/tests/TEST.md` - -**Step 1: Write the failing test** - -No new behavior. This task is verification and documentation. - -**Step 2: Run test to verify it fails** - -Not applicable. - -**Step 3: Write minimal implementation** - -Run: -- `cd notebooklm/agent-harness && python3 -m pytest cli_anything/notebooklm/tests/test_core.py cli_anything/notebooklm/tests/test_cli_smoke.py -q` -- `cd notebooklm/agent-harness && python3 -m cli_anything.notebooklm.notebooklm_cli --help` - -Append the verification notes to `TEST.md`. - -**Step 4: Run test to verify it passes** - -Expected: -- targeted tests PASS -- CLI help exits 0 - -**Step 5: Commit** - -```bash -git add notebooklm/agent-harness/cli_anything/notebooklm/tests/TEST.md -git commit -m "test: verify notebooklm harness scaffold" -``` diff --git a/docs/plans/2026-03-17-notebooklm-pr-polish.md b/docs/plans/2026-03-17-notebooklm-pr-polish.md deleted file mode 100644 index 3b2f0d536..000000000 --- a/docs/plans/2026-03-17-notebooklm-pr-polish.md +++ /dev/null @@ -1,116 +0,0 @@ -# NotebookLM PR Polish Implementation Plan - -> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. - -**Goal:** Tighten the experimental NotebookLM harness into a more upstream-ready contribution with stronger docs, explicit attribution, and subprocess-level CLI smoke coverage. - -**Architecture:** Keep the current service-style wrapper architecture intact. Improve PR readiness by expanding package-facing documentation and adding lightweight subprocess tests that verify the packaged CLI interface without requiring a live authenticated NotebookLM session. - -**Tech Stack:** Python 3, Click, pytest, subprocess, Markdown docs - -### Task 1: Add failing tests for PR-facing expectations - -**Files:** -- Modify: `notebooklm/agent-harness/cli_anything/notebooklm/tests/test_core.py` -- Modify: `notebooklm/agent-harness/cli_anything/notebooklm/tests/test_cli_smoke.py` - -**Step 1: Write the failing test** - -Add tests that require: -- README to document installation, test execution, safety boundaries, and references -- SKILL.md to include installation, usage, and explicit unofficial guidance -- subprocess CLI help to render through a resolved `cli-anything-notebooklm` entry path - -**Step 2: Run test to verify it fails** - -Run: `cd notebooklm/agent-harness && python3 -m pytest cli_anything/notebooklm/tests/test_core.py cli_anything/notebooklm/tests/test_cli_smoke.py -q` - -Expected: FAIL on missing README/SKILL sections or missing subprocess helper behavior - -**Step 3: Write minimal implementation** - -Expand documentation and add the smallest test helper needed for subprocess smoke coverage. - -**Step 4: Run test to verify it passes** - -Run: `cd notebooklm/agent-harness && python3 -m pytest cli_anything/notebooklm/tests/test_core.py cli_anything/notebooklm/tests/test_cli_smoke.py -q` - -Expected: PASS - -**Step 5: Commit** - -```bash -git add notebooklm/agent-harness/cli_anything/notebooklm/tests notebooklm/agent-harness/cli_anything/notebooklm/README.md notebooklm/agent-harness/cli_anything/notebooklm/skills/SKILL.md -git commit -m "test: harden notebooklm harness smoke coverage" -``` - -### Task 2: Improve docs for upstream review - -**Files:** -- Modify: `notebooklm/agent-harness/cli_anything/notebooklm/README.md` -- Modify: `notebooklm/agent-harness/cli_anything/notebooklm/skills/SKILL.md` -- Modify: `notebooklm/agent-harness/NOTEBOOKLM.md` - -**Step 1: Write the failing test** - -Covered by Task 1 doc assertions. - -**Step 2: Run test to verify it fails** - -Covered by Task 1. - -**Step 3: Write minimal implementation** - -Document: -- installation and local prerequisites -- run and test commands -- command group overview -- agent guidance and safe-by-default behavior -- explicit references and unofficial attribution boundary - -**Step 4: Run test to verify it passes** - -Run: `cd notebooklm/agent-harness && python3 -m pytest cli_anything/notebooklm/tests/test_core.py cli_anything/notebooklm/tests/test_cli_smoke.py -q` - -Expected: PASS - -**Step 5: Commit** - -```bash -git add notebooklm/agent-harness/NOTEBOOKLM.md notebooklm/agent-harness/cli_anything/notebooklm/README.md notebooklm/agent-harness/cli_anything/notebooklm/skills/SKILL.md -git commit -m "docs: polish notebooklm harness contribution docs" -``` - -### Task 3: Verify and record final state - -**Files:** -- Modify: `notebooklm/agent-harness/cli_anything/notebooklm/tests/TEST.md` - -**Step 1: Write the failing test** - -No new behavior test. This task records verification evidence. - -**Step 2: Run test to verify it fails** - -Not applicable. - -**Step 3: Write minimal implementation** - -Run: -- `cd notebooklm/agent-harness && python3 -m pytest cli_anything/notebooklm/tests -q` -- `cd notebooklm/agent-harness && python3 -m cli_anything.notebooklm.notebooklm_cli --help` - -Append fresh results to `TEST.md`. - -**Step 4: Run test to verify it passes** - -Expected: -- all NotebookLM scaffold tests PASS -- module help exits 0 and prints command groups - -**Step 5: Commit** - -```bash -git add notebooklm/agent-harness/cli_anything/notebooklm/tests/TEST.md -git commit -m "test: record notebooklm harness verification" -``` diff --git a/registry.json b/registry.json new file mode 100644 index 000000000..4758c86db --- /dev/null +++ b/registry.json @@ -0,0 +1,177 @@ +{ + "meta": { + "repo": "https://github.com/HKUDS/CLI-Anything", + "description": "CLI-Hub — Agent-native stateful CLI interfaces for softwares, codebases, and Web Services", + "updated": "2026-03-18" + }, + "clis": [ + { + "name": "anygen", + "display_name": "AnyGen", + "version": "1.0.0", + "description": "Generate docs, slides, websites and more via AnyGen cloud API", + "requires": "ANYGEN_API_KEY", + "homepage": "https://anygen.com", + "install_cmd": "pip install git+https://github.com/HKUDS/CLI-Anything.git#subdirectory=anygen/agent-harness", + "entry_point": "cli-anything-anygen", + "skill_md": "anygen/agent-harness/cli_anything/anygen/skills/SKILL.md", + "category": "generation" + }, + { + "name": "audacity", + "display_name": "Audacity", + "version": "1.0.0", + "description": "Audio editing and processing via sox", + "requires": "sox (apt install sox)", + "homepage": "https://www.audacityteam.org", + "install_cmd": "pip install git+https://github.com/HKUDS/CLI-Anything.git#subdirectory=audacity/agent-harness", + "entry_point": "cli-anything-audacity", + "skill_md": "audacity/agent-harness/cli_anything/audacity/skills/SKILL.md", + "category": "audio" + }, + { + "name": "blender", + "display_name": "Blender", + "version": "1.0.0", + "description": "3D modeling, animation, and rendering via blender --background --python", + "requires": "blender", + "homepage": "https://www.blender.org", + "install_cmd": "pip install git+https://github.com/HKUDS/CLI-Anything.git#subdirectory=blender/agent-harness", + "entry_point": "cli-anything-blender", + "skill_md": "blender/agent-harness/cli_anything/blender/skills/SKILL.md", + "category": "3d" + }, + { + "name": "comfyui", + "display_name": "ComfyUI", + "version": "1.0.0", + "description": "AI image generation workflow management via ComfyUI REST API", + "requires": "ComfyUI running at http://localhost:8188", + "homepage": "https://github.com/comfyanonymous/ComfyUI", + "install_cmd": "pip install git+https://github.com/HKUDS/CLI-Anything.git#subdirectory=comfyui/agent-harness", + "entry_point": "cli-anything-comfyui", + "skill_md": null, + "category": "ai" + }, + { + "name": "drawio", + "display_name": "Draw.io", + "version": "1.0.0", + "description": "Diagram creation and export via draw.io CLI", + "requires": "draw.io desktop app", + "homepage": "https://www.drawio.com", + "install_cmd": "pip install git+https://github.com/HKUDS/CLI-Anything.git#subdirectory=drawio/agent-harness", + "entry_point": "cli-anything-drawio", + "skill_md": "drawio/agent-harness/cli_anything/drawio/skills/SKILL.md", + "category": "diagrams" + }, + { + "name": "gimp", + "display_name": "GIMP", + "version": "1.0.0", + "description": "Raster image processing via gimp -i -b (batch mode)", + "requires": "gimp (apt install gimp)", + "homepage": "https://www.gimp.org", + "install_cmd": "pip install git+https://github.com/HKUDS/CLI-Anything.git#subdirectory=gimp/agent-harness", + "entry_point": "cli-anything-gimp", + "skill_md": "gimp/agent-harness/cli_anything/gimp/skills/SKILL.md", + "category": "image" + }, + { + "name": "inkscape", + "display_name": "Inkscape", + "version": "1.0.0", + "description": "SVG vector graphics with export via inkscape --export-filename", + "requires": "inkscape (apt install inkscape)", + "homepage": "https://inkscape.org", + "install_cmd": "pip install git+https://github.com/HKUDS/CLI-Anything.git#subdirectory=inkscape/agent-harness", + "entry_point": "cli-anything-inkscape", + "skill_md": "inkscape/agent-harness/cli_anything/inkscape/skills/SKILL.md", + "category": "image" + }, + { + "name": "kdenlive", + "display_name": "Kdenlive", + "version": "1.0.0", + "description": "Video editing and rendering via melt", + "requires": "melt (apt install melt)", + "homepage": "https://kdenlive.org", + "install_cmd": "pip install git+https://github.com/HKUDS/CLI-Anything.git#subdirectory=kdenlive/agent-harness", + "entry_point": "cli-anything-kdenlive", + "skill_md": "kdenlive/agent-harness/cli_anything/kdenlive/skills/SKILL.md", + "category": "video" + }, + { + "name": "libreoffice", + "display_name": "LibreOffice", + "version": "1.0.0", + "description": "Create and manipulate ODF documents, export to PDF/DOCX/XLSX/PPTX via headless mode", + "requires": "libreoffice", + "homepage": "https://www.libreoffice.org", + "install_cmd": "pip install git+https://github.com/HKUDS/CLI-Anything.git#subdirectory=libreoffice/agent-harness", + "entry_point": "cli-anything-libreoffice", + "skill_md": "libreoffice/agent-harness/cli_anything/libreoffice/skills/SKILL.md", + "category": "office" + }, + { + "name": "mermaid", + "display_name": "Mermaid", + "version": "1.0.0", + "description": "Mermaid Live Editor state files and renderer URLs", + "requires": null, + "homepage": "https://mermaid.js.org", + "install_cmd": "pip install git+https://github.com/HKUDS/CLI-Anything.git#subdirectory=mermaid/agent-harness", + "entry_point": "cli-anything-mermaid", + "skill_md": null, + "category": "diagrams" + }, + { + "name": "notebooklm", + "display_name": "NotebookLM", + "version": "0.1.0", + "description": "Experimental NotebookLM harness scaffold wrapping the installed notebooklm CLI for notebook, source, chat, artifact, download, and sharing workflows", + "requires": "notebooklm CLI from notebooklm-py + valid local NotebookLM login session", + "homepage": "https://notebooklm.google.com", + "install_cmd": "pip install git+https://github.com/HKUDS/CLI-Anything.git#subdirectory=notebooklm/agent-harness", + "entry_point": "cli-anything-notebooklm", + "skill_md": "notebooklm/agent-harness/cli_anything/notebooklm/skills/SKILL.md", + "category": "ai" + }, + { + "name": "obs-studio", + "display_name": "OBS Studio", + "version": "1.0.0", + "description": "Create and manage streaming/recording scenes via command line", + "requires": "obs-studio", + "homepage": "https://obsproject.com", + "install_cmd": "pip install git+https://github.com/HKUDS/CLI-Anything.git#subdirectory=obs-studio/agent-harness", + "entry_point": "cli-anything-obs-studio", + "skill_md": "obs-studio/agent-harness/cli_anything/obs_studio/skills/SKILL.md", + "category": "streaming" + }, + { + "name": "shotcut", + "display_name": "Shotcut", + "version": "1.0.0", + "description": "Video editing and rendering via melt/ffmpeg", + "requires": "melt (apt install melt), ffmpeg (apt install ffmpeg)", + "homepage": "https://shotcut.org", + "install_cmd": "pip install git+https://github.com/HKUDS/CLI-Anything.git#subdirectory=shotcut/agent-harness", + "entry_point": "cli-anything-shotcut", + "skill_md": "shotcut/agent-harness/cli_anything/shotcut/skills/SKILL.md", + "category": "video" + }, + { + "name": "zoom", + "display_name": "Zoom", + "version": "1.0.0", + "description": "Meeting management via Zoom REST API (OAuth2)", + "requires": "Zoom account + OAuth app credentials", + "homepage": "https://zoom.us", + "install_cmd": "pip install git+https://github.com/HKUDS/CLI-Anything.git#subdirectory=zoom/agent-harness", + "entry_point": "cli-anything-zoom", + "skill_md": "zoom/agent-harness/cli_anything/zoom/skills/SKILL.md", + "category": "communication" + } + ] +} From 54f78f65d2ef8a2e67e96d46fd46a350b3994daa Mon Sep 17 00:00:00 2001 From: liuche <1257067825@qq.com> Date: Wed, 18 Mar 2026 15:30:14 +0800 Subject: [PATCH 4/4] docs: trim notebooklm readme mentions --- README.md | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/README.md b/README.md index 333a71783..c1c353bbd 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,6 @@ CLI-Anything: Bridging the Gap Between AI Agents and the World's Software Thanks to all invaluable efforts from the community! More updates continuously on the way everyday.. -- **2026-03-18** 🧠 Added an **experimental NotebookLM harness scaffold** from the community. It wraps the installed `notebooklm` CLI for notebook discovery, source management, chat, artifact workflows, downloads, and sharing while preserving a clear experimental/community-maintained boundary. - **2026-03-17** 🌐 Launched the **[CLI-Hub](https://hkuds.github.io/CLI-Anything/hub/)** — a central registry where you can browse, search, and install any CLI with a single `pip` command. Contributors can add new CLIs or update existing ones by simply opening a PR with a `registry.json` entry. The hub updates automatically on merge. - **2026-03-16** 🤖 Added **SKILL.md generation** (Phase 6.5) — every generated CLI now ships with an AI-discoverable skill definition inside the Python package. ReplSkin auto-detects the skill file after `pip install`, and the REPL banner displays the absolute path for agents. Includes `skill_generator.py`, Jinja2 template, `package_data` in all setup.py files, and 51 new tests. @@ -617,29 +616,6 @@ Each application received complete, production-ready CLI interfaces — not demo > **100% pass rate** across all 1,588 tests — 1,138 unit tests + 450 end-to-end tests. -### 🧪 Experimental / Community Harnesses - -These contributions extend CLI-Anything into adjacent agent-native surfaces while keeping their support boundary explicit. - - - - - - - - - - - - - - - - -
SoftwareDomainCLI CommandBackendTests
🧠 NotebookLMAI Research & Knowledge Workspacecli-anything-notebooklmInstalled notebooklm CLI (notebooklm-py)✅ 21
- -> Experimental/community-maintained scaffold: wraps the installed NotebookLM CLI for auth checks, notebook selection, source ingestion, Q&A, artifact generation, downloads, and sharing. Unofficial and not affiliated with Google. - --- ## 📊 Test Results