feat(note): add --json flag to note list command#259
feat(note): add --json flag to note list command#259Flosters wants to merge 2 commits intoteng-lin:mainfrom
Conversation
Adds machine-readable JSON output to `notebooklm note list`, consistent
with every other list command in the CLI (source list, artifact list,
notebook list, etc.).
Output format:
[{"id": "...", "title": "...", "preview": "first 100 chars..."}]
Also sets no_wrap=True on the ID column in the table view so UUIDs
do not wrap in narrow terminals.
Closes the only list command missing --json support.
📝 WalkthroughWalkthroughAdded a Changes
Sequence Diagram(s)sequenceDiagram
participant CLI as Client CLI
participant Cmd as note_list command
participant Auth as client_auth
participant Out as Renderer / json_output_response
CLI->>Cmd: invoke `note list` (--json? true/false)
Cmd->>Auth: fetch notes for notebook_id
Auth-->>Cmd: return notes
alt json_output == true
Cmd->>Out: json_output_response({notebook_id, notes[], count})
Out-->>CLI: JSON payload (exit)
else json_output == false
Cmd->>Out: render Rich table (ID no_wrap=True)
Out-->>CLI: table output
end
Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces a --json flag to the note_list command, enabling JSON-formatted output for notes, and updates the table display to prevent ID wrapping. The review feedback recommends simplifying the JSON output logic to eliminate redundant checks and local imports, while also suggesting a more consistent relative import path.
src/notebooklm/cli/note.py
Outdated
| if not notes: | ||
| console.print("[yellow]No notes found[/yellow]") | ||
| if json_output: | ||
| from ..cli.helpers import json_output_response | ||
| json_output_response([]) | ||
| else: | ||
| console.print("[yellow]No notes found[/yellow]") | ||
| return | ||
|
|
||
| if json_output: | ||
| from ..cli.helpers import json_output_response | ||
| json_output_response([ | ||
| {"id": n.id, "title": n.title or "", "preview": (n.content or "")[:100]} | ||
| for n in notes | ||
| if isinstance(n, Note) | ||
| ]) | ||
| return |
There was a problem hiding this comment.
The JSON output logic can be simplified by merging the empty check and the main output block. Since the list comprehension naturally results in an empty list if notes is empty, both cases can be handled in a single block. This avoids redundant checks and repeated local imports. Additionally, using the relative import .helpers is more consistent with the existing imports in this file than ..cli.helpers. When outputting JSON for human consumption, ensure the implementation uses ensure_ascii=False to maintain readability of UTF-8 characters.
if json_output:
from .helpers import json_output_response
json_output_response([
{"id": n.id, "title": n.title or "", "preview": (n.content or "")[:100]}
for n in notes
if isinstance(n, Note)
])
return
if not notes:
console.print("[yellow]No notes found[/yellow]")
returnReferences
- When writing JSON files that may be human-read, use json.dumps(..., ensure_ascii=False) to improve readability by writing UTF-8 characters directly instead of as escape sequences.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/notebooklm/cli/note.py`:
- Around line 66-79: The JSON output branch must return a dict (not a raw list)
to match other list commands and the json_output_response signature: move the
import of json_output_response to the module top-level imports, then change both
places that call json_output_response to pass a dict like {"notes": [...],
"count": len(notes)} where the list is built from the notes iterable (keeping
the existing comprehension that filters by isinstance(n, Note) and maps to
{"id": n.id, "title": n.title or "", "preview": (n.content or "")[:100]}), and
when there are no notes call json_output_response({"notes": [], "count": 0}).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0f2124b8-525f-4f82-8ecc-e6ced5f954e1
📒 Files selected for processing (1)
src/notebooklm/cli/note.py
- Move json_output_response import to module level
- Restructure JSON output as {"notebook_id", "notes", "count"} dict
to match sibling commands (source list, artifact list)
- Handle empty notes in JSON path (returns count: 0, notes: [])
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/notebooklm/cli/note.py (1)
69-79:⚠️ Potential issue | 🟡 MinorKeep
countconsistent with the emittednotesarray.Line 75-Line 77 filters entries to
Note, but Line 78 useslen(notes). That can report acountlarger than the serialized array length.Suggested patch
if json_output: + note_items = [ + { + "id": n.id, + "title": n.title or "Untitled", + "preview": (n.content or "")[:100], + } + for n in notes + if isinstance(n, Note) + ] data = { "notebook_id": nb_id_resolved, - "notes": [ - { - "id": n.id, - "title": n.title or "Untitled", - "preview": (n.content or "")[:100], - } - for n in notes - if isinstance(n, Note) - ], - "count": len(notes), + "notes": note_items, + "count": len(note_items), } json_output_response(data) return🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/notebooklm/cli/note.py` around lines 69 - 79, The emitted JSON constructs "notes" by filtering the original notes iterable to instances of Note but sets "count" to len(notes), which can mismatch; change "count" to reflect the filtered list length (e.g., compute the list used for serialization or use sum(1 for n in notes if isinstance(n, Note))) so that the "notes" array and "count" stay consistent — update the code that builds the dict near the "notes" comprehension and the "count" key to use the same filtered/constructed collection.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/notebooklm/cli/note.py`:
- Line 57: Update the signature of note_list to include type annotations:
annotate ctx as click.Context, notebook_id as str | None, json_output as bool,
and client_auth as typing.Any (or a more specific type if known), and add an
explicit return type (-> None); also add necessary imports (from typing import
Any and import click) so mypy will type-check src/notebooklm/cli/note.py
successfully and the annotated signature for note_list matches the
Click-decorated parameters.
---
Duplicate comments:
In `@src/notebooklm/cli/note.py`:
- Around line 69-79: The emitted JSON constructs "notes" by filtering the
original notes iterable to instances of Note but sets "count" to len(notes),
which can mismatch; change "count" to reflect the filtered list length (e.g.,
compute the list used for serialization or use sum(1 for n in notes if
isinstance(n, Note))) so that the "notes" array and "count" stay consistent —
update the code that builds the dict near the "notes" comprehension and the
"count" key to use the same filtered/constructed collection.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 303dedeb-c6f8-46e6-b6ee-a6874de6d86f
📒 Files selected for processing (1)
src/notebooklm/cli/note.py
| @click.option("--json", "json_output", is_flag=True, help="Output as JSON") | ||
| @with_client | ||
| def note_list(ctx, notebook_id, client_auth): | ||
| def note_list(ctx, notebook_id, json_output, client_auth): |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Verify the updated signature location
rg -nP '^\s*def\s+note_list\s*\(' src/notebooklm/cli/note.py
# Validate type-checking expectation for src/notebooklm
python -m mypy src/notebooklm --ignore-missing-importsRepository: teng-lin/notebooklm-py
Length of output: 182
🏁 Script executed:
cd src/notebooklm/cli && head -80 note.pyRepository: teng-lin/notebooklm-py
Length of output: 2139
🏁 Script executed:
cd src/notebooklm/cli && tail -100 note.py | head -50Repository: teng-lin/notebooklm-py
Length of output: 1546
🏁 Script executed:
cd src/notebooklm && grep -r "def note_list" --include="*.py"Repository: teng-lin/notebooklm-py
Length of output: 136
🏁 Script executed:
cd src/notebooklm/cli && grep -A 1 "@with_client\|@artifact.command\|@note.command" note.py | grep "def " | head -10Repository: teng-lin/notebooklm-py
Length of output: 443
🏁 Script executed:
cd src/notebooklm/cli && find . -name "*.py" -type f | xargs grep -l "def.*->.*:" | head -5Repository: teng-lin/notebooklm-py
Length of output: 144
🏁 Script executed:
cd src/notebooklm/cli && head -100 chat.py | grep -A 5 "def chat"Repository: teng-lin/notebooklm-py
Length of output: 48
🏁 Script executed:
cd src/notebooklm/cli && sed -n '1,150p' chat.py | tail -70Repository: teng-lin/notebooklm-py
Length of output: 2750
Add type hints to the note_list function signature.
The function parameters should be annotated based on their Click decorators: ctx (Click context), notebook_id: str | None (from default=None), json_output: bool (from is_flag=True), and client_auth. Add a return type annotation as well. This is required by the coding guideline for src/notebooklm/**/*.py: "Use type hints and ensure type checking passes with mypy for src/notebooklm".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/notebooklm/cli/note.py` at line 57, Update the signature of note_list to
include type annotations: annotate ctx as click.Context, notebook_id as str |
None, json_output as bool, and client_auth as typing.Any (or a more specific
type if known), and add an explicit return type (-> None); also add necessary
imports (from typing import Any and import click) so mypy will type-check
src/notebooklm/cli/note.py successfully and the annotated signature for
note_list matches the Click-decorated parameters.
Summary
notebooklm note listwas the only list command in the CLI without a--jsonflag, making it impossible to parse notes programmatically.This PR adds
--jsontonote list, consistent withsource list,artifact list,notebook list, and every other list command.Output format:
Changes
cli/note.py: add--json/json_outputflag tonote list[]as JSON when no notes exist[{id, title, preview}]array for each noteno_wrap=Trueon the ID column in table view (UUIDs wrap badly in narrow terminals)Test plan
notebooklm note list --jsonreturns valid JSON arraynotebooklm note list --jsonon empty notebook returns[]notebooklm note list(no flag) still renders the rich table unchangedSummary by CodeRabbit
New Features
--jsonflag tonote listto return JSON (includes notebook_id, notes array with id, title defaulting to "Untitled", preview of first 100 chars, and count) instead of table output.Bug Fixes / Style