L2-normalizes FastEmbed output vectors at the provider boundary so SQLite vector scoring keeps its unit-vector contract for custom FastEmbed models such as multilingual MiniLM variants.
Zero vectors are preserved as-is to avoid division errors, and the provider tests cover both non-unit vectors and zero-vector behavior.
Verification:
- uv run pytest tests/repository/test_fastembed_provider.py -q
- uv run ruff check src/basic_memory/repository/fastembed_provider.py tests/repository/test_fastembed_provider.py
- uv run ruff format --check src/basic_memory/repository/fastembed_provider.py tests/repository/test_fastembed_provider.py
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: tk-pkm111 <133480534+tk-pkm111@users.noreply.github.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Adds LiteLLM as a semantic embedding provider, including provider configuration, vector normalization, live-provider evaluation tooling, and documentation for OpenAI, Cohere, Azure Foundry, Azure OpenAI, and NVIDIA NIM-style cases.
Maintainer follow-up on this PR added provider hardening, asymmetric document/query embedding support, dimension-forwarding controls, SQLite/Postgres vector invalidation coverage, and the repeatable live LiteLLM harness.
Verification:
- Full base-repo Tests workflow passed for 3d4e092ceb: https://github.com/basicmachines-co/basic-memory/actions/runs/27072071785
- Live LiteLLM harness passed locally for OpenAI, Cohere, and Azure Foundry.
Co-authored-by: Aarish Alam <arishalam121@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: RheagalFire <arishalam121@gmail.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Review feedback on #876:
- Codex (P2): the SessionStart "Where to write" brief said all auto-capture goes to
captureFolder, but captureFolder is only the PreCompact checkpoint folder. Narrowed
it — checkpoints go to captureFolder; decisions/tasks/notes follow
placementConventions (or topic folders when none are set), so proactive captures
aren't dumped into sessions/ alongside checkpoints.
- Claude review: replaced the em dash in the skills-source-guard echo string with a
hyphen so the instruction's console output stays ASCII (CLAUDE.md compatibility).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
- Project mapping: document local vs cloud project creation — a cloud-workspace
project needs a cloud-connected MCP server and a workspace selector; a purely
local server fails to mkdir the cloud-style path. Pin cloud primaryProject to the
external_id UUID.
- Install shared skills: guard against clobbering a source checkout — if ./skills is
git-tracked and holds memory-* dirs (the skills' own source repo), skip the npx
install instead of overwriting the working copy with published versions.
- Cloud/teams: warn that the SessionStart brief reads only the first 6 shared
projects per session, so order the most relevant first when more are configured.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
- Focus question is now load-bearing: it drives an adaptive light folder-structure
suggestion and is stored. The placement step branches learn (existing project
with notes) vs suggest (new/empty project) instead of only learning.
- Seed schema notes via the write_note `metadata` param instead of content
frontmatter. The cloud write path silently coerces nested YAML to the string
'[object Object]', corrupting schema/settings (basic-memory-cloud#1000); the
metadata param round-trips correctly on both local and cloud.
- Surface placementConventions + captureFolder in the SessionStart brief so the
output style's "follow stored placement conventions" reflex has something to
follow — previously written by setup but never shown to Claude (dead config).
- Add a post-settings smoke test (run the hook's recall query, confirm routing)
and a restart prompt gated on outputStyle in the close.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Codex flagged (review 4397232451) that the hooks locate the CLI via
`command -v basic-memory || command -v bm`, but our own README recommends
connecting the MCP server as `claude mcp add basic-memory -- uvx basic-memory mcp`
— an ephemeral uv run that leaves NO binary on PATH. In that (recommended) setup
both hooks exited before doing anything, so the bridge silently did nothing even
though the MCP server worked.
Both hooks now resolve the CLI invocation as: prefer a `basic-memory`/`bm` binary
(fast), else fall back to `uvx basic-memory`, else `uv tool run basic-memory`, else
silent no-op. The launcher may be multi-token, so the embedded Python splits it with
shlex and prepends it to each command list. The uv cache is already warm from
running the MCP server, so the fallback is cheap.
Verified: with no binary on PATH but uvx present, SessionStart now produces a brief
(previously a silent no-op). Updated the README requirement to note uvx-only works.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Codex flagged /basic-memory:remember passing a UUID primaryProject as `project`
(review 4397206729) — the same routing bug already fixed in the hooks and the
share skill. Rather than fix only the flagged spot, audited every project-ref
site and made them all consistent:
- remember: route primaryProject as project_id when it's a UUID (was the flag)
- status: same for the primaryProject-scoped search_notes queries
- setup: same for the schema-seed write to primaryProject
share/SKILL.md and both hooks (session-start, pre-compact) already handle it.
That's now every place the plugin routes to a project ref — a project ref may be
a workspace-qualified name (-> project) or an external_id UUID (-> project_id),
and all six sites detect and route accordingly. Plugin validates.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Addresses the remaining review finding (deferred while #859 was open; now merged,
so the hermes module is on main and the comment lands cleanly here). The two
getattr sites read MCP SDK result/tool fields; `mcp` is an unpinned dependency
(plugin.yaml), and its CallToolResult / ListToolsResult / Tool shapes have varied
across SDK versions — so the defensive getattr is intentional, not the speculative
kind CLAUDE.md warns against. Added constraint comments at both sites explaining why.
No behavior change. `just package-check-hermes` passes (244 passed, 12 integration
tests skipped without BM_INTEGRATION).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Resolves the in-scope findings from the github-actions review (its prepared commit
1283a46 couldn't push — 403) and the Codex P2 (review 4397168884):
- pre-compact.sh: route a UUID primaryProject via --project-id, not --project.
This mirrors session-start.sh; without it a UUID-configured project gets correct
session briefs but SILENT checkpoint failures at every compaction. The substantive
correctness fix. Verified end-to-end (checkpoint now lands in a UUID-keyed project).
- skills/share/SKILL.md: same UUID routing for /basic-memory:share team targets —
pass external_id UUIDs as project_id, qualified names as project. (Codex P2.)
- skills/status/SKILL.md: read outputStyle from the ROOT settings object, not the
basicMemory block — otherwise /basic-memory:status reports capture reflexes as off
for a correctly-configured user.
- docs/getting-started.md: move outputStyle out of the basicMemory block to root in
the team example (matches settings.example.json; it's a top-level Claude Code key).
- session-start.sh: split the >100-char project-routing line.
Out of scope here: the hermes/__init__.py getattr-rationale comments the bot
prepared live in the #859 consolidation code (not in this PR's diff) — flagged for
that PR. Plugin validates; hooks smoke-tested.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
The MCP server is a hard prerequisite for the plugin (its hooks and skills call
it), but the two were in separate README sections with no link between them.
- The "Claude Code plugin" install section now points to "Connect your AI client"
and states the MCP server must be connected first.
- The "Connect your AI client → Claude Code" section now points to the plugin for
the full memory bridge.
- /basic-memory:setup's prerequisite check is now concrete: verify the MCP server
via list_memory_projects, and if absent, walk the user through installing and
`claude mcp add basic-memory -- uvx basic-memory mcp` before starting the interview.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
The umbrella install section still described the plugin as bundling "skills,
hooks, and an agent" — the agent was removed in the v0.4 redesign. Updated to the
current surface: hooks (briefings + checkpoints), the opt-in output style, and the
/basic-memory:* skills.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
1. SessionStart worker pool was sized 8 but submits up to 9 searches (3 primary
+ MAX_SHARED=6 shared) — the 9th queued behind a possibly-10s call and could
push the hook past Claude Code's 20s SessionStart timeout before the brief
printed. Size the pool to 3 + MAX_SHARED so nothing queues. (Introduced in
Phase 4 when the third primary query was added.)
2. captureChattyness was dead config — written by setup but read by no hook, and
its "heavy" level advertised "checkpoint without compaction," which no hook
implements. The real proactivity knob is `outputStyle`. Removed
captureChattyness entirely and folded the "how active should I be?" question
into the output-style step (the single, actually-wired toggle); the always-on
hooks run regardless. Updated setup skill, settings.example.json, and DESIGN.
Both flagged by Codex (review 4395911984); same class as the recallTimeframe
dead-config fix from the earlier self-review. Plugin validates; hook smoke-tested.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Following the evaluation on #866, the genuinely-useful behavior from the deleted
Claude Code plugin skills is preserved as framework-agnostic shared skills (usable
by any MCP agent — Claude Desktop, OpenClaw, etc.), instead of being lost in the
v0.4 clean-break. The Claude Code plugin itself is unchanged — it deliberately
relies on hooks + the output style for capture/recall and pulls these shared skills
via /basic-memory:setup.
New shared skills (ported from the deleted set, CC-specific glue stripped):
- memory-curate (from knowledge-organize) — the real coverage gap: knowledge-GRAPH
curation (orphan notes, relation suggestion, duplicate merge, tag/folder audit,
hub notes, sparse-note enrichment). Distinct from memory-defrag, which is
agent-memory-FILE hygiene.
- memory-continue (from continue-conversation) — resume prior work by rebuilding
context from the graph (build_context / recent_activity / search), timeframe table,
resume playbooks.
- memory-capture (from knowledge-capture) — synthesize a thread's current state into
one note, rewriting in place via a thread_id key. The Claude-Code-specific session
UUID is generalized to "any stable thread id your host exposes."
Also completed memory-notes' edit reference (added prepend / replace_section to the
two existing operations). Updated skills/README.md and skills/CLAUDE.md.
`just package-check-skills` validates 13 skills. NOTE: skills/skills-lock.json is a
generated artifact of the `npx skills` CLI (not used by Claude Code or OpenClaw) and
should be regenerated once the set is final — not hand-edited here.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Addresses the consolidation direction from issue #866: the Claude Code plugin
should consume the canonical skills/ set, not reinvent a parallel one. Rather
than vendoring copies into the plugin, /basic-memory:setup now offers to install
the shared memory-* skills via `npx skills add basicmachines-co/basic-memory
--path skills` (the existing SPEC-58 distribution path, same source OpenClaw
bundles). The plugin stays "hooks + Claude-Code-specific skills" and pulls the
shared toolkit on demand — skills/ remains the single source of truth, no
duplication in the repo.
Updated the setup interview + apply steps, README, getting-started, and CHANGELOG.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Resolves the 8 self-review findings on PR #865:
session-start.sh:
- Wire recallTimeframe into a new "Recent sessions" query (--after_date) — it was
parsed but unused after the Phase 4 rewrite. The brief now surfaces recent
session checkpoints (the resume cursor), which is the one query the recency
window legitimately applies to. (#1)
- Guard secondaryProjects/teamProjects JSON types — a string value was iterated
character-by-character into bogus per-char queries. (#3)
- A configured-but-unreachable/misnamed primaryProject now emits a one-line
"couldn't read" signal instead of a silent blank brief. (#7)
pre-compact.sh:
- Filter transcript turns by the `isMeta` and `toolUseResult` flags instead of a
`text.startswith("<")` heuristic. This stops dropping legitimate user messages
that start with "<" (#4) and stops capturing tool-result/meta frames as human
turns (#8) — verified against a real transcript (25 human turns cleanly
separated from 8 meta + 288 tool-result frames).
- Title now uses second precision so rapid same-minute compactions don't collide
and silently drop/overwrite a checkpoint; removed the unused stamp/slug. (#2)
- Empty-checkpoint guard now requires at least one real user turn, not just any
turn, so an assistant-only transcript can't produce a dangling-title note. (#5)
validate_skills.py:
- parse_frontmatter now skips indented lines, capturing only top-level keys, so a
schema note's nested schema:/settings: children can't overwrite a top-level
type/entity via last-write-wins. Documented its single-line-only limitation. (#6)
All verified end-to-end; `just package-check-claude-code` and
`package-check-skills` pass; ruff clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Adds user- and contributor-facing documentation under plugins/claude-code/docs/:
- why-combine-memory.md — the value proposition: two-memories/two-jobs, the
1+1=3 framing, the three personas (thinker / builder / operator) with concrete
use cases, and the official "use both" stance. The "why would I want this" doc.
- getting-started.md — a ~5-minute guided walkthrough: prerequisites, install,
/basic-memory:setup, a capture→checkpoint→brief loop to see it work, the team
workspace path, tuning knobs, and troubleshooting.
- architecture.md — how it works flow by flow, with mermaid diagrams: the bridge
(working memory <-> durable graph), the four surfaces, SessionStart and
PreCompact sequence flows, capture reflexes, team read/share, component map.
Linked all three from the README's new Documentation section. Marked Phase 5
docs done in DESIGN. Migration guide intentionally skipped — v0.4 is a clean
break (uninstall old, install new), noted in the CHANGELOG. Dogfood remains the
open human step.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Implements DESIGN.md Phase 4, grounded in a real two-workspace BM Cloud account.
Recall reads across the team:
- hooks/session-start.sh rewritten to read the primary project (active tasks +
open decisions) AND each configured shared/team project (open decisions) in
parallel via ThreadPoolExecutor. Routes by workspace-qualified name or
external_id UUID (project names collide across workspaces, so bare names won't
route); per-call timeout, capped at 6 shared projects, graceful on any failure.
Adds a "From shared projects (read-only)" section + the share-vs-capture note.
Verified against the real my-team-2 workspace (OAuth routing) and local fixtures.
Deliberate team writes:
- skills/share/SKILL.md → /basic-memory:share <note>: copies a note from the
primary project into a configured teamProjects target's promoteFolder, with
shared_from attribution and a confirmation step. Preserves the note's type so
shared decisions stay findable in the team's structured recall. Pulled forward
from future-work since team usage needs a safe write path.
Safe by default: capture (PreCompact checkpoints, /remember) NEVER writes to a
shared project. The proposed teamProjects.autoWrite flag is deliberately not
shipped — documented as future rather than ship an unenforced flag.
Config: secondaryProjects (read sources) + teamProjects (share targets with
promoteFolder), both requiring qualified names/UUIDs. setup interview step 3 now
configures them via list_workspaces; status reports team read-sources + share
targets; settings.example.json documents the shape. REQUIRED_SKILLS adds share.
Discovery verified: Skills (4): remember, setup, share, status. Passes
`just package-check-claude-code` incl. `claude plugin validate . --strict`.
Updated README (Teams section), CHANGELOG, and DESIGN §6 + Phase 4 status.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Adds the bootstrap interview from DESIGN.md Phase 3 as a prose skill, plus the
first-run nudge that points users to it.
- skills/setup/SKILL.md → /basic-memory:setup: an adaptive ~2-min interview that
maps the Claude Code project to a Basic Memory project (pick existing or create),
seeds the session/decision/task schemas into it, optionally learns the project's
placement conventions (list_directory + sampling, stored in placementConventions),
writes the basicMemory settings block, and enables the capture output style.
- hooks/session-start.sh: nudges toward /basic-memory:setup on first run (when no
basicMemory config block exists). The nudge now survives a failed/empty task
query, so a brand-new user with no project yet still sees it; it stops once setup
writes the config (config presence is the sentinel — no separate file).
- validate_claude_plugin.py: REQUIRED_SKILLS now includes setup.
Corrects the Phase 1 schema-seeding finding: writing a schema file's content via
write_note (CLI or MCP) indexes it as type: schema AND resolves via schema_validate
— the earlier "must use note_type/metadata" conclusion was confounded by the enum
YAML bug (since fixed). Verified end-to-end: all three schemas seed, index, and
resolve (entity=Session/Decision/Task); nudge verified across all config states;
discovery shows Skills (3): setup, remember, status.
Updated README, CHANGELOG, and DESIGN Phase 3 status. Passes
`just package-check-claude-code` incl. `claude plugin validate . --strict`.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Adds the two deliberate-gesture skills from DESIGN.md Phase 2, both as prose
skills (skills are prompts) — robust to arbitrary user text and any MCP server
tool-name prefix, no bundled-script path placeholders.
- skills/remember/SKILL.md → /basic-memory:remember <text>: quick capture to the
rememberFolder (default bm-remember) with a first-line title and manual-capture
tag, via write_note. Model-invocable so "remember that…" triggers it.
- skills/status/SKILL.md → /basic-memory:status: user-only diagnostic
(disable-model-invocation) reporting active project, capture/remember folders,
output-style state, recent session checkpoints, and active-task count.
validate_claude_plugin.py now requires the shipped skill set (REQUIRED_SKILLS)
and validates each skill's name/description frontmatter. Passes
`just package-check-claude-code` incl. `claude plugin validate . --strict`.
Discovery verified end-to-end: installed from a local marketplace and confirmed
via `claude plugin details` — Skills (2): remember, status; Hooks (2):
SessionStart, PreCompact; Agents (0) — namespaced as /basic-memory:<name>.
Updated README (Commands table), CHANGELOG, and DESIGN Phase 2 status.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Adds a --scope {all,core,packages} option to scripts/update_versions.py so the
version bump can target just the host-native agent artifacts (Claude Code plugin
+ root/local marketplaces, Hermes, OpenClaw) separately from the Python package
core (__init__.py, server.json). Default scope is "all" — fully backward
compatible.
New justfile recipes:
- set-version <v> [scope] — write version (all|core|packages)
- set-version-dry-run <v> [scope] — preview
- set-packages-version <v> — plugin/agent artifacts only
- set-packages-version-dry-run <v> — preview
The release / beta / release-dry-run recipes now route through set-version
instead of calling the script inline, so version-setting has one source of
truth and is reusable by the /release skill. Updated the /release command doc
to describe the consolidated version update and the new recipes.
Tests cover the new scope filtering (packages leaves core untouched and vice
versa; invalid scope errors). Existing lockstep behavior unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Reframes the Claude Code plugin as the bridge between Claude's working
memory and Basic Memory's durable graph, per plugins/claude-code/DESIGN.md.
This is the minimal-first vertical slice with a clean break from v0.3.x.
Added:
- hooks/session-start.sh — SessionStart brief: one structured `type: task`
query against the configured (or default) project + always-on recall
prompt. Plain stdout, silent if BM is absent.
- hooks/pre-compact.sh — PreCompact checkpoint: writes a `type: session`
note before compaction (extractive). Only writes when primaryProject is
set, so it never touches an un-opted-in graph.
- output-styles/basic-memory.md — capture reflexes (search-first, typed
decision notes, cite permalinks); keep-coding-instructions: true.
- schemas/{session,decision,task}.md — picoschema seeds (validation: warn)
so plugin-written notes are findable via metadata_filters. task mirrors
the memory-tasks skill.
- settings.example.json — copyable config with sensible defaults.
Removed (clean break):
- the six bundled skills, the basic-memory-manager agent, the
PreToolUse/PostToolUse write_note hooks, the basic-memory config-note
convention, and PLUGIN.md. Equivalent workflows live in top-level skills/.
Other:
- Rewrote scripts/validate_claude_plugin.py for the new layout (hooks +
output-style + schemas; agent dropped; skills optional). Passes
`just package-check-claude-code` incl. `claude plugin validate --strict`.
- Rewrote README around the bridge story; updated CHANGELOG, marketplace
descriptions, and the AGENTS.md package-check note.
- Hooks tested end-to-end against throwaway projects (brief surfaces tasks;
checkpoint writes a queryable session note; both degrade silently).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
WIP design doc for modernizing the Claude Code plugin around the
"bridge between Claude auto-memory and Basic Memory" positioning.
- Reframes the plugin as connective tissue between Claude's working
memory and BM's durable graph (per docs/vs-built-in-memory).
- Defines the four core surfaces: SessionStart brief, PreCompact
checkpoint, output-style reflexes, deliberate skills.
- Leans on BM schemas + structured metadata search for deterministic
recall (ships session/decision/task picoschemas).
- Maps Claude Code projects to BM projects; team-workspace safety
(read-across, write-by-gesture); bootstrap interview.
- Records verified findings for 8 open questions (CC v2.1.153 /
basic-memory 0.21.5) from an investigate→adversarial-verify pass:
PreCompact 600s budget enables LLM summaries; metadata_filters
confirmed; path-scoped rules cut (don't load yet); commands are
plugin-namespaced; bootstrap via SessionStart sentinel.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
The Task schema in memory-tasks/SKILL.md and the enum examples in
memory-schema/SKILL.md used a bare YAML flow sequence followed by a
trailing description:
status?(enum): [active, blocked, done, abandoned], current state
A flow sequence ([...]) cannot be followed by ', current state' —
python-frontmatter/PyYAML raises a ParserError. In schema_router.py,
_schema_frontmatter_from_file catches that error and silently falls
back to DB metadata, so the schema's enum constraints never load.
Per the picoschema parser docstring, enum descriptions belong inside
the parens:
status?(enum, current state): [active, blocked, done, abandoned]
Fixes 4 lines (1 in memory-tasks, 3 in memory-schema). The
memory-literary-analysis schemas use the quoted-string form
("role(enum)": "[...], desc"), which is valid YAML handled by
_parse_enum_string, so they were left unchanged.
Verified: all yaml schema blocks now parse via parse_schema_note,
and just package-check-skills passes (10 skills validated).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: phernandez <paul@basicmachines.co>
Follow-up PR review: Phase 2 prose used a CWD-relative
`--output-schema schemas/verdicts.schema.json`, while Phase 1 uses
`"$SKILL_DIR/schemas/..."`. Following Phase 2 from the repo root would
resolve the wrong path and break the Codex refutation call. Now matches the
$SKILL_DIR-qualified pattern used everywhere else; swept the file to confirm
all command/flag path positions are qualified.
Signed-off-by: phernandez <paul@basicmachines.co>