Commit Graph

1458 Commits

Author SHA1 Message Date
Drew Cain c9770375e9 feat(mcp): add title and tags annotations to all MCP tool decorators (issue #826 phase 1)
Adds human-readable `title` and categorization `tags` to all ~24 @mcp.tool
decorators across the MCP tools package. FastMCP 3.3.1 (pinned in pyproject.toml)
supports both fields natively. Tags used: notes, search, projects, cloud, schema,
navigation, canvas, ui.

Extends test_tool_contracts.py with an async test that asserts every registered
tool has a non-empty title and at least one tag to prevent future regressions.

output_schema is explicitly deferred as a follow-up (phase 2): it requires
per-tool design decisions about which tools reliably return structured JSON and
how to handle tools that return str|dict depending on output_format.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Drew Cain <groksrc@gmail.com>
2026-06-11 15:21:25 -05:00
Drew Cain 0811c48252 fix(mcp): normalize note_types/entity_types/categories on direct call path and reject non-string list elements
Codex review of PR #962 identified two real issues:

1. CLI bypass: the BeforeValidator(parse_str_list) on note_types, entity_types, and
   categories only fires through MCP/Pydantic validation. The CLI path in
   cli/commands/tool.py calls search_notes() directly, so `bm tool search-notes
   --type note,task` arrived as note_types=["note,task"] and matched nothing.
   Fix: add in-body parse_str_list() normalization for all three params (mirroring
   the existing parse_tags() call for tags on the same code path).

2. Silent stringify: parse_str_list used str(raw) in the list branch, so [42] became
   ["42"] before Pydantic saw it, accepting invalid input as a no-result search
   instead of rejecting it. Fix: guard against non-string list elements and return
   the original value unchanged so Pydantic rejects it with a clear error.

Tests added: annotation-level split tests for note_types/entity_types/categories,
non-string-element rejection tests, async direct-call regression for note_types,
and unit-level parse_str_list non-string list tests in test_coerce.py.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Drew Cain <groksrc@gmail.com>
2026-06-11 14:38:24 -05:00
Drew Cain 747e64e5e9 feat(mcp): comma-split note_types/entity_types/categories in search_notes (#930)
Add `parse_str_list` to utils.py — like `parse_tags` but without stripping
'#' — and wire it as the BeforeValidator for note_types, entity_types, and
categories in search_notes. This makes passing "note,task" or
'["note","task"]' work correctly instead of being wrapped as a single literal
value by coerce_list.

coerce_list is left unchanged; canvas and other callers that depend on its
wrap-single-string behaviour are unaffected.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Drew Cain <groksrc@gmail.com>
2026-06-11 14:38:24 -05:00
Drew Cain be00df27c7 fix(api): allow setting default project when none is currently set
The v2 set_default_project_by_id handler fetched the current default
solely to echo it as old_project in the response, and raised 404
"No default project is currently set" when none existed. That guard
(marked # pragma: no cover, never tested) made the bootstrap/recovery
case impossible: with no default row in the DB, `bm project default
<name>` always failed -- yet that is exactly the command you reach for
when no default is set.

ProjectStatusResponse.old_project is already Optional, so the guard
served no schema requirement. Remove it and build old_project only
when a previous default exists, else pass None.

Add a regression test that clears is_default, sets a default via the
endpoint, and asserts 200, old_project is None, the new project is
default, and a follow-up read-back returns it.

Closes #975

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Drew Cain <groksrc@gmail.com>
2026-06-11 14:17:35 -05:00
Drew Cain 3ce42de57e test(mcp): narrow read_note return before string assertions for ty
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Drew Cain <groksrc@gmail.com>
2026-06-11 13:30:16 -05:00
Drew Cain a143072d35 fix(core): require line-anchored frontmatter fences in file_utils
has_frontmatter(), parse_frontmatter(), and remove_frontmatter() detected
frontmatter by substring/split (`content.startswith("---")` plus
`content.split("---", 2)`) rather than by line-anchored fences. A single-line
string that merely starts with `---` — e.g. `---\nstatus: active\n---\nBody`
where `\n` are literal backslash-n characters, a common CLI/agent input shape —
was misread as frontmatter: yaml parsed `\nstatus` as a key that got merged into
the note's YAML on disk, and the body was silently transformed.

Introduce a shared `_split_frontmatter()` helper that anchors both fences to
their own lines (`^---[ \t]*$`), tolerating leading blank lines so dedented
heredoc-style content still parses. All three public helpers now delegate to it,
preserving existing behavior for valid frontmatter (BOM stripping, empty -> {},
non-dict -> ParseError, and the existing ParseError messages callers assert on).

Closes #972

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Drew Cain <groksrc@gmail.com>
2026-06-11 13:30:16 -05:00
Drew Cain 497a4e0a43 fix(cli): preload deferred rich/typer modules before in-place upgrade
brew upgrade / uv tool upgrade delete the running install's files while the old process is still alive. rich defers importing rich._emoji_codes until print time and typer defers typer.rich_utils until excepthook time, so printing the post-upgrade status message crashed the exiting process with ModuleNotFoundError (and the traceback renderer crashed too). Import both before launching the upgrade subprocess, while the files still exist.

Signed-off-by: Drew Cain <groksrc@gmail.com>
2026-06-11 12:55:04 -05:00
Drew Cain ec5fac8d76 chore(ci): wait for the release PR merge to land before tagging
Addresses Codex review on PR #961: gh pr merge may not complete synchronously if merge gates exist (or while GitHub computes mergeability), so the recipes now fall back to queueing auto-merge and poll origin/main for the rebased bump commit (up to 5 minutes) before tagging, with manual finish instructions on timeout.

Signed-off-by: Drew Cain <groksrc@gmail.com>
2026-06-11 01:35:34 -05:00
Drew Cain 05bfd0f04e chore(ci): remove user-specific absolute paths from release runbook
Signed-off-by: Drew Cain <groksrc@gmail.com>
2026-06-11 01:35:34 -05:00
Drew Cain 338f357f55 chore(ci): route release recipes through PRs and refresh release runbook
main's ruleset now rejects direct pushes, which broke 'just release' mid-flight for v0.22.0. The release/beta recipes now land the version bump via a rebase-merged release PR and tag the rebased commit (found by commit subject). Adds a changelog-on-main pre-flight check, and updates AGENTS.md and the release runbook to match, including the docs.basicmemory.com steps which still described the old src/pages site.

Signed-off-by: Drew Cain <groksrc@gmail.com>
2026-06-11 01:35:34 -05:00
Drew Cain 8e7825ba01 chore: update version to 0.22.0 for v0.22.0 release
Signed-off-by: Drew Cain <groksrc@gmail.com>
v0.22.0
2026-06-11 00:53:13 -05:00
Drew Cain dc29ba2a00 docs(core): add v0.22.0 changelog entry
Signed-off-by: Drew Cain <groksrc@gmail.com>
2026-06-11 00:53:13 -05:00
Drew Cain fe9b2e9c95 Add glama.json to claim the Glama MCP directory listing (#953) 2026-06-11 00:02:21 -05:00
Paul Hernandez 650f88a2c5 docs(cli): document personal-vs-team cloud sync semantics (#947)
Folds in the intent of #857 reconciled with the post-#920 push/pull reality. Closes #851.

Co-authored-by: Drew Cain <groksrc@users.noreply.github.com>
Signed-off-by: phernandez <paul@basicmemory.com>
2026-06-10 14:50:28 -05:00
Paul Hernandez 2f7ef136de perf(ci): lean CI — testmon-select branches, shard postgres, drop depot, remove bossbot (#945)
Branch builds testmon-select against cached baselines; 3-shard Postgres on 3.14 only; free runners; 120s hang ceiling; non-code changes skip the matrix; BM Bossbot and infographic machinery removed.

Signed-off-by: phernandez <paul@basicmemory.com>
2026-06-10 14:21:57 -05:00
Drew Cain 0a3a6bbd96 test(mcp): integration coverage for colliding observation permalinks (#926)
Refs #909. Integration coverage over the #931 fix.

Signed-off-by: phernandez <paul@basicmemory.com>
2026-06-10 14:05:05 -05:00
Drew Cain 7bb7664fae fix(core): use Observation.permalink in build_context to match the search index (#946)
Closes #929

Signed-off-by: phernandez <paul@basicmemory.com>
2026-06-10 14:05:02 -05:00
Paul Hernandez db578ccfdb fix(mcp): recover edit_note when file exists on disk but is not indexed (#934)
Closes #581

Signed-off-by: phernandez <paul@basicmemory.com>
2026-06-10 14:04:59 -05:00
Paul Hernandez df485aa5a4 fix(mcp): tighten search_notes tags input and normalize for direct callers (#941)
Refs #910. Follow-up to #932.

Signed-off-by: phernandez <paul@basicmemory.com>
2026-06-10 14:04:56 -05:00
K Jagadeeswara Reddy 44ecec2917 test(mcp): assert tags param and tag: shorthand comma equivalence
Integration regression test for #910: the tags= parameter and the
tag:alpha,beta query shorthand must return the same results for the
same comma string.

Cherry-picked from #918. The validator swap itself (coerce_list ->
parse_tags) landed separately via #932; this carries the remaining
test-only piece with original authorship.

Signed-off-by: K Jagadeeswara Reddy <social@jagadeeswar.com>
2026-06-10 12:04:07 -05:00
Paul Hernandez 49041a5168 docs(skills): fix npx skill install docs and ignore docs assets (#927)
Refs #930 docs cleanup; adds docs/assets to gitignore.

Signed-off-by: phernandez <paul@basicmemory.com>
2026-06-10 01:30:50 -05:00
Paul Hernandez 8cbe1634b6 fix(cli): don't let bm cloud setup overwrite an existing rclone remote (#923)
Closes #922

Signed-off-by: phernandez <paul@basicmemory.com>
2026-06-10 01:02:43 -05:00
Paul Hernandez c4b651f5b0 fix(mcp): accept page/page_size in read_note for parity with sibling tools (#933)
Closes #883

Refs #882

Signed-off-by: phernandez <paul@basicmemory.com>
2026-06-10 00:03:22 -05:00
Paul Hernandez ca9a4d9c12 fix(mcp): split comma-separated tags in search_notes tags param (#932)
Closes #910

Signed-off-by: phernandez <paul@basicmemory.com>
2026-06-09 22:23:26 -05:00
Paul Hernandez aa9594d82a perf(ci): disable semantic search in default test fixtures (#938)
Disable the silently-on semantic embedding stack in default test fixtures and deselect on-demand benchmarks from CI int jobs. int SQLite 337s -> 110s, Postgres unit ~25min -> ~13min.

Signed-off-by: phernandez <paul@basicmemory.com>
2026-06-09 21:55:21 -05:00
Paul Hernandez 93494b8c13 ci(ci): gate bossbot on passing tests (#937)
Signed-off-by: phernandez <paul@basicmachines.co>
2026-06-09 20:47:34 -05:00
Paul Hernandez 93ed34001b fix(core): disambiguate truncated observation permalinks to prevent index collisions (#931)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 20:20:46 -05:00
Paul Hernandez ec94feb6a4 Merge branch 'main' into codex/fix-bossbot-assets-publish 2026-06-09 19:23:27 -05:00
Paul Hernandez 831b9141a5 ci(ci): use pytestmon and Depot runners (#928)
Signed-off-by: phernandez <paul@basicmachines.co>
2026-06-09 19:20:08 -05:00
phernandez 2de19713f6 fix(ci): make BM Bossbot asset cleanup idempotent
Signed-off-by: phernandez <paul@basicmachines.co>
2026-06-09 19:14:13 -05:00
phernandez 79fcfbce6a fix(ci): restrict BM Bossbot to trusted PR authors
Signed-off-by: phernandez <paul@basicmachines.co>
2026-06-09 17:16:49 -05:00
phernandez 3d22ba3004 fix(ci): harden BM Bossbot finalization
Signed-off-by: phernandez <paul@basicmachines.co>
2026-06-09 16:32:06 -05:00
phernandez 62229d9d0a fix(ci): address BM Bossbot PR feedback
Signed-off-by: phernandez <paul@basicmachines.co>
2026-06-09 15:55:36 -05:00
phernandez 03ba268cb1 feat(ci): add BM Bossbot PR gate
Signed-off-by: phernandez <paul@basicmachines.co>
2026-06-09 15:18:40 -05:00
Paul Hernandez de53e0ecc5 feat(cli): per-workspace rclone remotes for Team push/pull (#920)
Signed-off-by: phernandez <paul@basicmachines.co>
2026-06-08 17:39:37 -05:00
Paul Hernandez 4128cac9ab docs(core): add Basic Machines agent style guidance (#921)
Signed-off-by: phernandez <paul@basicmachines.co>
2026-06-08 17:09:50 -05:00
Paul Hernandez 9b53d7863f feat(cli): add Team-safe cloud push/pull and gate sync to Personal workspaces (#917)
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.
2026-06-08 14:08:49 -05:00
Paul Hernandez a8d034b940 fix(mcp): point move mismatch guidance at landing path (#916)
Signed-off-by: phernandez <paul@basicmachines.co>
2026-06-08 10:42:38 -05:00
Paul Hernandez 7c0937f658 fix(mcp): validate navigation pagination and fix recent_activity project display (#915)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 23:03:00 -05:00
Paul Hernandez 8570d96bad fix(cli): align bm tool commands with MCP (error exits, overwrite, category, default) (#913)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 22:56:43 -05:00
Paul Hernandez 480a2d9468 fix(mcp): resolve memory:// in move_note and stop false cross-project rejections (#914)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 22:44:08 -05:00
Paul Hernandez df7452e3ba fix(core): make note_types search filter case-insensitive (#912)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 22:44:04 -05:00
Paul Hernandez 85a8e59d0f fix(core): split comma-separated tags in parse_tags (#911)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 22:44:01 -05:00
Paul Hernandez 07cb7a606b docs(core): mark LiteLLM provider experimental (#899)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 20:13:54 -05:00
Paul Hernandez 0ef03edde6 feat(cli): add --type to the write-note tool command (#907)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 20:06:44 -05:00
Paul Hernandez efe43a10ea feat(cli): add --wait and --timeout to bm status (#906)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 19:58:02 -05:00
Paul Hernandez 4fe6fe09c8 feat(core): add observation category filter to search (#908)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 19:57:19 -05:00
Paul Hernandez 8acdb49a41 fix(core): reuse a single embedding provider per process (#903)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 19:14:07 -05:00
Paul Hernandez f7304bf553 feat(cli): improve workspace and cloud bisync command discoverability (#905)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 18:18:17 -05:00
Paul Hernandez 5b034f081d fix(core): self-heal corrupt FastEmbed model cache (#900)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 17:37:30 -05:00