From 83f60054df03deeea12bbbd769a750c11c69e3b0 Mon Sep 17 00:00:00 2001 From: Drew Cain Date: Fri, 12 Jun 2026 11:35:16 -0500 Subject: [PATCH] feat(cli): rename read-note --include-frontmatter to --frontmatter Keep --include-frontmatter as a deprecated alias for back-compat (the old name shipped in 0.22.0). The MCP tool parameter include_frontmatter is unchanged. Co-Authored-By: Claude Signed-off-by: Drew Cain --- src/basic_memory/cli/commands/tool.py | 15 +++++++++------ tests/cli/test_cli_tool_rich_output.py | 17 +++++++++++++++-- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/basic_memory/cli/commands/tool.py b/src/basic_memory/cli/commands/tool.py index 8da155e4..23d394de 100644 --- a/src/basic_memory/cli/commands/tool.py +++ b/src/basic_memory/cli/commands/tool.py @@ -198,7 +198,7 @@ def _display_read_note(result: dict[str, Any], *, include_frontmatter: bool = Fa console.print(Panel(header, expand=False)) - # Trigger: --include-frontmatter was passed; the MCP tool populates "frontmatter". + # Trigger: --frontmatter was passed; the MCP tool populates "frontmatter". # Why: the JSON payload always carries a "frontmatter" key regardless of the flag, # so checking non-empty alone would render it even without the flag. The flag # must be threaded in to gate the panel. @@ -215,7 +215,7 @@ def _display_read_note(result: dict[str, Any], *, include_frontmatter: bool = Fa fm_table.add_row(markup_escape(str(key)), markup_escape(str(value))) console.print(Panel(fm_table, title="[dim]frontmatter[/dim]", expand=False)) - # Trigger: --include-frontmatter makes the API return the literal file, so + # Trigger: --frontmatter makes the API return the literal file, so # content starts with the frontmatter block the panel above already shows. # Why: rendering it again through Markdown duplicates the frontmatter (and # Markdown mangles the --- fences into rules/headings). @@ -393,11 +393,11 @@ def _plain_read_note(result: dict[str, Any]) -> None: """Render read-note content faithfully: the note body, or the literal file. Plain mode adds NO decoration: no header line, no synthesized frontmatter - block, no placeholder for empty notes. Without --include-frontmatter the + block, no placeholder for empty notes. Without --frontmatter the API returns the note body; with it, the literal file (frontmatter block included). Either is printed verbatim, trimmed only of the surrounding newline artifacts the API keeps from frontmatter stripping, so the output - round-trips (e.g. ``read-note X --plain --include-frontmatter > note.md``). + round-trips (e.g. ``read-note X --plain --frontmatter > note.md``). """ content = result.get("content", "") body = content.strip("\n") if content else "" @@ -600,7 +600,10 @@ def write_note( def read_note( identifier: str, include_frontmatter: bool = typer.Option( - False, "--include-frontmatter", help="Include YAML frontmatter in output" + False, + "--frontmatter", + "--include-frontmatter", + help="Include YAML frontmatter in output (--include-frontmatter is a deprecated alias)", ), json_output: bool = typer.Option( False, "--json", help="Output raw JSON instead of formatted display" @@ -634,7 +637,7 @@ def read_note( Examples: bm tool read-note my-note - bm tool read-note my-note --include-frontmatter + bm tool read-note my-note --frontmatter bm tool read-note my-note --plain bm tool read-note my-note --json """ diff --git a/tests/cli/test_cli_tool_rich_output.py b/tests/cli/test_cli_tool_rich_output.py index 5082b610..3e989064 100644 --- a/tests/cli/test_cli_tool_rich_output.py +++ b/tests/cli/test_cli_tool_rich_output.py @@ -456,7 +456,7 @@ def test_read_note_rich_include_frontmatter(mock_mcp): frontmatter block must be stripped from the Markdown body or it renders again under the panel. """ - result = _tty_runner(["tool", "read-note", "test-note", "--include-frontmatter"]) + result = _tty_runner(["tool", "read-note", "test-note", "--frontmatter"]) assert result.exit_code == 0, f"CLI failed: {result.output}" # Frontmatter panel appears with key/value data @@ -776,7 +776,7 @@ def test_read_note_plain_include_frontmatter(mock_mcp): With the flag, content IS the file (frontmatter block included); plain mode prints it verbatim and must not prepend a synthesized key/value block. """ - result = _tty_runner(["tool", "read-note", "test-note", "--plain", "--include-frontmatter"]) + result = _tty_runner(["tool", "read-note", "test-note", "--plain", "--frontmatter"]) assert result.exit_code == 0, f"CLI failed: {result.output}" # ONLY the literal file: no header line, output starts at the fence @@ -787,6 +787,19 @@ def test_read_note_plain_include_frontmatter(mock_mcp): assert result.output.count("title: Test Note") == 1 +@patch( + "basic_memory.cli.commands.tool.mcp_read_note", + new_callable=AsyncMock, + return_value=READ_NOTE_RESULT_WITH_FRONTMATTER, +) +def test_read_note_include_frontmatter_alias(mock_mcp): + """--include-frontmatter still works as a deprecated alias for --frontmatter.""" + result = _tty_runner(["tool", "read-note", "test-note", "--plain", "--include-frontmatter"]) + + assert result.exit_code == 0, f"CLI failed: {result.output}" + assert result.output.startswith("---\ntitle: Test Note") + + @patch( "basic_memory.cli.commands.tool.mcp_read_note", new_callable=AsyncMock,