In cloud mode, ConfigManager has no local config file so
default_project always returned None. Add async
get_default_project_name() on ProjectService that falls back
to the database is_default flag when ConfigManager returns None.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
🔧#640 — LinkResolver selects worst match instead of best
Replace `min(results, key=lambda x: x.score)` with `results[0]`.
Both SQLite and Postgres return results sorted best-first in SQL,
so using `results[0]` is backend-agnostic and correct.
🔧#641 — search_notes output_format="text" returns raw Pydantic model
Add `_format_search_markdown()` that formats SearchResponse as readable
markdown with title, permalink, score, and matched snippet per result.
Update prompts to use `output_format="json"` since they need structured
data for result counting and branching logic.
🔧#642 — metadata_filters with `note_type` key returns empty results
Add `_METADATA_KEY_ALIASES` mapping at the tool level that aliases
`note_type` → `type` before passing metadata_filters to the search query.
The frontmatter field is `type`, not `note_type`.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
frontmatter.Post.__init__ takes `content` and `handler` as positional
parameters. When user YAML frontmatter contains these as field names,
unpacking metadata via **kwargs causes "got multiple values for argument
'content'".
Replace frontmatter.loads() with frontmatter.parse() + Post() + update()
in entity_parser, and replace the **metadata unpacking in
entity_service.update_entity() with the same safe pattern.
Fixes basic-memory-cloud#375
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
YAML block sequence syntax can cause PyYAML to parse scalar fields like
`title` and `type` as lists instead of strings. Downstream code calls
.strip()/.casefold() on these values, crashing with
"'list' object has no attribute 'strip'".
Add _coerce_to_string() helper that joins list items with ", " and apply
it in entity_parser.parse_markdown_content() and
entity_service.fast_edit_entity() where these fields are extracted.
Fixes basic-memory-cloud#376
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
- Wrap isatty() in _is_interactive_session() with try/except ValueError
so MCP stdio transport shutdown no longer produces noisy tracebacks
- Check both search_vector_chunks AND search_vector_embeddings exist
before running JOIN queries in get_embedding_status(), fixing
OperationalError when only the chunks table is present
- Add test for closed-stream scenario
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Add EmbeddingStatus model to project_info schemas and wire it into
ProjectInfoResponse. ProjectService.get_embedding_status() queries
vector tables for chunk/embedding counts, detects orphaned chunks
and missing embeddings, and recommends reindex when appropriate.
Handles both SQLite and Postgres backends. 🔍
Includes 6 unit tests covering: disabled search, missing vector tables,
entities without chunks, orphaned chunks, healthy state, and integration
with get_project_info().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
build_context now falls back to LinkResolver when an exact permalink lookup
returns empty results. This reuses the same resolution pipeline as read_note
(permalink candidates, title match, file path, FTS) so callers no longer
get empty results for valid note identifiers.
Also changes ensure_frontmatter_on_sync default to True — frontmatter is
now added during sync by default. Tests updated accordingly.
🔧 ContextService accepts optional LinkResolver, wired via DI in all 3 factory variants
✅ 2027 unit + 278 integration tests passing
Closes#582
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Cloud projects with bisync had a split-brain problem: `path` held a cloud
slug while the actual local directory lived in `local_sync_path`. This caused
`bm status` and file sync to fail for bisync'd cloud projects.
Changes:
- Config migration promotes `local_sync_path` → `path` for entries where
`path` is a non-absolute cloud slug
- `ensure_project_paths_exists` skips cloud-only projects with slug paths
- `initialize_file_sync` and watch service now keep cloud projects that have
an absolute local path (bisync copy) instead of skipping all cloud projects
- `sync-setup` and `project add --cloud --local-path` set both `path` and
`local_sync_path` to the local directory
- `sync-setup` creates the project in the local DB for immediate MCP use
- `_get_sync_project` falls back from `local_sync_path` to `path`
- Config load errors now show user-friendly messages instead of stack traces
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Add a new config option to enforce frontmatter on markdown sync when missing, writing derived title/type/permalink and updating in-memory metadata before upsert. Add startup warning when this option is combined with disable_permalinks to make precedence explicit. Add config/sync/initialization tests for the new behavior, and stabilize project list CLI integration assertions by forcing a wide terminal in tests to avoid Rich truncation.
Signed-off-by: phernandez <paul@basicmachines.co>
MCP server crashes in cloud mode with:
ValueError: DATABASE_URL must be set when using Postgres backend
Root cause: initialize_app() did not check cloud_mode_enabled before
trying to initialize the database. Only ensure_initialization() had
the check. In cloud mode, tenant DBs are per-request via headers,
not via DATABASE_URL environment variable.
Also includes minor formatting fix in telemetry.py.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Add is_test_env property to BasicMemoryConfig that checks:
- config.env == "test"
- BASIC_MEMORY_ENV env var is "test"
- PYTEST_CURRENT_TEST is set
Replace duplicated test detection logic in:
- api/app.py
- mcp/server.py
- services/initialization.py
- telemetry.py (disables telemetry during tests)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Denormalizes project_id onto Relation and Observation tables to enable
efficient project-scoped queries without joins. Migration backfills
from associated entity and adds pg_trgm extension with GIN indexes
for fuzzy link resolution on PostgreSQL.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>