Skip to content

feat(note): add --json flag to note list command#259

Open
Flosters wants to merge 2 commits intoteng-lin:mainfrom
Flosters:feat/note-list-json
Open

feat(note): add --json flag to note list command#259
Flosters wants to merge 2 commits intoteng-lin:mainfrom
Flosters:feat/note-list-json

Conversation

@Flosters
Copy link
Copy Markdown

@Flosters Flosters commented Apr 9, 2026

Summary

notebooklm note list was the only list command in the CLI without a --json flag, making it impossible to parse notes programmatically.

This PR adds --json to note list, consistent with source list, artifact list, notebook list, and every other list command.

Output format:

$ notebooklm note list --json
[{"id": "...", "title": "...", "preview": "first 100 chars of content"}]

Changes

  • cli/note.py: add --json / json_output flag to note list
    • Returns [] as JSON when no notes exist
    • Returns [{id, title, preview}] array for each note
    • Sets no_wrap=True on the ID column in table view (UUIDs wrap badly in narrow terminals)

Test plan

  • notebooklm note list --json returns valid JSON array
  • notebooklm note list --json on empty notebook returns []
  • notebooklm note list (no flag) still renders the rich table unchanged
  • ID column no longer wraps in narrow terminals

Summary by CodeRabbit

  • New Features

    • Added --json flag to note list to return JSON (includes notebook_id, notes array with id, title defaulting to "Untitled", preview of first 100 chars, and count) instead of table output.
    • When the flag is used and no notes are found, an empty JSON list is returned.
  • Bug Fixes / Style

    • Improved ID column readability in the default table output to prevent wrapping.

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.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 9, 2026

📝 Walkthrough

Walkthrough

Added a --json (json_output) option to note list and updated note_list signature to accept json_output. When enabled, the command returns structured JSON (includes notebook_id, notes with id, title, preview, and count) and exits early; otherwise it renders a Rich table with ID column no_wrap=True.

Changes

Cohort / File(s) Summary
Note list CLI & output
src/notebooklm/cli/note.py
Added --json CLI option and new json_output parameter on note_list. When json_output is true, emits JSON via json_output_response(data) (includes notebook_id, notes array with id, title default "Untitled", preview first 100 chars, and count) and returns early. When false, retains Rich table output and sets the table "ID" column to no_wrap=True.

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
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

🐰 A tiny flag hops in to show,
JSON whispers instead of a row,
Titles that sleep, previews cut neat,
CLI carrots and output sweet,
Hop, list, and off it goes! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main change: adding a --json flag to the note list command, which is the primary feature implemented in this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines 65 to 80
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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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]")
                return
References
  1. 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.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between a997718 and 45a4194.

📒 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>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/notebooklm/cli/note.py (1)

69-79: ⚠️ Potential issue | 🟡 Minor

Keep count consistent with the emitted notes array.

Line 75-Line 77 filters entries to Note, but Line 78 uses len(notes). That can report a count larger 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

📥 Commits

Reviewing files that changed from the base of the PR and between 45a4194 and 306922e.

📒 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):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ 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-imports

Repository: teng-lin/notebooklm-py

Length of output: 182


🏁 Script executed:

cd src/notebooklm/cli && head -80 note.py

Repository: teng-lin/notebooklm-py

Length of output: 2139


🏁 Script executed:

cd src/notebooklm/cli && tail -100 note.py | head -50

Repository: 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 -10

Repository: teng-lin/notebooklm-py

Length of output: 443


🏁 Script executed:

cd src/notebooklm/cli && find . -name "*.py" -type f | xargs grep -l "def.*->.*:" | head -5

Repository: 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 -70

Repository: 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant