Skip to content

Latest commit

 

History

History
625 lines (484 loc) · 48.2 KB

File metadata and controls

625 lines (484 loc) · 48.2 KB

Basic Memory for Claude Code — Design

Status: Draft (v0.4 modernization) Supersedes: the current PLUGIN.md feature-list framing Related:


1. Positioning — what this plugin actually is

Claude Code now has a working-memory layer of its own: auto-memory at ~/.claude/projects/<path>/memory/MEMORY.md, plus per-subagent memory at .claude/agent-memory/. Claude writes to it itself, loads the first 200 lines / 25 KB at session start, and splits topic files off when MEMORY.md grows too long.

This plugin is not a memory layer. It is the bridge between Claude's working memory and Basic Memory's durable, semantic, portable graph.

This is the product's documented stance, not our invention. The official Basic Memory vs built-in memory page is explicit:

"Basic Memory doesn't replace them — it works alongside them. The best setup uses both."

Their stated division of labor:

  • Built-in memory (Claude auto-memory, CLAUDE.md): coding standards, preferences, working instructions — "the immediate operational layer."
  • Basic Memory: architecture decisions, research, meeting summaries, project context — "persistent, interconnected organizational infrastructure" that's searchable, linkable, and portable across every AI tool.

The plugin is the mechanism that makes that documented "use both" setup actually happen automatically, instead of requiring the user to manually shuttle context between the two.

Two memories, two jobs:

Claude auto-memory Basic Memory
Where ~/.claude/projects/<path>/memory/ Your filesystem + BM graph
Who writes Claude, automatically Both human and Claude, deliberately
Scope Per-project, local-only, hidden Cross-project, syncable, portable, sharable
Captures Build commands, debugging tips, patterns Decisions, research, observations + relations
Loaded Every session, automatically Queried on demand
Format Whatever Claude finds useful Markdown + frontmatter + observations + relations

1 + 1 = 3 is the thesis: auto-memory tells the plugin "we were here, on this topic, recently"; BM brings the graph neighborhood, open decisions, and active tasks. Neither alone is targeted. Together, the brief is.

2. Personas

The plugin serves three personas with the same spine and divergent add-ons.

The thinker — non-developer power user

Writer, researcher, consultant, planner. Lives in Claude Code or Claude Desktop. No git, no PRs. Keeps a body of work that compounds over months. Cares about: picking up where they left off, not losing texture of long conversations, recalling what they actually decided.

The builder — developer

Uses git, GitHub, runs Claude Code as their primary IDE companion. Same needs as the thinker, plus a code dimension. Cares about: tying decisions to commits, surviving multi-hour debugging without amnesia, code archaeology weeks later.

The operator — PM, team lead, ops

Runs multiple concurrent projects through Claude. Maybe occasional git, mostly meetings, decisions, status. Cares about: not bleeding context between projects, recalling cross-project patterns, weekly digests delivered without their attention.

The thinker's needs are the foundation. The builder adds git hooks. The operator adds routines.

3. Architecture — the four core surfaces

We keep the plugin to a small number of well-chosen artifacts. Everything else (workflow skills, agents, deep references) loads from the top-level skills/ package on demand.

plugins/claude-code/
├── .claude-plugin/
│   ├── plugin.json
│   └── marketplace.json
├── hooks/
│   ├── session-start.sh        # ambient: brief Claude on what's relevant
│   └── pre-compact.sh          # ambient: checkpoint before amnesia
├── output-styles/
│   └── basic-memory.md         # reflexes: search first, capture decisions
│                               # NOTE: rules/ deferred — path-scoped rules don't load yet (Q5)
├── skills/
│   ├── bm-setup/SKILL.md          # /basic-memory:bm-setup — bootstrap interview (first-run)
│   ├── bm-remember/SKILL.md       # /basic-memory:bm-remember <text> — quick deliberate capture
│   └── bm-status/SKILL.md         # /basic-memory:bm-status — show plugin state
├── schemas/                    # picoschema seeds, copied into the user's BM project at bootstrap
│   ├── session.md              # type: session — resume checkpoints
│   ├── decision.md             # type: decision — durable choices + rationale
│   └── task.md                 # type: task — active work tracking
├── settings.example.json       # opinionated defaults, easy to copy
├── DESIGN.md                   # this file
└── README.md                   # rewritten around the bridge story

Three layers, matching what Hermes and OpenClaw converged on:

Layer Surface When it fires What it does
Ambient hooks/, rules/ Lifecycle events, file context Brief Claude, checkpoint, guide placement
Background output-styles/basic-memory.md System prompt, every turn Reflexes — search first, capture inline
Deliberate skills/{bm-setup,bm-remember,bm-status}/ User invokes (/basic-memory:bm-setup, /basic-memory:bm-remember) One-shot user gestures

4. The core flows

4.1 Cold-start resume — SessionStart hook

Fires once per session before the LLM sees anything. Assembles a targeted briefing by querying multiple sources in parallel and emitting a structured context block.

Inputs: cwd, configured BM project(s), and (optionally) auto-memory's MEMORY.md read from disk — see the timing caveat below.

Verified (Q1): the firing order of SessionStart relative to auto-memory load is not documented, so we don't assume auto-memory is already in context when the hook runs. If the brief wants to factor in auto-memory's last-topic, the hook reads ~/.claude/projects/<path>/memory/MEMORY.md directly from disk (always available) rather than relying on it being in context. The 1+1=3 trick still works; it's just a file read, not a context read.

Parallel queries — structured metadata search, not fuzzy full-text. This is the multiplier: because the plugin ships schemas (§4.5) that stamp type and status onto every note it writes, recall is deterministic. We ask for exactly the notes that matter, not "things that look textually similar to the cwd."

  1. Active tasks: search_notes("", metadata_filters={"type": "task", "status": {"$in": ["active", "in-progress"]}}, project=<primary>)
  2. Open decisions: search_notes("", metadata_filters={"type": "decision", "status": "open"}, project=<primary>)
  3. Recent sessions: search_notes("", metadata_filters={"type": "session"}, after_date=<recallTimeframe>, project=<primary>) — the last checkpoint carries the resume cursor
  4. Recent activity (catch-all, anything untyped): recent_activity(timeframe=<recallTimeframe>, project=<primary>)
  5. (If team workspaces enabled) Queries 1–3 against team project(s) in parallel

All five run concurrently. Structured filters return precise sets; the catch-all recent_activity sweeps up anything not yet schema-typed. The brief leads with the precise sets and falls back to the sweep.

Output format (cribbed from OpenClaw, refined):

## Basic Memory — context for this session

**Project:** my-app (personal) · 7 days since last activity

### Active tasks
- **Wire SessionStart hook** — outline drafted, hook script next [permalink]
- **Decide on routines for v0.4** — blocked on platform docs [permalink]

### Recent activity (last 3 days)
- DESIGN.md draft for Claude Code plugin (decisions/)
- Notes on Hermes recall pattern (research/)

### Possibly relevant to your current work
- BM project mapping decision (decisions/) — referenced 2x last week

---

You have Basic Memory available. Before answering recall questions ("what did we
decide", "where did we leave off"), search BM first. When the user makes a
material decision, capture it as a DecisionNote inline. Cite permalinks when
referencing prior work.

The trailing instruction block is the recall prompt — without it, agents ignore injected context. Customizable via settings.json (recallPrompt key).

Output mechanism (verified, Q4): SessionStart injects context two ways — plain stdout (anything the hook prints is added to context, no JSON needed) or JSON {"hookSpecificOutput": {"hookEventName": "SessionStart", "additionalContext": "..."}}. We use plain stdout — simpler, fewer parse failures. Two hard constraints:

  • 10,000-character cap per output string. Overflow is spilled to a file and replaced with a preview + path. The brief must stay well under 10k — so recent-activity inclusion is bounded (cap each section at N items; prefer permalinks over previews).
  • Shell-profile interference. If the user's shell profile echoes anything on startup, it corrupts hook output. The hook must run with a clean environment / guard against profile noise.

Performance budget: SessionStart should complete in under 1 second. Parallelize all queries. If BM is unreachable, emit a minimal "BM unavailable; auto-memory only this session" stub and continue — never block the session.

4.2 Compaction without amnesia — PreCompact hook

Fires immediately before context compaction. Writes a SessionNote (checkpoint) to BM containing what Claude is about to forget.

Content of the SessionNote:

  • Frontmatter: type: session, started, ended_or_compacted, project, cwd, claude_session_id
  • Observations:
    • [decision] items surfaced this session
    • [problem] / [attempted] / [rejected] — what we tried that didn't work (critical for resume)
    • [next-step] — explicit cursor for the next session
  • Relations: produced [[<note>]] for each BM write this session
  • Open threads: free-form list of unresolved questions

Where it lands: <primary-bm-project>/sessions/<YYYY-MM-DD-HHMM>-<slug>.md.

Latency budget (verified, Q2) — much more generous than we assumed. PreCompact hooks block compaction synchronously with a 600-second (10-minute) default timeout (configurable). A multi-second MCP write_note call — or even a full LLM summarization pass — fits comfortably inside that window. This flips our earlier "extractive-only" assumption:

  • Default is now a real summary, not extractive heuristics. The hook can call out to summarize the transcript into a proper SessionNote. preCompactCapture: "summarized" becomes the sensible default; "extractive" stays as a fast fallback option.
  • The hook does not need decision: block (we're not preventing compaction, just recording before it). It writes the SessionNote and exits 0, letting compaction proceed.
  • One nuance from verification: the timeout cancels the hook if exceeded, and compaction proceeds regardless — so the write should be resilient to being killed mid-flight (write the note early, enrich after, rather than building everything then writing once).

Optional conformance check (verified, Q8): the hook may validate the note it just wrote via schema_validate(identifier=<new-note>) — the single-note path is cheap and bounded (one entity load + one schema-file read). Do not call batch schema_validate(note_type="session") here — that scans every session note with per-note file I/O (O(N)); leave batch validation and schema_diff to the nightly hygiene routine.

4.3 The reflex layer — output-style (rules deferred)

output-styles/basic-memory.md appends to the system prompt:

  • Before answering questions that look like recall, call search_notes (prefer metadata_filters for typed notes) or build_context. Don't answer from training.
  • When the user makes a material decision, write a DecisionNote (type: decision, status: open) inline as part of your response — conforming to the decision schema so it's queryable later.
  • When referencing prior work, cite the permalink.
  • When auto-memory and BM disagree, flag the conflict explicitly.
  • Stay within the active BM project unless the user explicitly asks for another.

The reflexes deliberately target typed, schema-conforming writes. A decision captured without type: decision is invisible to the next session's structured recall; a decision captured with it shows up in the SessionStart brief automatically. The output-style is what makes capture land in the queryable shape.

User opts in via outputStyle: basic-memory in their settings. Not auto-applied — this is the user's choice of how Claude behaves.

Path-scoped rules are deferred (verified, Q5 — refuted). The original plan was a rules/basic-memory.md with paths: frontmatter that loads placement/format conventions only when a BM note is in context. Verification found that path-scoped rules do not load automatically at all in the current Claude Code (open bug anthropics/claude-code#16853, "never worked"), and even when fixed they're repo-relative — they would not match BM notes living outside the git tree under ~/basic-memory/ (issue #25562). So we don't ship a path-scoped rule.

Where placement/format conventions live instead: the bootstrap-inferred conventions (folder naming, favored observation categories, frontmatter shape) are stored in the basicMemory block of .claude/settings.json and surfaced two ways:

  1. The SessionStart hook folds a one-line conventions summary into the brief (within the 10k budget).
  2. The output-style references "follow the project's stored placement conventions when writing notes."

If/when path-scoped rules start working and support out-of-tree globs, we can add a rules/basic-memory.md as a third surface. Until then it's dead weight that wouldn't load.

4.4 Deliberate gestures — skills

Three skills only, each Claude-Code-specific (everything else lives in top-level skills/).

Verified (Q3) — slash commands are always plugin-namespaced. A skill folder skills/bm-setup/ in a plugin named basic-memory is invoked as /basic-memory:bm-setup — namespacing is mandatory and can't be shortened. Skills are auto-discovered on install, no extra registration.

Naming decision (revised after dogfood). The four skills carry a bm- prefix (bm-setup, bm-remember, bm-share, bm-status). Phase 2 originally dropped the prefix, reasoning that the basic-memory: namespace already disambiguates. Dogfooding the merged plugin proved otherwise: the Claude Code slash picker shows only the bare skill name — the plugin name is relegated to the focused-item tooltip — so un-prefixed skills blend into the list with other plugins' similarly-named ones (spec, simplify, schedule, …). The bm- prefix makes them group and read as ours in the picker. The cost is a cosmetic stutter in the full form (/basic-memory:bm-setup).

This is a workaround, not the end state. The real fix is upstream: anthropics/claude-code#50486 (open) asks the picker to namespace plugin skills the way it already does for commands. If/when that lands, the picker would show basic-memory:setup natively — revisit dropping the bm- prefix then, to kill the stutter. (Aside: #22063 — a name frontmatter field stripping the namespace — does not affect us; with name == dir the namespace is retained, confirmed live in the session skill list.)

/basic-memory:bm-setup — bootstrap interview (§7). Run after install and any time the user wants to reconfigure.

/basic-memory:bm-remember <text> — quick capture. Writes to a bm-remember/ folder, separated from auto-captures. First line becomes title (truncated to 80 chars), tagged manual-capture. Optional --project flag for cross-project.

/basic-memory:bm-status — show plugin state: active BM project, capture folders, recent SessionNotes, sync status, last successful BM call. Trust-building UI.

4.5 The schema layer — why our note types are contracts, not conventions

Basic Memory ships a schema system (Picoschema) and structured metadata search. The plugin leans on both. This is the single biggest "leverage what BM already does" decision in the design.

The plugin ships schemas for the note types it creates. Three to start:

Note type type: Written by Purpose
Session session PreCompact hook, /basic-memory:bm-handoff (future) Resume cursor — what we were doing, what's next
Decision decision output-style reflex, /basic-memory:bm-decide (future) Durable record of choices + rationale
Task task user + Claude Active work tracking (aligns with skills/memory-tasks)

These conform to the SessionNote / DecisionNote picoschema shapes defined in SPEC-55, so when the SPEC-55 Writer SDK and async pipeline land, validation Just Works and nothing has to change in user-facing behavior.

Why schemas, not just "write notes with a convention":

  1. Deterministic recall. A schema stamps type and status into frontmatter. SessionStart (§4.1) then queries metadata_filters={"type":"session"} and gets exactly the sessions — no fuzzy matching, no false positives, no missed notes. Structured search is AND-composable: {"type":"decision","status":"open","tags":["auth"]} returns open auth decisions, full stop. This is the difference between a recall brief that's precise and one that's vibes.

  2. The schema teaches the structure. Picoschema fields map to observations and relations and frontmatter:

    # schemas/session.md
    ---
    title: Session
    type: schema
    entity: session
    version: 1
    schema:
      summary: string, one-paragraph what-happened
      next_step?(array): string, cursor for resuming
      decision?(array): string, decisions made this session
      problem?(array): string, problems hit (incl. attempted-and-rejected)
      produced?(array): Entity, notes created this session   # → `produced [[note]]` relations
    settings:
      validation: warn
      frontmatter:
        project: string
        cwd?: string
        started: string
        status?(enum): [open, resumed, closed]
        claude_session_id?: string
    ---

    The PreCompact hook and the output-style don't need to memorize the SessionNote shape — they read it from the schema. The schema is the single source of truth for "what a good checkpoint looks like."

  3. Drift detection becomes a hygiene routine. schema_diff flags when session/decision notes start diverging from their schema over time. A future nightly routine can run it and surface drift. schema_validate can run in the PreCompact hook to confirm the checkpoint we just wrote actually conforms before we rely on it next session.

  4. Bootstrap can infer, not dictate. schema_infer analyzes the user's existing notes and suggests schemas/conventions. The bootstrap interview (§7) runs it to seed the stored placement/format conventions (in the basicMemory settings block — see §4.3, since path-scoped rules don't load yet) with the user's real patterns instead of imposing ours. Opinionated defaults, flexible to what's already there.

Validation mode: warn, never strict. We stamp structure to make recall work, but we never block a write because a field is missing. The user's flow is sacred; the schema is a helper, not a gate. Users who want strict validation flip it themselves.

Where schemas live: the plugin's bootstrap writes them into the primary BM project's schemas/ folder (schemas/session.md, schemas/decision.md, schemas/task.md) — they become normal, editable BM notes the user owns, not hidden plugin internals. If the user already has schemas for these types, bootstrap leaves them alone.

5. Claude Code projects ↔ Basic Memory projects

These are orthogonal concepts that the plugin must explicitly map.

Claude Code project = a cwd-keyed working directory (typically a repo). Auto-memory uses this as its key.

Basic Memory project = a logical knowledge base. Could be 1:1 with a repo, could be cross-cutting, could be unrelated.

5.1 Mapping model

Each Claude Code project has:

  • One primary BM project — destination for SessionStart context + PreCompact checkpoints + /basic-memory:bm-remember. Required.
  • Zero or more secondary BM projects — read-only by default for recall (SessionStart can query them); writes require explicit user gesture.

Mapping is configured in .claude/settings.json:

{
  "basicMemory": {
    "primaryProject": "my-app",
    "secondaryProjects": ["team-engineering", "personal/notes"],
    "captureFolder": "sessions",
    "rememberFolder": "bm-remember",
    "recallPrompt": "...",
    "recallTimeframe": "3d"
  }
}

Resolution order for the primary project when the setting is absent:

  1. .claude/settings.local.json (per-user override)
  2. .claude/settings.json (team-committed default)
  3. basic-memory note at the repo root (legacy, kept for back-compat)
  4. User's global default project (from BM CLI: bm project list --default)
  5. Trigger bootstrap interview

5.2 The 1:1 default is fine but not enforced

For most thinker-persona users, one repo ↔ one BM project is natural. For developers and operators, fan-out is common (one BM project spans multiple repos: a "company-knowledge" project that aggregates context from every code repo). The plugin should not force 1:1.

6. Team / shared workspaces

Basic Memory Cloud has workspaces (each an org or personal space) containing projects. Some users contribute to shared projects visible to teammates. This needs deliberate, safe-by-default design.

Routing reality (verified against a real two-workspace account): project names collide across workspaces — e.g. main and getting-started exist in more than one. A bare name won't route. Every cross-workspace ref in config must therefore be a workspace-qualified name (<workspace-slug>/<project>, e.g. my-team-2/notes, from the qualified_name field) or an external_id UUID (most stable — survives renames). The CLI accepts these via --project / --project-id; the hook detects a UUID and routes accordingly. Cross-workspace reads route over the user's OAuth session (no API key needed) — confirmed working for both structured search and recent-activity.

6.1 Defaults — safe by design

  • Auto-capture defaults to personal. SessionNotes, PreCompact checkpoints, and /basic-memory:bm-remember quick captures only ever land in primaryProject. The capture hooks never write to a shared project — full stop, no opt-in flag in v0.4.
  • Recall reads across. SessionStart queries the shared projects in parallel for open decisions and folds them into the brief (read-only — discloses nothing).
  • Sharing is a deliberate gesture. /basic-memory:bm-share copies a personal note into a configured team project with attribution, after explicit confirmation. The personal→team boundary is always a visible, manual action.

6.2 Configuration shape

{
  "basicMemory": {
    "primaryProject": "basic-memory-7020…/main",
    "secondaryProjects": ["my-team-2/main", "my-team-2/notes"],
    "teamProjects": {
      "my-team-2/notes": { "promoteFolder": "shared" }
    }
  }
}
  • secondaryProjects — workspace-qualified refs (or UUIDs) read for recall. Read-only.
  • teamProjects — share targets for /basic-memory:bm-share; each carries a promoteFolder (default shared). Also read for recall (SessionStart reads the union of secondaryProjects and teamProjects keys, capped at 6 per session).

autoWrite is deferred. An earlier draft proposed teamProjects.<name>.autoWrite to let auto-capture write to a team project. v0.4 does not implement it — auto-capture stays personal and sharing is always manual. Revisit only if a team explicitly wants shared session memory; until then we don't ship a flag we don't enforce.

6.3 What this unlocks

  • Team-engineering project becomes a living memory shared across the team. Recent decisions, ADRs, debugging notes from any teammate's session show up in everyone's SessionStart brief.
  • New team members get instant context — first SessionStart pulls the team graph and they're already oriented.
  • Cross-pollination — operator running a strategy session sees recent technical decisions from builders.

7. Bootstrap — /basic-memory:bm-setup interview

Users get overwhelmed starting from zero. The plugin opens with a guided interview that establishes opinionated defaults from a short conversation.

Verified (Q6) — there is no install hook. Claude Code has no PostInstall/PreInstall lifecycle event (feature request #11240 was closed as duplicate). We can't auto-run setup the moment the plugin installs. The workaround is the SessionStart hook detecting first-run: it checks for a sentinel (e.g. ${CLAUDE_PLUGIN_DATA}/.bootstrapped or the absence of a basicMemory config) and, if missing, injects a one-line nudge — "Basic Memory isn't configured yet. Run /basic-memory:bm-setup (≈3 min) to wire it up." A bonus verified capability: SessionStart can return {"reloadSkills": true} to re-scan skills mid-session, useful if setup writes new skills/config that should activate without a restart.

Trigger paths:

  1. SessionStart detects no basicMemory config and no basic-memory note → inject the one-line nudge suggesting /basic-memory:bm-setup (we cannot run it automatically)
  2. User runs /basic-memory:bm-setup explicitly (anytime, including for reconfiguration)

Interview script (Claude executes it; SKILL.md provides the structure):

  1. "Quick setup — about 3 minutes. What's this Claude Code project mostly about? (code / writing / research / planning / mixed)"
  2. "Do you have a Basic Memory project for this already, or should I create one?"
    • If existing: offer list_memory_projects() output to pick from
    • If new: ask name and folder (default: ~/basic-memory/<repo-name>/)
  3. "Are you using Basic Memory Cloud or local-only?"
    • If cloud + team workspace exists: "You're on the <team> workspace. Want me to also read from team projects for recall? (read-only by default; we can opt-in to writes later)"
  4. "How chatty should I be?"
    • Light (default): SessionStart brief on each session, PreCompact checkpoint, /basic-memory:bm-remember on demand
    • Standard: above + capture decisions inline via output-style
    • Heavy: above + every-session SessionNote even without compaction
  5. "Should I look at your existing notes and suggest some placement conventions?" (yes → runs schema_infer on the existing notes, summarizes the patterns it found, and stores them in the basicMemory settings block — see §4.3, since path-scoped rules don't load yet — from the user's real conventions rather than imposed ones)
  6. "I'll set up schemas for session checkpoints and decisions so I can find them precisely later — okay?" (yes → writes schemas/session.md, schemas/decision.md, schemas/task.md into the primary project, skipping any that already exist; validation mode warn)
  7. "Want me to enable the basic-memory output style now?" (yes → adds outputStyle: basic-memory to settings)

Output: writes .claude/settings.json (with prompt to commit or keep local) including any inferred conventions, writes the three schema notes, optionally creates the BM project, and drops the bootstrap sentinel so SessionStart stops nudging. Closes with: "Done. I'll start using this on the next message. Try /basic-memory:bm-status anytime to see what I'm tracking."

Why an interview, not a config form: the interview is adaptive — it skips questions when context is obvious (e.g., it sees you're on cloud and on a team; doesn't ask about local), suggests reasonable defaults the user can accept with a single word, and produces a meaningful starting point in under 3 minutes. A config form makes the user own every decision.

8. Configuration — opinionated defaults

settings.example.json ships the defaults; user copies to .claude/settings.json and tweaks. Bootstrap writes this automatically.

{
  "basicMemory": {
    "primaryProject": null,
    "secondaryProjects": [],
    "captureFolder": "sessions",
    "rememberFolder": "bm-remember",
    "recallTimeframe": "3d",
    "recallPrompt": "You have Basic Memory available. Search before answering recall questions. Capture material decisions inline. Cite permalinks when referencing prior work.",
    "preCompactCapture": "summarized",
    "placementConventions": null,
    "teamProjects": {}
  },
  "outputStyle": "basic-memory"
}

preCompactCapture defaults to "summarized" — verified (Q2) that PreCompact's 600s budget is ample for an LLM summarization pass; "extractive" remains as a fast fallback. placementConventions holds the bootstrap-inferred placement/format guidance (§4.3) since path-scoped rules don't load yet. Everything is overridable. Nothing is mandatory.

9. What's out of scope (for v0.4)

These come later or never:

  • Per-turn capture into BM. Auto-memory already does the per-turn working summary. Doubling it into BM creates noise without value.
  • Commit-hook integration (entire.io-style). Worthy idea, but defer to a builder-add-on after the spine is proven.
  • Routines. Defer to a separate doc + phase. Once the spine works, routines become "weekly digests, nightly hygiene, GitHub webhooks."
  • Statusline integration. Polish; deferred.
  • Subagent memory bundling. The platform now offers memory: project|user|local on subagents. Worth exploring but doesn't fit the spine.
  • Replacement of basic-memory-manager agent. Plugin ships no agent in v0.4. Users who want one install from a separate package.

10. What this replaces / deletes

From the current plugin (v0.3.x):

  • All six plugin-local skills (continue-conversation, edit-note, knowledge-capture, knowledge-organize, placement, research) → migrate workflows to top-level skills/, deprecate local copies
  • basic-memory-manager.md agent → deleted; mention in CHANGELOG that users wanting it should install from skills/ package
  • PreToolUse: write_note hook (the "echo additionalContext" hook) → deleted; placement guidance moves to the basicMemory settings block + output-style (§4.3 — path-scoped rules don't load yet)
  • PostToolUse: write_note confirmation echo → deleted (noise)
  • basic-memory config-note convention → deprecated in favor of .claude/settings.json (kept as fallback for one release)
  • 314-line PLUGIN.md feature list → replaced by a short README.md framed around the bridge story

11. Verified findings (resolved open questions)

Verified 2026-05-28 against Claude Code v2.1.153 and basic-memory 0.21.5, via a fan-out investigate → adversarial-verify workflow (each finding independently confirmed/refuted from a second angle). Verdicts: confirmed = both passes agree; refuted = verify pass overturned the first answer; uncertain = could not be confirmed, treat with caution.

# Question Verdict Answer Design consequence
Q1 Does SessionStart fire before/after auto-memory loads? Can the hook use auto-memory as input? uncertain Firing order vs MEMORY.md load is not documented. The often-cited "SessionStart fires before CLAUDE.md loads" phrasing isn't in current docs. What is certain: the hook can read MEMORY.md from disk anytime. Don't assume auto-memory is in context at SessionStart. If the brief wants it, read the file from disk (§4.1). Don't build anything that depends on context-load ordering.
Q2 Does PreCompact block synchronously? Timeout? Can it do multi-second/LLM work? confirmed Blocks synchronously; 600s default timeout (configurable). MCP/LLM calls fit easily. On timeout the hook is killed and compaction proceeds. (To block compaction you'd return exit 2 / decision:block before the timeout — we don't.) Upgraded the design. preCompactCapture default is now "summarized" (real LLM pass), not extractive (§4.2, §8). Write the note early, enrich after, in case of kill.
Q3 Do plugin skills/commands appear in the / menu, and as what? confirmed Auto-discovered, always namespaced as /<plugin-name>:<skill>, but the picker shows only the bare skill name (namespace in the tooltip). Use a bm- prefix (bm-setup …) so they're legible in the picker — see §4.4. Workaround for anthropics/claude-code#50486; revisit if that lands.
Q4 How does SessionStart inject context? Size limit? confirmed Plain stdout (added to context, no JSON needed) or JSON hookSpecificOutput.additionalContext. 10,000-char cap per string; overflow spills to a file. Shell-profile echoes corrupt output. Use plain stdout. Keep the brief well under 10k — cap each section's item count, prefer permalinks over previews. Guard against shell-profile noise (§4.1).
Q5 Do path-scoped rules/ with paths: load for BM files (incl. outside the git tree)? refuted Path-scoped rules don't load automatically at all — open bug #16853 ("never worked"). Even when fixed they're repo-relative (won't match ~/basic-memory/, #25562). Dropped rules/ from the plugin. Placement/format conventions move to the basicMemory settings block + SessionStart brief + output-style (§4.3). Revisit if the platform bug is fixed and out-of-tree globs are supported.
Q6 Is there a run-on-install hook for bootstrap? confirmed No PostInstall/PreInstall lifecycle event (#11240 closed as dup). Only SessionStart + explicit Setup. Bonus: SessionStart can return {"reloadSkills": true}. Bootstrap can't auto-run. SessionStart detects first-run via a sentinel file and nudges the user to run /basic-memory:bm-setup (§7).
Q7 Does search_notes support metadata_filters + after_date in 0.21.5? Min version? confirmed Both present and working in 0.21.5. Full operator set: $in, $gt/$gte/$lt/$lte, $between, array-contains, equality, dot-notation (schema.confidence). On search_notes since v0.18.1 (verify corrected the first pass's "v0.19.0"). Structured recall is safe as a baseline — no fallback path needed for the 0.21.5 target. Pin a minimum basic-memory >= 0.19.0 in prerequisites for margin. Use tags/status shorthands for common cases (§4.1).
Q8 Is schema_validate cheap enough for PreCompact? Are the schema tools in 0.21.5? confirmed All three (validate/infer/diff) present since v0.19.0. schema_validate(identifier=…) = cheap single-note. schema_validate(note_type=…) = batch, O(N) with per-note file I/O. PreCompact validates only the note it just wrote via the identifier path (§4.2). Batch validation + schema_diff go to the nightly hygiene routine (§13).

Net effect on the design: two findings made it better (Q2 → real LLM summaries at compaction; Q7 → structured recall is a safe baseline), two forced corrections (Q5 → no path-scoped rules; Q3 → namespaced commands), and one stays a known unknown to design around (Q1 → read auto-memory from disk, never assume context order).

12. Task list — v0.4 milestone

Phase 0: Verify open questions — ✅ DONE (2026-05-28)

  • SessionStart timing vs auto-memory (Q1 — uncertain; read MEMORY.md from disk)
  • PreCompact latency budget (Q2 — 600s, LLM summary feasible)
  • Slash-command discovery + namespacing (Q3 — /basic-memory:<skill>)
  • additionalContext size limit (Q4 — 10k chars, plain stdout)
  • Path-scoped rules behavior (Q5 — refuted; rules don't load, dropped)
  • Bootstrap-on-install (Q6 — no install hook; SessionStart sentinel)
  • metadata_filters/after_date in 0.21.5 (Q7 — confirmed, baseline-safe)
  • schema_validate cost (Q8 — single-note cheap; batch → routine)
  • Findings recorded in §11

Phase 1: The spine — ✅ DONE (2026-05-28)

Build decisions (user calls): minimal-first vertical slice (one fast query at SessionStart, extractive PreCompact — prove the loop, enrich later) and hard-delete the old surfaces (no coverage audit, clean break). Hooks implemented in bash + python3 (python is a guaranteed BM dependency; the multi-query Python single-session helper is the enrich step). preCompactCapture ships as "extractive" even though the eventual default is "summarized" — the LLM pass is the first enrich step.

  • Delete deprecated artifacts — six skills, basic-memory-manager agent, both write_note hooks, PLUGIN.md, config-note convention
  • Write schemas/{session,decision,task}.md (picoschema, validation: warn; task mirrors memory-tasks)
  • Validate the three schemas — confirmed they parse through BM's exact path (frontmatter.loadsparse_schema_noteparse_picoschema). Found + fixed an enum-syntax bug: enum descriptions must go inside the parens (status?(enum, desc): [a,b]), not after the [...] value — the latter is invalid YAML. (Same latent bug exists in the canonical memory-tasks schema → flagged as a separate task.)
  • Write hooks/session-start.sh — plain-stdout brief, single type: task / status: active query, recall prompt; works against the default project (pin via primaryProject); silent if BM absent. Tested end-to-end.
  • Write hooks/pre-compact.shextractive checkpoint conforming to the session schema; only writes when primaryProject is set; silent/no-op otherwise. Tested end-to-end (note written + queryable by --type session).
  • Write output-styles/basic-memory.md — reflexes; keep-coding-instructions: true so it composes with dev work.
  • Write rules/basic-memory.mdcut (Q5). Conventions go in basicMemory settings + the brief.
  • Write settings.example.json (ships preCompactCapture: "extractive" for parity with the implemented hook; placementConventions reserved)
  • Update .claude-plugin/plugin.json + both marketplace.json descriptions (bridge framing; name: basic-memory confirmed for namespacing). Version left to release tooling.
  • Rewrite scripts/validate_claude_plugin.py for the new layout (hooks + output-style + schemas; agent dropped; skills optional). Passes ci-check and claude plugin validate . --strict.
  • Update plugin CHANGELOG.md + the AGENTS.md package-check description
  • Add basic-memory >= 0.19.0 to prerequisites (Q7 margin)
  • update_versions.py — no change needed (no new versioned manifests; hooks / schemas / output-style / settings carry no version)

Carried into later phases (not part of the minimal cut): the first-run sentinel nudge moves to Phase 3 (it should point at /basic-memory:bm-setup, which doesn't exist yet); the multi-query parallel brief and the LLM-summarized PreCompact are the enrich steps.

Implementation finding for Phase 3 bootstrap (corrected in Phase 3): an earlier Phase 1 note claimed the CLI write-note couldn't seed a resolvable schema and that bootstrap must use the MCP write_note(note_type="schema", metadata=…) path. That was confounded by the enum-syntax YAML bug — at the time of that test the schema frontmatter didn't parse, so it couldn't index. Re-verified in Phase 3 with the fixed schemas: writing a schema file's content via write_note (CLI or MCP, embedded frontmatter) indexes it as type: schema and schema_validate resolves it (schema_entity: "Session", valid_count: 1). So schema seeding is just a content copy — bootstrap reads each schemas/*.md and writes it via write_note; no note_type/metadata gymnastics needed.

Phase 2: Deliberate gestures — ✅ DONE (2026-05-28)

Both skills implemented as prose skills (skills are prompts) rather than bash-injection scripts — avoids shell-quoting fragility on arbitrary user text and the ${CLAUDE_SKILL_DIR} path uncertainty, and works regardless of the MCP server's tool-name prefix.

  • Write skills/bm-remember/SKILL.md/basic-memory:bm-remember — model-invocable ("remember that…"); writes verbatim to rememberFolder with a first-line title and manual-capture tag via write_note.
  • Write skills/bm-status/SKILL.md/basic-memory:bm-statusdisable-model-invocation (user-only diagnostic); reports project, folders, output-style, recent checkpoints, active-task count.
  • Test slash-command discovery end-to-end — installed from a local marketplace and confirmed via claude plugin details: Skills (2): remember, status, namespaced as /basic-memory:<name> (validates Q3 live). Cleaned up afterward.
  • Validator now requires the shipped skill set (REQUIRED_SKILLS); passes claude plugin validate . --strict.

Phase 3: Bootstrap interview — ✅ DONE (2026-05-28)

Implemented as a prose skill (the interview is conversational; Claude runs it using its MCP tools). Verified the whole loop end-to-end against throwaway projects.

  • Write skills/bm-setup/SKILL.md/basic-memory:bm-setup — adaptive interview that maps the project, seeds schemas, optionally learns conventions, writes settings, and enables the output style. Model-invocable ("set up basic memory") + user-invocable.
  • Wire schema_infer/list_directory into the bootstrap — the skill inspects the folder layout + samples notes, summarizes the user's real conventions, and stores the summary string in basicMemory.placementConventions (infer, don't dictate).
  • Wire schema-seeding — the skill reads <plugin>/schemas/*.md (two dirs up from the skill) and writes each via write_note (content copy, skip-if-exists). Verified end-to-end: all three seed, index as type: schema, and resolve via schema_validate (entity=Session/Decision/Task). This corrects the earlier Phase 1 finding — schema seeding is a plain content copy; the previous "must use note_type/metadata" conclusion was confounded by the enum YAML bug.
  • First-run detection in SessionStart — nudges toward /basic-memory:bm-setup when no basicMemory config block exists (config presence is the sentinel; no separate file). The nudge survives a failed/empty task query. Verified across all three config states (no config → nudge; block without project → pin tip; project pinned → silent).
  • {"reloadSkills": true}not needed. Setup adds no new skill files mid-session (the skills already ship with the plugin); config changes take effect when the hooks next run. Documented as N/A rather than implemented.
  • Tests for inferred-conventions — the inference is model-driven (prose skill), so there's no deterministic code unit to unit-test. The testable contract is the skill's presence + frontmatter, enforced by validate_claude_plugin.py's REQUIRED_SKILLS (now setup, remember, status). Real validation is dogfooding.

Phase 4: Team workspace support — ✅ DONE (2026-05-28)

Grounded in a real two-workspace BM Cloud account (verified name-collision routing, OAuth cross-workspace reads). Pulled /basic-memory:bm-share forward from future-work since team usage needs a safe write path.

  • Extend SessionStart to read primary + shared projects in parallelThreadPoolExecutor over structured queries (primary: active tasks + open decisions; each shared project: open decisions). Routes by qualified name or UUID, per-call timeout, capped at 6 shared projects, graceful on any failure. Verified against the real my-team-2 workspace and with local fixtures.
  • Add /basic-memory:bm-share (skills/bm-share/) — the deliberate personal→team write: reads a note from primaryProject, confirms, and copies it to a configured teamProjects target's promoteFolder with shared_from attribution. Preserves the note's type so shared decisions stay findable in the team's structured recall.
  • Config: secondaryProjects (read sources) + teamProjects (share targets with promoteFolder). Documented in settings.example.json with the qualified-name requirement. autoWrite deliberately not shipped (see §6.2). REQUIRED_SKILLS now includes share.
  • status reports team read-sources + share targets; setup interview step 3 now configures them via list_workspaces + qualified names.
  • Document the share-vs-capture distinction (README "Teams" section + the read-only note in the SessionStart brief itself).

Phase 5: Docs + dogfood

Docs done 2026-05-28; dogfood is the remaining (human) step.

  • Rewrite README.md around the bridge story (done in Phase 1; expanded with Commands + Teams sections in Phases 2-4).
  • docs/why-combine-memory.md — user-facing value prop, the three personas, use cases, and the "use both" framing.
  • docs/getting-started.md — guided install → setup → see-it-work → team walkthrough.
  • docs/architecture.md — flow-by-flow with mermaid diagrams (the bridge, SessionStart, PreCompact, capture reflexes, team read/share, component map).
  • Linked all three from the README's Documentation section.
  • Migration guidenot needed. v0.4 is a clean break; upgrade = uninstall the old plugin, install the new one. Noted in the CHANGELOG's "Removed" section.
  • Internal dogfood — full team uses v0.4 for a week of normal work (incl. the team workspace path); file bugs against rough edges.
  • Refine the recall prompt and SessionStart brief format based on real sessions.
  • (Likely follow-ups surfaced by dogfood) the multi-query brief enrichment and the LLM-summarized PreCompact checkpoint.

Phase 6: Release

  • Bump version in lockstep with basic-memory release
  • Update CHANGELOG
  • just package-check-claude-code passes
  • Tag and ship

13. Future work (post-v0.4)

  • Routines integration — three routine templates (nightly hygiene, weekly digest, daily reflection). Separate design doc. The nightly hygiene routine is the natural home for schema_diff drift detection and the deferred LLM-summary pass over the day's extractive SessionNotes.
  • /basic-memory:bm-share — promote personal note → team project — shipped in Phase 4.
  • Team autoWrite — opt-in for auto-capture (PreCompact/remember) to write to a team project, for teams that want shared session memory. Deferred from Phase 4 (§6.2).
  • /basic-memory:bm-blame <sha> — code archaeology, builder add-on.
  • Commit-hook integration — PostToolUse on Bash(git commit *) writes CommitNote linking SHA to session's BM writes.
  • Subagent memory bundling — explore memory: project|user on dedicated BM subagents.
  • Statusline — small visible presence (active project, last write).
  • /basic-memory:bm-promote — review auto-memory MEMORY.md, graduate observations into BM with proper schema.