Every basic-memory CLI invocation paid roughly 2 seconds of module-import
cost before any work started, which blew the Claude Code plugin's
SessionStart hook budget on cold machines (#886). The cost came from
module-level imports that pulled the entire server stack into CLI startup:
- mcp/async_client.py imported FastAPI at module level, so every consumer
of get_client() loaded FastAPI even for cloud-routed or help-only paths.
- mcp/clients/*.py imported call_* helpers from basic_memory.mcp.tools.utils,
which executes the whole tools package __init__ — every MCP tool module
plus fastmcp and the mcp SDK.
- mcp/project_context.py imported fastmcp.Context and ToolError eagerly.
- CLI command modules (tool, ci, schema) imported MCP tool functions at
module level; db and the import_* commands pulled SQLAlchemy/Alembic and
the markdown/file-service stack; status/doctor/orphans/command_utils
imported ToolError (the mcp SDK) and basic_memory.db.
- schemas/base.py imported dateparser (~0.13s) for one helper function.
The fix only defers imports to the point of use (no behavior changes):
FastAPI now loads inside _resolve_local_asgi_database alongside the
existing lazy api.app import, so it is only paid when a request actually
routes through the in-process ASGI transport; the typed clients import
call_* per method; project_context uses PEP 563 annotations with Context
under TYPE_CHECKING; the CLI command modules import their heavy
dependencies inside the command bodies. Tests that patched the old
module-level aliases now patch the source modules instead.
Measured on a warm cache (python -X importtime / wall time):
- import basic_memory.cli.main: 1.92s -> 0.45s
- bm --help: 2.40s -> 0.52s
- bm tool search-notes --help: 2.40s -> 0.86s
A regression test asserts that importing the CLI entry module with full
command registration leaves fastapi, sqlalchemy, alembic, fastmcp, mcp,
basic_memory.api.app, basic_memory.db, basic_memory.markdown,
basic_memory.mcp.tools, and basic_memory.services out of sys.modules.
Fixes#886
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
- Prompts (search, continue_conversation) now call MCP tools directly
instead of going through API endpoints, matching the recent_activity
pattern and fixing #526 where prompts returned empty results
- sync_file catches SemanticDependenciesMissingError separately so
entities are still returned successfully when vector embedding fails,
with a clear warning instead of silent failure (#578)
- `bm status` and `bm doctor` default to local routing since they scan
the local filesystem — cloud routing returned Docker-internal paths
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Refactor CLI commands to use typed ProjectClient instead of raw HTTP calls,
and add workspace metadata to cloud project listings so users can distinguish
personal vs organization projects.
Key changes:
- 🔧 CLI commands now use ProjectClient typed API clients instead of
call_get/call_post with manual URL construction
- 🏢 Cloud project listings include workspace_name, workspace_type, and
workspace_tenant_id for each cloud-sourced project
- Pass config.default_workspace when fetching cloud projects via
_fetch_cloud_projects() and CLI list_projects
- Add --workspace flag to `bm project list` for explicit workspace override
- Add "Workspace" column to CLI project list table
- Add `bm tool list-projects` and `bm tool list-workspaces` JSON commands
- Comprehensive tests for workspace passthrough, merge behavior, and CLI routing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
feat: Add multiple projects support
feat: enhanced read_note for when initial result is not found
fix: merge frontmatter when updating note
fix: handle directory removed on sync watch
- incremental sync on watch
- sync non-markdown files in knowledge base
- experimental `read_resource` tool for reading non-markdown files in raw form (pdf, image)