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 <noreply@anthropic.com>
Signed-off-by: Drew Cain <groksrc@gmail.com>
Drop the synthesized title/permalink header and the (no content)
placeholder: plain mode is the content verbatim (note body, or the literal
file with --include-frontmatter). Decoration belongs to the Rich path.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Drew Cain <groksrc@gmail.com>
The file's own frontmatter carries title/permalink, so the header line
duplicated it. Plain without the flag keeps the header.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Drew Cain <groksrc@gmail.com>
With the flag the API returns the literal file as content, so both display
paths printed the frontmatter twice (synthesized block/panel + the block
inside content). Plain now prints the file verbatim; Rich keeps the panel
and strips the block from the Markdown body.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Drew Cain <groksrc@gmail.com>
The API content field keeps the blank line left by frontmatter stripping;
plain print() rendered it as a double gap under the header. JSON mode stays
byte-faithful.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Drew Cain <groksrc@gmail.com>
The four interactive bm tool commands (search-notes, read-note,
build-context, recent-activity) now support three output modes instead of
two:
- JSON — raw machine-readable output (--json, or automatically when piped).
- Rich — colored Panel/Table/Tree/Markdown (the default TTY experience).
- Plain — undecorated, greppable text with no ANSI colors, box-drawing, or
markup (--plain, forced even when piped).
Precedence, highest first: --json > --plain > non-TTY (JSON) > TTY (config
style). Passing both --json and --plain is a typer error with a non-zero
exit. Each TTY default is governed by the new BasicMemoryConfig field
cli_output_style ("rich"/"plain", default "rich", env
BASIC_MEMORY_CLI_OUTPUT_STYLE), mirroring the existing CLI-behavior config
conventions; its Field description documents the setting since the repo has
no general CLI-settings reference doc.
Plain renderers deliberately do NOT apply rich.markup.escape — escaping is
only correct on the Rich path and would corrupt literal brackets — so
[draft]/[fact] survive verbatim. The plain search path keeps the total=0
fallback so the corrected result count matches the Rich path. String tool
results still route to JSON to preserve ty narrowing.
Module docstring and each command's help text now document the three modes
and the config default.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Drew Cain <groksrc@gmail.com>
Bug 1 — Rich markup injection: user-sourced titles, permalinks, snippets,
observation categories/content, and frontmatter keys/values were interpolated
directly into Rich markup strings, causing bracketed text (e.g. "[draft]",
"[fact]") to be silently swallowed or restyled. Apply markup_escape() at
every injection point in _display_search_results, _display_read_note,
_display_build_context, and _display_recent_activity. Observation labels use
markup_escape on the full "[category] content" fragment so the literal brackets
around the category are also escaped.
Bug 2 — frontmatter panel ignores flag: _display_read_note rendered the
frontmatter panel whenever result["frontmatter"] was non-empty, but the JSON
payload always carries that key regardless of --include-frontmatter. Thread
the boolean flag as a keyword argument and gate the panel on both the flag and
non-empty content.
Bug 3 — "0 result(s)" subtitle: the search API returns total=0 even when
results is non-empty. result.get("total", len(results)) never triggered its
default because the key exists; fall back to len(results) when total is falsy
but results is non-empty, keeping page-count math consistent.
Tests: add cases for bracketed title surviving search output, "[fact]" category
surviving build-context tree, read-note without --include-frontmatter asserting
no frontmatter panel, and search with total=0 asserting correct subtitle count.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Drew Cain <groksrc@gmail.com>
- `_display_read_note`: render frontmatter key/value panel when present so
`--include-frontmatter` is not silently dropped in the Rich path
- `_display_build_context`: render ContextResult.observations under each
primary node (category + truncated content) so interactive users see the
same core facts as `--json` output; update subtitle to include count
- Update BUILD_CONTEXT_RESULT fixture with real ObservationSummary shape
(type/category/content/permalink/file_path/created_at)
- Add test_read_note_rich_include_frontmatter asserting frontmatter keys appear
- Add test_build_context_rich_renders_observations asserting category/content visible
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Drew Cain <groksrc@gmail.com>
_display_build_context was reading top-level item.get("title")/item.get("type")
on each results[i], but the real GraphContext.model_dump() shape wraps every item
as a ContextResult with primary_result + related_results nested inside. This
made every related note render as an empty tree node.
Fix:
- Rewrite _display_build_context to iterate context_items, build a primary-result
node from item["primary_result"], then add each item["related_results"] entry as
a child with relation_type/type/title rendered.
- Update _display_search_results to use the real SearchResponse key "current_page"
(not "page"), pass query from the CLI argument, add Score and Snippet columns
(score + matched_chunk/content truncated to 200 chars) as the issue requested.
- Update test fixtures in test_cli_tool_rich_output.py to match the real payload
shapes (nested ContextResult, current_page, score/matched_chunk fields, no
updated_at in recent-activity).
- Fix test_build_context_json_flag_overrides_tty assertion to navigate the nested
primary_result/related_results shape.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Drew Cain <groksrc@gmail.com>
search-notes, read-note, build-context, and recent-activity now display
formatted Rich output (tables, panels, Markdown rendering) when stdout is
an interactive TTY. When piped or redirected the commands continue to emit
raw JSON exactly as before. A new --json flag is available on each command
to force JSON output even in a TTY.
Follows the bm status / bm project list precedent: Rich by default for
humans, JSON for machines.
Closes#678
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Drew Cain <groksrc@gmail.com>
Adds additive, git-style `bm cloud push`/`pull` that are safe on shared Team workspaces (never delete on the destination; conflicts abort by default with `--on-conflict {fail|keep-local|keep-cloud|keep-both}`), and gates the destructive `bm cloud sync`/`bisync` mirrors to Personal workspaces. Closes#858. Longer-term Team-safe reconciler tracked in #862; workspace-scoped mount info (Codex P1) tracked as a follow-up.
`bm cloud login` only caught SubscriptionRequiredError after the
post-login `/proxy/health` subscription check. OAuth succeeds and tokens
are saved, but if that check returns anything else — a 5xx while the
tenant instance is still provisioning, a 403/401 whose body doesn't match
the subscription_required shape, or a transport error — make_api_request
raises a generic CloudAPIError that escaped uncaught, dumping a raw
httpx.raise_for_status traceback. Users read this as "login failed" even
though authentication actually worked.
Add a CloudAPIError handler that prints a clean, actionable message and
exits non-zero. make_api_request wraps every httpx error (status and
transport) in CloudAPIError, so the single handler covers them all.
Fixes#863.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>